Merge "Fix Lint warnings in fragment" into androidx-master-dev
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 30cedff..aafa7bf 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -45,7 +45,6 @@
<option name="myAdditionalJavadocTags" value="hide" />
</inspection_tool>
<inspection_tool class="KDocUnresolvedReference" enabled="true" level="ERROR" enabled_by_default="true" />
- <inspection_tool class="LongLine" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MissingDeprecatedAnnotation" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="NullableProblems" enabled="true" level="ERROR" enabled_by_default="true">
<option name="REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL" value="true" />
@@ -69,7 +68,6 @@
</inspection_tool>
<inspection_tool class="RawTypeCanBeGeneric" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="RawUseOfParameterizedType" enabled="true" level="ERROR" enabled_by_default="true" />
- <inspection_tool class="Reformat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RemoveEmptyParenthesesFromAnnotationEntry" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<scope name="Compose" level="WEAK WARNING" enabled="false" />
</inspection_tool>
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index 36959fc..b6edf7e 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -18,7 +18,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
api(project(":lifecycle:lifecycle-runtime"))
api(project(":lifecycle:lifecycle-viewmodel"))
api("androidx.savedstate:savedstate:1.0.0-rc01")
diff --git a/activity/activity/lint-baseline.xml b/activity/activity/lint-baseline.xml
deleted file mode 100644
index 591c093..0000000
--- a/activity/activity/lint-baseline.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2019 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.
- -->
-
-<issues format="5" by="lint 3.5.0-beta04" client="gradle" variant="debug" version="3.5.0-beta04">
-
- <issue
- id="LambdaLast"
- message="Functional interface parameters (such as parameter 1, "owner", in androidx.activity.OnBackPressedDispatcher.addCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
- errorLine1=" @NonNull OnBackPressedCallback onBackPressedCallback) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/activity/OnBackPressedDispatcher.java"
- line="144"
- column="13"/>
- </issue>
-
-</issues>
diff --git a/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.java b/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.java
index 71e5dce..e842248 100644
--- a/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.java
+++ b/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.java
@@ -16,6 +16,8 @@
package androidx.activity;
+import android.annotation.SuppressLint;
+
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -139,6 +141,7 @@
*
* @see #onBackPressed()
*/
+ @SuppressLint("LambdaLast")
@MainThread
public void addCallback(@NonNull LifecycleOwner owner,
@NonNull OnBackPressedCallback onBackPressedCallback) {
diff --git a/ads/ads-identifier-provider/build.gradle b/ads/ads-identifier-provider/build.gradle
index 535bbde..78b4ade 100644
--- a/ads/ads-identifier-provider/build.gradle
+++ b/ads/ads-identifier-provider/build.gradle
@@ -27,7 +27,7 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
implementation(AUTO_VALUE_ANNOTATIONS)
annotationProcessor(AUTO_VALUE)
diff --git a/ads/ads-identifier/build.gradle b/ads/ads-identifier/build.gradle
index 3ab81a6..3eca9f2 100644
--- a/ads/ads-identifier/build.gradle
+++ b/ads/ads-identifier/build.gradle
@@ -27,7 +27,7 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
implementation(AUTO_VALUE_ANNOTATIONS)
annotationProcessor(AUTO_VALUE)
api(GUAVA_LISTENABLE_FUTURE)
diff --git a/animation/build.gradle b/animation/build.gradle
index 1d1800e..65781d0 100644
--- a/animation/build.gradle
+++ b/animation/build.gradle
@@ -26,7 +26,7 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT, libs.exclude_for_espresso)
diff --git a/animation/integration-tests/testapp/build.gradle b/animation/integration-tests/testapp/build.gradle
index a9992a4..82a6fe05 100644
--- a/animation/integration-tests/testapp/build.gradle
+++ b/animation/integration-tests/testapp/build.gradle
@@ -23,7 +23,7 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
implementation(project(":animation"))
implementation(project(":animation:testing"))
diff --git a/animation/testing/build.gradle b/animation/testing/build.gradle
index ad98a4c..f33191e 100644
--- a/animation/testing/build.gradle
+++ b/animation/testing/build.gradle
@@ -26,7 +26,7 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
implementation(project(":animation"))
implementation(ANDROIDX_TEST_EXT_JUNIT)
implementation(ANDROIDX_TEST_CORE)
diff --git a/annotation/annotation/README.md b/annotation/annotation/README.md
index b7f5912..e65290d 100644
--- a/annotation/annotation/README.md
+++ b/annotation/annotation/README.md
@@ -7,7 +7,7 @@
[Release notes](https://developer.android.com/jetpack/androidx/releases/annotation)
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/annotations/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/annotation/annotation/)
[Reference documentation](https://developer.android.com/reference/androidx/classes.html)
diff --git a/appcompat/build.gradle b/appcompat/build.gradle
index e18e0bd9..5ce2ab6 100644
--- a/appcompat/build.gradle
+++ b/appcompat/build.gradle
@@ -12,7 +12,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.0.0")
api("androidx.cursoradapter:cursoradapter:1.0.0")
api("androidx.fragment:fragment:1.1.0-rc01")
diff --git a/asynclayoutinflater/build.gradle b/asynclayoutinflater/build.gradle
index 3dcd2f0..2c12378 100644
--- a/asynclayoutinflater/build.gradle
+++ b/asynclayoutinflater/build.gradle
@@ -9,7 +9,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
}
androidx {
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt
index 050c32d..09a21f4 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt
@@ -23,14 +23,18 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
@LargeTest
@RunWith(Parameterized::class)
-class ParameterizedBenchmark(@Suppress("unused") private val input: Int) {
+class ParameterizedBenchmark(
+ @Suppress("unused") private val input: Int,
+ @Suppress("unused") private val stringInput: String
+) {
companion object {
@JvmStatic
- @Parameterized.Parameters
- fun data(): Collection<Array<Int>> = List(2) { arrayOf(it) }
+ @Parameters(name = "size={0},str:{1}")
+ fun data(): Collection<Array<Any>> = List(2) { arrayOf(it, "$it=:") }
}
@get:Rule
diff --git a/benchmark/common/build.gradle b/benchmark/common/build.gradle
index 488d327..a6d74c9 100644
--- a/benchmark/common/build.gradle
+++ b/benchmark/common/build.gradle
@@ -32,6 +32,7 @@
androidTestImplementation(ANDROIDX_TEST_RULES)
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+ androidTestImplementation(KOTLIN_TEST_COMMON)
}
androidx {
diff --git a/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
index d48f976..5783636 100644
--- a/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
+++ b/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
@@ -24,6 +24,7 @@
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import kotlin.test.assertTrue
@SmallTest
@RunWith(JUnit4::class)
@@ -90,6 +91,7 @@
"benchmarks": [
{
"name": "MethodA",
+ "params": {},
"className": "package.Class1",
"totalRunTimeNs": 900000000,
"metrics": {
@@ -110,6 +112,7 @@
},
{
"name": "MethodB",
+ "params": {},
"className": "package.Class2",
"totalRunTimeNs": 900000000,
"metrics": {
@@ -134,4 +137,61 @@
tempFile.readText()
)
}
+
+ @Test
+ fun validateJsonWithParams() {
+ val reportWithParams = BenchmarkState.Report(
+ testName = "MethodWithParams[number=2,primeNumber=true]",
+ className = "package.Class",
+ totalRunTimeNs = 900000000,
+ data = listOf(100, 101, 102),
+ repeatIterations = 100000,
+ thermalThrottleSleepSeconds = 90000000,
+ warmupIterations = 8000
+ )
+
+ val tempFile = tempFolder.newFile()
+ ResultWriter.writeReport(tempFile, listOf(reportWithParams))
+ val reportText = tempFile.readText()
+
+ assertTrue {
+ reportText.contains(
+ """
+ | "name": "MethodWithParams[number=2,primeNumber=true]",
+ | "params": {
+ | "number": "2",
+ | "primeNumber": "true"
+ | },
+ """.trimMargin()
+ )
+ }
+ }
+
+ @Test
+ fun validateJsonWithInvalidParams() {
+ val reportWithInvalidParams = BenchmarkState.Report(
+ testName = "MethodWithParams[number=2,=true,]",
+ className = "package.Class",
+ totalRunTimeNs = 900000000,
+ data = listOf(100, 101, 102),
+ repeatIterations = 100000,
+ thermalThrottleSleepSeconds = 90000000,
+ warmupIterations = 8000
+ )
+
+ val tempFile = tempFolder.newFile()
+ ResultWriter.writeReport(tempFile, listOf(reportWithInvalidParams))
+ val reportText = tempFile.readText()
+
+ assertTrue {
+ reportText.contains(
+ """
+ | "name": "MethodWithParams[number=2,=true,]",
+ | "params": {
+ | "number": "2"
+ | },
+ """.trimMargin()
+ )
+ }
+ }
}
diff --git a/benchmark/common/src/main/java/androidx/benchmark/IsolationActivity.kt b/benchmark/common/src/main/java/androidx/benchmark/IsolationActivity.kt
index 18fe7c7..bf34af0 100644
--- a/benchmark/common/src/main/java/androidx/benchmark/IsolationActivity.kt
+++ b/benchmark/common/src/main/java/androidx/benchmark/IsolationActivity.kt
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
+import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
import android.os.Build
@@ -26,6 +27,7 @@
import android.os.PowerManager
import android.os.Process
import android.util.Log
+import android.view.WindowManager
import android.widget.TextView
import androidx.annotation.AnyThread
import androidx.annotation.RestrictTo
@@ -60,21 +62,24 @@
if (firstInit) {
if (!CpuInfo.locked && isSustainedPerformanceModeSupported()) {
sustainedPerformanceModeInUse = true
- application.registerActivityLifecycleCallbacks(sustainedPerfCallbacks)
+ }
+ application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
- // trigger the one missed lifecycle event, from registering the callbacks late
- sustainedPerfCallbacks.onActivityCreated(this, savedInstanceState)
+ // trigger the one missed lifecycle event, from registering the callbacks late
+ activityLifecycleCallbacks.onActivityCreated(this, savedInstanceState)
- // Keep at least one core busy. Together with a single threaded benchmark, this makes
- // the process get multi-threaded setSustainedPerformanceMode.
+ if (sustainedPerformanceModeInUse) {
+ // Keep at least one core busy. Together with a single threaded benchmark, this
+ // makes the process get multi-threaded setSustainedPerformanceMode.
//
- // We want to keep to the relatively lower clocks of the multi-threaded benchmark mode
- // to avoid any benchmarks running at higher clocks than any others.
+ // We want to keep to the relatively lower clocks of the multi-threaded benchmark
+ // mode to avoid any benchmarks running at higher clocks than any others.
//
// Note, thread names have 15 char max in Systrace
thread(name = "BenchSpinThread") {
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST)
- while (true) {}
+ while (true) {
+ }
}
}
firstInit = false
@@ -159,11 +164,32 @@
false
}
- private val sustainedPerfCallbacks = object : Application.ActivityLifecycleCallbacks {
+ private val activityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
@SuppressLint("NewApi") // window API guarded by [isSustainedPerformanceModeSupported]
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
- activity.window.setSustainedPerformanceMode(true)
+ if (sustainedPerformanceModeInUse) {
+ activity.window.setSustainedPerformanceMode(true)
+ }
+
+ // Forcibly wake the device, and keep the screen on to prevent benchmark failures.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+ val keyguardManager =
+ activity.getSystemService(KEYGUARD_SERVICE) as KeyguardManager
+ keyguardManager.requestDismissKeyguard(activity, null)
+ activity.setShowWhenLocked(true)
+ activity.setTurnScreenOn(true)
+ activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ } else {
+ @Suppress("DEPRECATION")
+ activity.window.addFlags(
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ )
+ }
}
+
override fun onActivityDestroyed(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
override fun onActivityStarted(activity: Activity) {}
diff --git a/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt b/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
index 6b6ca2e..d71d75b 100644
--- a/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
+++ b/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
@@ -23,6 +23,7 @@
import androidx.annotation.VisibleForTesting
import androidx.test.platform.app.InstrumentationRegistry
import java.io.File
+import java.io.IOException
internal object ResultWriter {
@VisibleForTesting
@@ -50,7 +51,21 @@
file.run {
if (!exists()) {
parentFile?.mkdirs()
- createNewFile()
+ try {
+ createNewFile()
+ } catch (exception: IOException) {
+ throw IOException(
+ """
+ Failed to create file for benchmark report. Make sure the
+ instrumentation argument additionalOutputDir is set to a writable
+ directory on device. If using a version of Android Gradle Plugin that
+ doesn't support additionalOutputDir, ensure your app's manifest file
+ enables legacy storage behavior by adding the application attribute:
+ android:requestLegacyExternalStorage="true"
+ """.trimIndent(),
+ exception
+ )
+ }
}
val writer = JsonWriter(bufferedWriter())
@@ -91,16 +106,41 @@
private fun JsonWriter.reportObject(report: BenchmarkState.Report): JsonWriter {
beginObject()
.name("name").value(report.testName)
+ .name("params").paramsObject(report)
.name("className").value(report.className)
.name("totalRunTimeNs").value(report.totalRunTimeNs)
.name("metrics").metricsObject(report)
.name("warmupIterations").value(report.warmupIterations)
.name("repeatIterations").value(report.repeatIterations)
.name("thermalThrottleSleepSeconds").value(report.thermalThrottleSleepSeconds)
-
return endObject()
}
+ private fun JsonWriter.paramsObject(report: BenchmarkState.Report): JsonWriter {
+ beginObject()
+ getParams(report.testName).forEach { name(it.key).value(it.value) }
+ return endObject()
+ }
+
+ private fun getParams(testName: String): Map<String, String> {
+ val parameterStrStart = testName.indexOf('[')
+ val parameterStrEnd = testName.lastIndexOf(']')
+
+ val params = HashMap<String, String>()
+ if (parameterStrStart >= 0 && parameterStrEnd >= 0) {
+ val paramListString = testName.substring(parameterStrStart + 1, parameterStrEnd)
+ paramListString.split(",").forEach { paramString ->
+ val separatorIndex = paramString.indexOfFirst { it == ':' || it == '=' }
+ if (separatorIndex in 1 until paramString.length - 1) {
+ val key = paramString.substring(0, separatorIndex)
+ val value = paramString.substring(separatorIndex + 1)
+ params[key] = value
+ }
+ }
+ }
+ return params
+ }
+
private fun JsonWriter.metricsObject(report: BenchmarkState.Report): JsonWriter {
beginObject()
diff --git a/biometric/build.gradle b/biometric/build.gradle
index e1dbcfe..e423137 100644
--- a/biometric/build.gradle
+++ b/biometric/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
api("androidx.appcompat:appcompat:1.1.0-rc01")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc02")
api("androidx.fragment:fragment:1.1.0-rc01")
}
diff --git a/browser/build.gradle b/browser/build.gradle
index 5dd3789..06018de 100644
--- a/browser/build.gradle
+++ b/browser/build.gradle
@@ -17,7 +17,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
api("androidx.annotation:annotation:1.1.0")
implementation("androidx.collection:collection:1.1.0")
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index 1102279..810d38b 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -51,7 +51,7 @@
.addStubs("car/stubs/android.car.jar")
prebuilts(LibraryGroups.CARDVIEW, "1.0.0")
prebuilts(LibraryGroups.COLLECTION, "1.1.0")
- prebuilts(LibraryGroups.CONCURRENT, "1.0.0-beta01")
+ prebuilts(LibraryGroups.CONCURRENT, "1.0.0-rc01")
prebuilts(LibraryGroups.CONTENTPAGER, "1.0.0")
prebuilts(LibraryGroups.COORDINATORLAYOUT, "1.1.0-beta01")
prebuilts(LibraryGroups.CORE, "core", "1.2.0-alpha02")
@@ -79,11 +79,11 @@
ignore(LibraryGroups.LOADER.group, "loader-ktx")
prebuilts(LibraryGroups.LOADER, "1.1.0-rc01")
prebuilts(LibraryGroups.LOCALBROADCASTMANAGER, "1.1.0-alpha01")
- prebuilts(LibraryGroups.MEDIA, "media", "1.1.0")
+ prebuilts(LibraryGroups.MEDIA, "media", "1.1.0-rc01")
ignore(LibraryGroups.MEDIA2.group, "media2-exoplayer")
prebuilts(LibraryGroups.MEDIA2, "media2-widget", "1.0.0-beta01")
prebuilts(LibraryGroups.MEDIA2, "1.0.0-rc01")
- prebuilts(LibraryGroups.MEDIAROUTER, "1.1.0")
+ prebuilts(LibraryGroups.MEDIAROUTER, "1.1.0-rc01")
ignore(LibraryGroups.NAVIGATION.group, "navigation-testing")
ignore(LibraryGroups.NAVIGATION.group, "navigation-safe-args-generator")
ignore(LibraryGroups.NAVIGATION.group, "navigation-safe-args-gradle-plugin")
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index b13de44..2a15464 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -27,7 +27,7 @@
dependencies {
api(project(":camera:camera-core"))
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
implementation("androidx.annotation:annotation:1.0.0")
implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
implementation(GUAVA_LISTENABLE_FUTURE)
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
index b552085..67292a2 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
@@ -84,8 +84,9 @@
@Rule
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
Manifest.permission.CAMERA);
+
@Before
- public void setUp() {
+ public void setUp() {
assumeTrue(CameraUtil.deviceHasCamera());
mAnalysisResults = new HashSet<>();
mAnalysisResultsSemaphore = new Semaphore(/*permits=*/ 0);
@@ -168,11 +169,23 @@
}
@Test
- public void analyzerAnalyzesImages_whenCameraIsOpen()
+ public void analyzesImages_withAcquireLatest_whenCameraIsOpen()
+ throws InterruptedException, CameraInfoUnavailableException {
+ analyzerAnalyzesImagesWithMode(ImageReaderMode.ACQUIRE_LATEST_IMAGE);
+ }
+
+ @Test
+ public void analyzesImages_withAcquireNext_whenCameraIsOpen()
+ throws InterruptedException, CameraInfoUnavailableException {
+ analyzerAnalyzesImagesWithMode(ImageReaderMode.ACQUIRE_NEXT_IMAGE);
+ }
+
+ private void analyzerAnalyzesImagesWithMode(ImageReaderMode imageReaderMode)
throws InterruptedException, CameraInfoUnavailableException {
final int imageFormat = ImageFormat.YUV_420_888;
ImageAnalysisConfig config =
- new ImageAnalysisConfig.Builder().setCallbackHandler(mHandler).build();
+ new ImageAnalysisConfig.Builder().setImageReaderMode(
+ imageReaderMode).setCallbackHandler(mHandler).build();
ImageAnalysis useCase = new ImageAnalysis(config);
Map<String, Size> suggestedResolutionMap = new HashMap<>();
suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
@@ -180,6 +193,8 @@
CameraUtil.openCameraWithUseCase(mCameraId, mCamera, useCase);
useCase.setAnalyzer(mAnalyzer);
+ mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS);
+
int sensorRotation = CameraX.getCameraInfo(mCameraId).getSensorRotationDegrees();
// The frames should have properties which match the configuration.
for (ImageProperties properties : mAnalysisResults) {
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index b111e0d..d792fc0 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -29,7 +29,7 @@
api("androidx.lifecycle:lifecycle-common:2.0.0")
implementation("androidx.exifinterface:exifinterface:1.0.0")
implementation("androidx.annotation:annotation:1.0.0")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
implementation(ARCH_LIFECYCLE_LIVEDATA)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingCallback.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingCallback.java
index dfd5e51..1d226f4 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingCallback.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingCallback.java
@@ -138,7 +138,7 @@
}
synchronized void finishImage(ImageProxy imageProxy) {
- imageProxy.close();
mFinishedImageTimestamp.set(imageProxy.getTimestamp());
+ imageProxy.close();
}
}
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index 2be49b7..396ab3c 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -30,7 +30,7 @@
implementation("androidx.annotation:annotation:1.0.0")
implementation(GUAVA_LISTENABLE_FUTURE)
implementation(project(":camera:camera-core"))
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
implementation(JUNIT)
}
diff --git a/car/core/api/1.0.0-alpha8.txt b/car/core/api/1.0.0-alpha8.txt
index d3d8be7..bf6878c 100644
--- a/car/core/api/1.0.0-alpha8.txt
+++ b/car/core/api/1.0.0-alpha8.txt
@@ -295,7 +295,7 @@
method public androidx.car.widget.CarMenuItem.Builder setDisplayBehavior(androidx.car.widget.CarMenuItem.DisplayBehavior);
method public androidx.car.widget.CarMenuItem.Builder setEnabled(boolean);
method public androidx.car.widget.CarMenuItem.Builder setIcon(android.graphics.drawable.Drawable);
- method public androidx.car.widget.CarMenuItem.Builder setIcon(android.content.Context, @DrawableRes int);
+ method @Deprecated public androidx.car.widget.CarMenuItem.Builder setIcon(android.content.Context, @DrawableRes int);
method public androidx.car.widget.CarMenuItem.Builder setOnClickListener(androidx.car.widget.CarMenuItem.OnClickListener);
method public androidx.car.widget.CarMenuItem.Builder setStyle(@StyleRes int);
method public androidx.car.widget.CarMenuItem.Builder setTitle(CharSequence);
diff --git a/car/core/api/current.txt b/car/core/api/current.txt
index d3d8be7..bf6878c 100644
--- a/car/core/api/current.txt
+++ b/car/core/api/current.txt
@@ -295,7 +295,7 @@
method public androidx.car.widget.CarMenuItem.Builder setDisplayBehavior(androidx.car.widget.CarMenuItem.DisplayBehavior);
method public androidx.car.widget.CarMenuItem.Builder setEnabled(boolean);
method public androidx.car.widget.CarMenuItem.Builder setIcon(android.graphics.drawable.Drawable);
- method public androidx.car.widget.CarMenuItem.Builder setIcon(android.content.Context, @DrawableRes int);
+ method @Deprecated public androidx.car.widget.CarMenuItem.Builder setIcon(android.content.Context, @DrawableRes int);
method public androidx.car.widget.CarMenuItem.Builder setOnClickListener(androidx.car.widget.CarMenuItem.OnClickListener);
method public androidx.car.widget.CarMenuItem.Builder setStyle(@StyleRes int);
method public androidx.car.widget.CarMenuItem.Builder setTitle(CharSequence);
diff --git a/car/core/api/restricted_1.0.0-alpha8.txt b/car/core/api/restricted_1.0.0-alpha8.txt
index c177433..b4d0539 100644
--- a/car/core/api/restricted_1.0.0-alpha8.txt
+++ b/car/core/api/restricted_1.0.0-alpha8.txt
@@ -318,7 +318,7 @@
method public androidx.car.widget.CarMenuItem.Builder setDisplayBehavior(androidx.car.widget.CarMenuItem.DisplayBehavior);
method public androidx.car.widget.CarMenuItem.Builder setEnabled(boolean);
method public androidx.car.widget.CarMenuItem.Builder setIcon(android.graphics.drawable.Drawable);
- method public androidx.car.widget.CarMenuItem.Builder setIcon(android.content.Context, @DrawableRes int);
+ method @Deprecated public androidx.car.widget.CarMenuItem.Builder setIcon(android.content.Context, @DrawableRes int);
method public androidx.car.widget.CarMenuItem.Builder setOnClickListener(androidx.car.widget.CarMenuItem.OnClickListener);
method public androidx.car.widget.CarMenuItem.Builder setStyle(@StyleRes int);
method public androidx.car.widget.CarMenuItem.Builder setTitle(CharSequence);
diff --git a/car/core/api/restricted_current.txt b/car/core/api/restricted_current.txt
index c177433..b4d0539 100644
--- a/car/core/api/restricted_current.txt
+++ b/car/core/api/restricted_current.txt
@@ -318,7 +318,7 @@
method public androidx.car.widget.CarMenuItem.Builder setDisplayBehavior(androidx.car.widget.CarMenuItem.DisplayBehavior);
method public androidx.car.widget.CarMenuItem.Builder setEnabled(boolean);
method public androidx.car.widget.CarMenuItem.Builder setIcon(android.graphics.drawable.Drawable);
- method public androidx.car.widget.CarMenuItem.Builder setIcon(android.content.Context, @DrawableRes int);
+ method @Deprecated public androidx.car.widget.CarMenuItem.Builder setIcon(android.content.Context, @DrawableRes int);
method public androidx.car.widget.CarMenuItem.Builder setOnClickListener(androidx.car.widget.CarMenuItem.OnClickListener);
method public androidx.car.widget.CarMenuItem.Builder setStyle(@StyleRes int);
method public androidx.car.widget.CarMenuItem.Builder setTitle(CharSequence);
diff --git a/car/core/src/androidTest/java/androidx/car/widget/CarToolbarTest.java b/car/core/src/androidTest/java/androidx/car/widget/CarToolbarTest.java
index 2ce3e70..81f524c 100644
--- a/car/core/src/androidTest/java/androidx/car/widget/CarToolbarTest.java
+++ b/car/core/src/androidTest/java/androidx/car/widget/CarToolbarTest.java
@@ -336,7 +336,7 @@
.Builder()
.setTitle(actionItemText)
.setDisplayBehavior(CarMenuItem.DisplayBehavior.ALWAYS) // Action item
- .setIcon(mActivity, android.R.drawable.sym_def_app_icon)
+ .setIcon(mActivity.getDrawable(android.R.drawable.sym_def_app_icon))
.build();
mActivityRule.runOnUiThread(() ->
diff --git a/car/core/src/main/java/androidx/car/widget/CarMenuItem.java b/car/core/src/main/java/androidx/car/widget/CarMenuItem.java
index 42d20b8..400a2e0 100644
--- a/car/core/src/main/java/androidx/car/widget/CarMenuItem.java
+++ b/car/core/src/main/java/androidx/car/widget/CarMenuItem.java
@@ -265,7 +265,10 @@
* @param context Context to load the drawable resource with.
* @param iconResId Resource id of icon of the {@code CarMenuItem}.
* @return This {@code Builder} object to allow call chaining.
+ *
+ * @deprecated Use {@link #setIcon(Drawable)} instead.
*/
+ @Deprecated
@NonNull
public Builder setIcon(@NonNull Context context, @DrawableRes int iconResId) {
mIconDrawable = context.getDrawable(iconResId);
diff --git a/car/moderator/build.gradle b/car/moderator/build.gradle
index bd7370d..4d19dbd 100644
--- a/car/moderator/build.gradle
+++ b/car/moderator/build.gradle
@@ -26,7 +26,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxModelCodeGenTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxModelCodeGenTests.kt
index a019068..f5aca24 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxModelCodeGenTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxModelCodeGenTests.kt
@@ -27,6 +27,7 @@
import androidx.compose.composer
import androidx.compose.runWithCurrent
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
@@ -64,6 +65,7 @@
}
@Test
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
fun testCGModelView_PersonModel(): Unit = ensureSetup {
val tvNameId = 384
val tvAgeId = 385
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTypeResolutionTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTypeResolutionTests.kt
index 1974f9d..e4bb666 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTypeResolutionTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTypeResolutionTests.kt
@@ -160,10 +160,10 @@
@Composable fun test(
- @Children children: @Composable() () -> Unit,
+ children: @Composable() () -> Unit,
value: Int,
x: Int,
- @Children children2: @Composable() () -> Unit,
+ children2: @Composable() () -> Unit,
value2: Int
) {
<LinearLayout>
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ChildrenAnnotationTest.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ChildrenAnnotationTest.kt
new file mode 100644
index 0000000..dedcd26
--- /dev/null
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ChildrenAnnotationTest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 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.compose.plugins.kotlin.analysis
+
+import androidx.compose.plugins.kotlin.AbstractComposeDiagnosticsTest
+
+class ChildrenAnnotationTest : AbstractComposeDiagnosticsTest() {
+
+ fun testReportChildrenOnWrongParameter() {
+ doTest("""
+ import androidx.compose.*;
+
+ @Composable fun MyWidget(<!CHILDREN_MUST_BE_LAST!>@Children children: ()->Unit<!>, value: Int) {
+ System.out.println(""+children+value)
+ }
+ """)
+ }
+}
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ChildAnnotationChecker.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ChildAnnotationChecker.kt
new file mode 100644
index 0000000..c618a31
--- /dev/null
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ChildAnnotationChecker.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 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.compose.plugins.kotlin
+
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtElement
+import androidx.compose.plugins.kotlin.analysis.ComposeErrors
+import org.jetbrains.kotlin.container.StorageComponentContainer
+import org.jetbrains.kotlin.container.useInstance
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
+import org.jetbrains.kotlin.resolve.TargetPlatform
+import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
+import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
+import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
+
+open class ChildAnnotationChecker() : DeclarationChecker, StorageComponentContainerContributor {
+ override fun check(
+ declaration: KtDeclaration,
+ descriptor: DeclarationDescriptor,
+ context: DeclarationCheckerContext
+ ) {
+ if(descriptor is FunctionDescriptor) {
+ descriptor.valueParameters.forEachIndexed { index, param ->
+ if(param.hasChildrenAnnotation() && index != descriptor.valueParameters.lastIndex) {
+ context.trace.report(ComposeErrors.CHILDREN_MUST_BE_LAST.on(param.findPsi() as KtElement))
+ }
+ }
+ }
+ }
+
+ override fun registerModuleComponents(
+ container: StorageComponentContainer,
+ platform: TargetPlatform,
+ moduleDescriptor: ModuleDescriptor
+ ) {
+ if (platform != JvmPlatform) return
+ container.useInstance(this)
+ }
+}
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
index e046b68c..bab14b9 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
@@ -111,6 +111,10 @@
)
StorageComponentContainerContributor.registerExtension(
project,
+ ChildAnnotationChecker()
+ )
+ StorageComponentContainerContributor.registerExtension(
+ project,
UnionAnnotationCheckerProvider()
)
KtxParsingExtension.registerExtension(project,
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ResolvedKtxElementCall.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ResolvedKtxElementCall.kt
index d5e819f..ed88c3c 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ResolvedKtxElementCall.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ResolvedKtxElementCall.kt
@@ -238,13 +238,13 @@
collector: MutableMap<String, MutableList<AttributeMeta>>
) {
callDescriptor?.let {
- it.valueParameters.forEach { param ->
+ it.valueParameters.forEachIndexed { index, param ->
collector.multiPut(
AttributeMeta(
name = param.name.asString(),
type = param.type,
descriptor = param,
- isChildren = param.hasChildrenAnnotation()
+ isChildren = param.hasChildrenAnnotation() || it.valueParameters.size-1 == index
)
)
}
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeDefaultErrorMessages.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeDefaultErrorMessages.kt
index 68acda7..ced33ab 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeDefaultErrorMessages.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeDefaultErrorMessages.kt
@@ -157,5 +157,10 @@
"Ambiguous targets. {0}",
Renderers.AMBIGUOUS_CALLS
)
+ MAP.put(
+ ComposeErrors.CHILDREN_MUST_BE_LAST,
+ "Children annotation must only occur on last parameter. This annotation is deprecated (move children to " +
+ "be last parameter, make it @Composable, and remove the @Children annotation.)."
+ )
}
}
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java
index 467ab57..a9b2802 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java
@@ -94,6 +94,8 @@
MISSING_REQUIRED_CHILDREN = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<KtExpression, Collection<KotlinType>, Collection<KotlinType>>
ILLEGAL_ASSIGN_TO_UNIONTYPE = DiagnosticFactory2.create(ERROR);
+ DiagnosticFactory0<KtElement>
+ CHILDREN_MUST_BE_LAST = DiagnosticFactory0.create(ERROR);
@SuppressWarnings("UnusedDeclaration")
Object INITIALIZER = new Object() {
diff --git a/compose/compose-runtime/build.gradle b/compose/compose-runtime/build.gradle
index 6f7a33a..52f9ae6 100644
--- a/compose/compose-runtime/build.gradle
+++ b/compose/compose-runtime/build.gradle
@@ -15,6 +15,7 @@
*/
+import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import static androidx.build.dependencies.DependenciesKt.*
@@ -25,15 +26,30 @@
plugins {
id("AndroidXPlugin")
id("com.android.library")
- id("kotlin-android")
+ id("kotlin-multiplatform")
}
-dependencies {
- implementation "androidx.annotation:annotation:1.0.0"
- implementation(KOTLIN_COMPOSE_STDLIB)
- implementation(KOTLIN_COMPOSE_REFLECT)
- testImplementation(JUNIT)
- testImplementation(ROBOLECTRIC)
+kotlin {
+ android()
+
+ sourceSets {
+ commonMain.dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$KOTLIN_VERSION"
+ }
+ commonTest.dependencies {
+ // TODO https://youtrack.jetbrains.com/issue/KT-29343
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$KOTLIN_VERSION"
+ implementation(kotlin("test-annotations-common"))
+ }
+ androidMain.dependencies {
+ implementation "androidx.annotation:annotation:1.0.0"
+ implementation(KOTLIN_COMPOSE_STDLIB)
+ implementation(KOTLIN_COMPOSE_REFLECT)
+ }
+ androidTest.dependencies {
+ }
+
+ }
}
android {
@@ -51,6 +67,11 @@
testCoverageEnabled = false
}
}
+ sourceSets {
+ main {
+ manifest.srcFile 'src/androidMain/AndroidManifest.xml'
+ }
+ }
}
androidx {
diff --git a/compose/compose-runtime/compose-runtime-benchmark/build.gradle b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
index dcb32f6..22c1201 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/build.gradle
+++ b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
@@ -30,6 +30,10 @@
android {
defaultConfig {
minSdkVersion 21
+
+ // Work-around for setting the testBuildType to "release" for benchmark projects causes gradle import get
+ // confused.
+ testBuildType = "debug"
}
lintOptions {
disable("SyntheticAccessor")
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
index 4bba238..e48051b 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
@@ -18,8 +18,12 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="androidx.compose.benchmark">
- <!-- Important: disable debuggable for accurate performance results -->
+ <!--
+ ~ Important: disable debuggable for accurate performance results
+ ~ requestLegacyExternalStorage to enable legacy JSON reporting when targeting Q
+ -->
<application
+ android:requestLegacyExternalStorage="true"
android:debuggable="false"
tools:replace="android:debuggable">
<activity
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmarkBase.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmarkBase.kt
index 8d72e02..29fd820 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmarkBase.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmarkBase.kt
@@ -37,17 +37,17 @@
val activityRule = ActivityTestRule(ComposeActivity::class.java)
fun measureCompose(block: @Composable() () -> Unit) {
+ val activity = activityRule.activity
benchmarkRule.measureRepeated {
- val activity = activityRule.activity
-
activity.setContent {
block()
}
runWithTimingDisabled {
- activity.disposeComposition()
+ activity.setContent { }
}
}
+ activity.disposeComposition()
}
fun measureRecompose(block: RecomposeReceiver.() -> Unit) {
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
index 6525704..da720b8 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
@@ -73,7 +73,7 @@
}
@Composable
-fun Table(@Children children: @Composable() () -> Unit) {
+fun Table(children: @Composable() () -> Unit) {
Column { children() }
}
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
index 7712cee..c7667bc 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
@@ -36,7 +36,7 @@
}
@Composable
-fun Stack(vertical: Boolean, @Children children: @Composable() () -> Unit) {
+fun Stack(vertical: Boolean, children: @Composable() () -> Unit) {
if (vertical) {
Column { children() }
} else {
@@ -45,7 +45,7 @@
}
@Composable
-fun Container(@Children children: @Composable() () -> Unit) {
+fun Container(children: @Composable() () -> Unit) {
// non-layout node component. just adds depth to the composition hierarchy.
children()
}
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/realworld4/RealWorld4_Widgets.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/realworld4/RealWorld4_Widgets.kt
index 20e1b98..eda5f71 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/realworld4/RealWorld4_Widgets.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/realworld4/RealWorld4_Widgets.kt
@@ -28,7 +28,7 @@
import androidx.ui.foundation.ColoredRect
import androidx.ui.core.dp
import androidx.ui.core.toRect
-import androidx.ui.core.vectorgraphics.SolidColor
+import androidx.ui.graphics.vectorgraphics.SolidColor
import androidx.ui.painting.Paint
import androidx.ui.graphics.Color
import androidx.ui.layout.FlexColumn
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
index 6b1d4b7..f91235e 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
@@ -31,7 +31,7 @@
import kotlin.random.Random
@Composable
-fun Stack(@Children children: @Composable() () -> Unit) {
+fun Stack(children: @Composable() () -> Unit) {
Column {
children()
}
diff --git a/compose/compose-runtime/integration-tests/android-tests/build.gradle b/compose/compose-runtime/integration-tests/android-tests/build.gradle
new file mode 100644
index 0000000..e733507
--- /dev/null
+++ b/compose/compose-runtime/integration-tests/android-tests/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 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.
+ */
+
+
+import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+dependencies {
+ androidTestImplementation(project(":compose:compose-runtime"))
+ androidTestImplementation(project(":compose:compose-runtime:integration-tests"))
+
+ androidTestImplementation(KOTLIN_COMPOSE_STDLIB)
+ androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+ androidTestImplementation(ANDROIDX_TEST_RULES)
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 18
+ }
+}
diff --git a/compose/compose-runtime/integration-tests/android-tests/src/androidTest/AndroidManifest.xml b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..bb550d8
--- /dev/null
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 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.
+ -->
+<manifest
+ package="androidx.compose.integrationtests.androidtests" xmlns:android="http://schemas.android.com/apk/res/android">
+ <application>
+ <activity
+ android:name="androidx.compose.TestActivity"/>
+ <activity
+ android:name="androidx.compose.ComposeIntoTestActivity"/>
+ <activity
+ android:name="androidx.compose.DisposeTests$DisposeTestActivity"/>
+ </application>
+</manifest>
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/ComposeIntoTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposeIntoTests.kt
similarity index 68%
rename from compose/compose-runtime/src/test/java/androidx/compose/ComposeIntoTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposeIntoTests.kt
index 14d1805..00dc77a 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/ComposeIntoTests.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposeIntoTests.kt
@@ -17,28 +17,32 @@
package androidx.compose
import android.app.Activity
-import android.os.Bundle
-import junit.framework.TestCase
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import org.junit.Assert.assertEquals
+import org.junit.Ignore
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.robolectric.Robolectric
-import org.robolectric.annotation.Config
-@RunWith(ComposeRobolectricTestRunner::class)
-@Config(
- manifest = Config.NONE,
- minSdk = 23,
- maxSdk = 23
-)
-class ComposeIntoTests : TestCase() {
+@RunWith(AndroidJUnit4::class)
+class ComposeIntoTests {
+
+ @get:Rule
+ val activityRule = ActivityTestRule(ComposeIntoTestActivity::class.java)
@Test
+ @SmallTest
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
fun testMultipleSetContentCalls() {
- val controller = Robolectric.buildActivity(ComposeIntoTestActivity::class.java)
- val activity = controller.create().get() as ComposeIntoTestActivity
+ val activity = activityRule.activity
+ activity.run()
+
assertEquals(1, activity.initializationCount)
assertEquals(1, activity.commitCount)
activity.run()
+
// if we call setContent multiple times, we want to ensure that it doesn't tear
// down the whole hierarchy, so onActive should only get called once.
assertEquals(1, activity.initializationCount)
@@ -46,13 +50,10 @@
}
}
-private class ComposeIntoTestActivity : Activity() {
+class ComposeIntoTestActivity : Activity() {
var initializationCount = 0
var commitCount = 0
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- run()
- }
+
fun run() {
setViewContent {
+onActive { initializationCount++ }
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/ComposeModelTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposeModelTests.kt
similarity index 90%
rename from compose/compose-runtime/src/test/java/androidx/compose/ComposeModelTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposeModelTests.kt
index ea060c3..cfd4457 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/ComposeModelTests.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposeModelTests.kt
@@ -17,10 +17,7 @@
package androidx.compose
import android.app.Activity
-import android.os.Bundle
-import android.view.Choreographer
import android.view.ViewGroup
-import android.widget.LinearLayout
import android.widget.TextView
import androidx.compose.frames.AbstractRecord
import androidx.compose.frames.Framed
@@ -29,13 +26,16 @@
import androidx.compose.frames._readable
import androidx.compose.frames._writable
import androidx.compose.frames.currentFrame
-import junit.framework.TestCase
-import org.junit.Before
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertFalse
+import org.junit.Ignore
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.robolectric.Robolectric
-import org.robolectric.RuntimeEnvironment
-import org.robolectric.annotation.Config
val PRESIDENT_NAME_1 = "George Washington"
val PRESIDENT_AGE_1 = 57
@@ -120,21 +120,15 @@
}
}
-@RunWith(ComposeRobolectricTestRunner::class)
-@Config(
- manifest = Config.NONE,
- minSdk = 23,
- maxSdk = 23
-)
-class ModelViewTests : TestCase() {
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ModelViewTests {
- @Before
- fun beforeTest() {
- val scheduler = RuntimeEnvironment.getMasterScheduler()
- scheduler.pause()
- }
+ @get:Rule
+ val activityRule = ActivityTestRule(TestActivity::class.java)
@Test
+ @UiThreadTest
fun testModelView_Simple(): Unit = FrameManager.isolated {
val tvId = 67
compose {
@@ -150,6 +144,7 @@
}
@Test
+ @UiThreadTest
fun testModelView_Simple_Recompose(): Unit = FrameManager.isolated {
val tvId = 71
compose {
@@ -168,6 +163,8 @@
}
@Test
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
+ @UiThreadTest
fun testModelView_PersonModel(): Unit = FrameManager.isolated {
val tvIdName = 90
val tvIdAge = 91
@@ -204,6 +201,7 @@
}
@Test
+ @UiThreadTest
fun testModelView_RecomposeScopeCleanup(): Unit = FrameManager.isolated {
val washington = Person(
PRESIDENT_NAME_1,
@@ -249,6 +247,7 @@
// b/122548164
@Test
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
fun testObserverEntering(): Unit = FrameManager.isolated {
val president = Person(
PRESIDENT_NAME_1,
@@ -297,6 +296,7 @@
}
@Test
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
fun testModelUpdatesNextFrameVisibility(): Unit = FrameManager.isolated {
val president = Person(
PRESIDENT_NAME_1,
@@ -344,7 +344,7 @@
president.name = PRESIDENT_NAME_16
// check that changes aren't there yet
assertEquals(PRESIDENT_NAME_1, (activity.findViewById(tvName) as TextView).text)
- Choreographer.getInstance().postFrameCallback {
+ Choreographer.postFrameCallback {
// after one frame we should see changes
assertEquals(PRESIDENT_NAME_16, (activity.findViewById(tvName) as TextView).text)
}
@@ -358,15 +358,13 @@
}
fun compose(block: ViewComposition.() -> Unit) =
- CompositionModelTest(block)
+ CompositionModelTest(block, activityRule.activity)
- class CompositionModelTest(val composable: ViewComposition.() -> Unit) {
+ class CompositionModelTest(val composable: ViewComposition.() -> Unit, val activity: Activity) {
var savedContext: CompositionContext? = null
inner class ActiveTest(val activity: Activity) {
private var firstCompose = true
private fun compose() {
- val scheduler = RuntimeEnvironment.getMasterScheduler()
- scheduler.advanceToLastPostedRunnable()
if (firstCompose) {
val composer = composer.composer
composer.startRoot()
@@ -392,8 +390,6 @@
}
fun then(block: (activity: Activity) -> Unit): ActiveTest {
- val controller = Robolectric.buildActivity(FrameTestActivity::class.java)
- val activity = controller.create().get()
val cc = Compose.createCompositionContext(
activity,
activity.root,
@@ -412,13 +408,3 @@
}
private val Activity.root get() = findViewById(ComposerComposeTestCase.ROOT_ID) as ViewGroup
-
-private class FrameTestActivity : Activity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(LinearLayout(this).apply {
- id =
- ComposerComposeTestCase.ROOT_ID
- })
- }
-}
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/ComposerComposeTestCase.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposerComposeTestCase.kt
similarity index 63%
rename from compose/compose-runtime/src/test/java/androidx/compose/ComposerComposeTestCase.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposerComposeTestCase.kt
index 90dbe54..6339325 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/ComposerComposeTestCase.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposerComposeTestCase.kt
@@ -17,30 +17,19 @@
package androidx.compose
import android.app.Activity
-import android.os.Bundle
import android.view.ViewGroup
-import android.widget.LinearLayout
-import junit.framework.TestCase
-import org.junit.runner.RunWith
-import org.robolectric.Robolectric
-import org.robolectric.annotation.Config
-@RunWith(ComposeRobolectricTestRunner::class)
-@Config(
- manifest = Config.NONE,
- minSdk = 23,
- maxSdk = 23
-)
-abstract class ComposerComposeTestCase : TestCase() {
- fun compose(composable: (ViewComposition) -> Unit) =
+abstract class ComposerComposeTestCase {
+
+ fun compose(activity: Activity, composable: (ViewComposition) -> Unit) =
ComposeTest(
- Root(composable)
+ Root(composable),
+ activity
)
- class ComposeTest(val component: Component) {
+ class ComposeTest(val component: Component, val activity: Activity) {
+
fun then(fn: (CompositionContext, Component, ViewGroup, Activity) -> Unit) {
- val controller = Robolectric.buildActivity(TestActivity::class.java)
- val activity = controller.create().get()
val root = activity.findViewById(ROOT_ID) as ViewGroup
val cc = Compose.createCompositionContext(root.context, root, component, null)
cc.compose()
@@ -52,15 +41,6 @@
override fun compose() = composable(composer)
}
- private class TestActivity : Activity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(LinearLayout(this).apply { id =
- ROOT_ID
- })
- }
- }
-
companion object {
val ROOT_ID = 18284847
}
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/ComposerExtensions.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposerExtensions.kt
similarity index 100%
rename from compose/compose-runtime/src/test/java/androidx/compose/ComposerExtensions.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ComposerExtensions.kt
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/CompositionTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/CompositionTests.kt
similarity index 99%
rename from compose/compose-runtime/src/test/java/androidx/compose/CompositionTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/CompositionTests.kt
index 32fdd69..cd26098 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/CompositionTests.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/CompositionTests.kt
@@ -43,9 +43,11 @@
import androidx.compose.mock.text
import androidx.compose.mock.update
import androidx.compose.mock.validate
+import androidx.test.filters.SmallTest
import junit.framework.TestCase
import org.junit.Assert
+@SmallTest
class CompositionTests : TestCase() {
fun testComposeAModel() {
val model = testModel()
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/EffectsTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/EffectsTests.kt
similarity index 94%
rename from compose/compose-runtime/src/test/java/androidx/compose/EffectsTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/EffectsTests.kt
index f3655ef..a8c8971 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/EffectsTests.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/EffectsTests.kt
@@ -17,38 +17,30 @@
package androidx.compose
import android.app.Activity
-import android.os.Bundle
import android.view.ViewGroup
import android.widget.Button
-import android.widget.LinearLayout
import android.widget.TextView
-import junit.framework.TestCase
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertFalse
+import junit.framework.TestCase.assertTrue
+import org.junit.Ignore
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.robolectric.Robolectric
-import org.robolectric.RuntimeEnvironment
-import org.robolectric.annotation.Config
-private class EffectTestActivity : Activity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(LinearLayout(this).apply {
- id =
- ComposerComposeTestCase.ROOT_ID
- })
- }
-}
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class EffectsTests {
-@RunWith(ComposeRobolectricTestRunner::class)
-@Config(
- manifest = Config.NONE,
- minSdk = 23,
- maxSdk = 23
-)
-class EffectsTests : TestCase() {
+ @get:Rule
+ val activityRule = ActivityTestRule(TestActivity::class.java)
@Test
+ @UiThreadTest
fun testMemoization1() {
var inc = 0
@@ -62,6 +54,7 @@
}
@Test
+ @UiThreadTest
fun testMemoization2() {
var calculations = 0
var compositions = 0
@@ -97,6 +90,7 @@
}
@Test
+ @UiThreadTest
fun testState1() {
val tv1Id = 100
var inc = 0
@@ -127,6 +121,7 @@
}
@Test
+ @UiThreadTest
fun testState2() {
val tv1Id = 100
val tv2Id = 200
@@ -173,6 +168,7 @@
}
@Test
+ @UiThreadTest
fun testPreCommit1() {
var mount = true
@@ -233,6 +229,7 @@
}
@Test
+ @UiThreadTest
fun testPreCommit2() {
var mount = true
@@ -308,6 +305,7 @@
}
@Test
+ @UiThreadTest
fun testPreCommit3() {
var x = 0
@@ -338,6 +336,7 @@
}
@Test
+ @UiThreadTest
fun testPreCommit31() {
var a = 0
var b = 0
@@ -379,6 +378,7 @@
}
@Test
+ @UiThreadTest
fun testPreCommit4() {
var x = 0
var key = 123
@@ -421,6 +421,7 @@
}
@Test
+ @UiThreadTest
fun testPreCommit5() {
var a = 0
var b = 0
@@ -484,6 +485,7 @@
}
@Test
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
fun testOnCommit1() {
var mount = true
@@ -514,10 +516,6 @@
log("Unmountable:end")
}
- val scheduler = RuntimeEnvironment.getMasterScheduler()
-
- scheduler.pause()
-
compose {
with(composer) {
log("compose:start")
@@ -531,7 +529,6 @@
log("compose:end")
}
}.then { _ ->
- scheduler.unPause()
assertArrayEquals(
listOf(
"compose:start",
@@ -545,9 +542,7 @@
logHistory
)
mount = false
- scheduler.pause()
}.then { _ ->
- scheduler.unPause()
assertArrayEquals(
listOf(
"compose:start",
@@ -569,6 +564,7 @@
}
@Test
+ @UiThreadTest
fun testAmbient1() {
val tv1Id = 100
@@ -615,6 +611,7 @@
}
@Test
+ @UiThreadTest
fun testAmbient2() {
val MyAmbient = Ambient.of<Double>("Hello") { throw Exception("not set") }
@@ -676,6 +673,7 @@
}
@Test
+ @UiThreadTest
fun testUpdatedComposition() {
val tv1Id = 100
var inc = 0
@@ -696,7 +694,8 @@
}
}
- class CompositionTest(val composable: () -> Unit) {
+ class CompositionTest(val composable: () -> Unit, val activity: Activity) {
+
inner class ActiveTest(
val activity: Activity,
val cc: CompositionContext,
@@ -717,8 +716,6 @@
}
fun then(block: (activity: Activity) -> Unit): ActiveTest {
- val controller = Robolectric.buildActivity(EffectTestActivity::class.java)
- val activity = controller.create().get()
val root = activity.root
val component = Root(composable)
val cc = Compose.createCompositionContext(root.context, root, component, null)
@@ -726,7 +723,7 @@
}
}
- fun compose(composable: () -> Unit) = CompositionTest(composable)
+ fun compose(composable: () -> Unit) = CompositionTest(composable, activityRule.activity)
}
private val Activity.root get() = findViewById(ComposerComposeTestCase.ROOT_ID) as ViewGroup
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/ObserverMapTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ObserverMapTests.kt
similarity index 94%
rename from compose/compose-runtime/src/test/java/androidx/compose/ObserverMapTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ObserverMapTests.kt
index f33e5f4..7c6cf81 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/ObserverMapTests.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ObserverMapTests.kt
@@ -16,18 +16,12 @@
package androidx.compose
+import androidx.test.filters.SmallTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
-import org.junit.runner.RunWith
-import org.robolectric.annotation.Config
-@RunWith(ComposeRobolectricTestRunner::class)
-@Config(
- manifest = Config.NONE,
- minSdk = 23,
- maxSdk = 23
-)
+@SmallTest
class ObserverMapTests {
private val node1 = 1
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/RecomposerTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/RecomposerTests.kt
similarity index 91%
rename from compose/compose-runtime/src/test/java/androidx/compose/RecomposerTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/RecomposerTests.kt
index e03abca..b19ebed 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/RecomposerTests.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/RecomposerTests.kt
@@ -21,21 +21,33 @@
import android.widget.LinearLayout
import android.widget.TextView
import androidx.compose.frames.currentFrame
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
import junit.framework.TestCase
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertFalse
+import junit.framework.TestCase.assertNotSame
+import junit.framework.TestCase.assertTrue
+import org.junit.Ignore
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.robolectric.RuntimeEnvironment
-import org.robolectric.annotation.Config
-@RunWith(ComposeRobolectricTestRunner::class)
-@Config(
- manifest = Config.NONE,
- minSdk = 23,
- maxSdk = 23
-)
+@RunWith(AndroidJUnit4::class)
class ComposerCompositionContextTests : ComposerComposeTestCase() {
+ @get:Rule
+ val activityRule = ActivityTestRule(TestActivity::class.java)
+
+ private fun compose(composable: (ViewComposition) -> Unit) =
+ compose(activityRule.activity, composable)
+
@Test
+ @SmallTest
+ @UiThreadTest
fun testNativeViewWithAttributes() = compose {
with(it) {
// <TextView id={456} text="some text" />
@@ -54,6 +66,8 @@
}
@Test
+ @SmallTest
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
fun testSlotKeyChangeCausesRecreate() {
var i = 1
@@ -91,6 +105,8 @@
}
@Test
+ @SmallTest
+ @UiThreadTest
fun testViewWithViewChildren() {
compose {
// <LinearLayout id={345}>
@@ -129,6 +145,8 @@
}
@Test
+ @SmallTest
+ @UiThreadTest
fun testForLoop() {
val items = listOf(1, 2, 3, 4, 5, 6)
compose {
@@ -161,6 +179,9 @@
}
@Test
+ @SmallTest
+ @UiThreadTest
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
fun testRecompose() {
val counter = Counter()
@@ -179,10 +200,6 @@
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- // Robolectric will by default just run everything sync. pause() is needed to emulate
- // delays
- RuntimeEnvironment.getMasterScheduler().pause()
-
(activity.findViewById(100) as TextView).performClick()
(activity.findViewById(102) as TextView).performClick()
@@ -192,24 +209,17 @@
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- RuntimeEnvironment.getMasterScheduler().unPause()
-
// only the clicked view got rerendered
assertEquals(1, counter["A"])
assertEquals(2, counter["100"])
assertEquals(1, counter["101"])
assertEquals(2, counter["102"])
- RuntimeEnvironment.getMasterScheduler().pause()
-
// recompose() both the parent and the child... and show that the child only
// recomposes once as a result
(activity.findViewById(99) as LinearLayout).performClick()
(activity.findViewById(102) as TextView).performClick()
- RuntimeEnvironment.getMasterScheduler().unPause()
- RuntimeEnvironment.getMasterScheduler().advanceToLastPostedRunnable()
-
assertEquals(2, counter["A"])
assertEquals(2, counter["100"])
assertEquals(1, counter["101"])
@@ -218,6 +228,8 @@
}
@Test
+ @SmallTest
+ @UiThreadTest
fun testRecomposeSync() {
val counter = Counter()
@@ -236,9 +248,6 @@
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- // stop the time so only sync recomposes will take place
- RuntimeEnvironment.getMasterScheduler().pause()
-
(activity.findViewById(100) as TextView).performClick()
// only the clicked view got rerendered
@@ -247,15 +256,11 @@
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- // unpause to see if nothing is scheduled during recomposeSync()
- RuntimeEnvironment.getMasterScheduler().unPause()
-
assertEquals(1, counter["A"])
assertEquals(2, counter["100"])
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- RuntimeEnvironment.getMasterScheduler().pause()
// try to recompose the parent, but ensure that even if we tap textView several times,
// it's all got recomposed once
(activity.findViewById(99) as LinearLayout).performClick()
@@ -269,12 +274,13 @@
assertEquals(5, counter["100"])
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
-
- RuntimeEnvironment.getMasterScheduler().unPause()
}
}
@Test
+ @SmallTest
+ @UiThreadTest
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
fun testRootRecompose() {
val counter = Counter()
@@ -300,10 +306,6 @@
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- // Robolectric will by default just run everything sync. pause() is needed to emulate
- // delays
- RuntimeEnvironment.getMasterScheduler().pause()
-
(activity.findViewById(100) as TextView).performClick()
(activity.findViewById(102) as TextView).performClick()
@@ -313,8 +315,6 @@
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- RuntimeEnvironment.getMasterScheduler().unPause()
-
// as we recompose ROOT on every tap, only root(and LinearLayout) counter should we
// increased once, because two clicks layed to one frame
assertEquals(2, counter["A"])
@@ -322,14 +322,9 @@
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- RuntimeEnvironment.getMasterScheduler().pause()
-
(activity.findViewById(99) as LinearLayout).performClick()
(activity.findViewById(102) as TextView).performClick()
- RuntimeEnvironment.getMasterScheduler().unPause()
- RuntimeEnvironment.getMasterScheduler().advanceToLastPostedRunnable()
-
// again, no matter what we tappes, we want to recompose root, so LinearLayout's counter
// got increased
assertEquals(3, counter["A"])
@@ -340,6 +335,8 @@
}
@Test
+ @SmallTest
+ @UiThreadTest
fun testRootRecomposeSync() {
val counter = Counter()
@@ -364,9 +361,6 @@
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- // stop the time so only sync recomposes will take place
- RuntimeEnvironment.getMasterScheduler().pause()
-
(activity.findViewById(100) as TextView).performClick()
// important! as we recompose Root every time
@@ -383,8 +377,6 @@
assertEquals(1, counter["101"])
assertEquals(1, counter["102"])
- RuntimeEnvironment.getMasterScheduler().unPause()
- RuntimeEnvironment.getMasterScheduler().advanceToLastPostedRunnable()
// make sure nothing has been scheduled inside recomposeSync()
assertEquals(3, counter["A"])
assertEquals(1, counter["100"])
@@ -464,6 +456,8 @@
}
@Test
+ @SmallTest
+ @UiThreadTest
fun testCorrectViewTree() = compose {
// <LinearLayout>
// <LinearLayout />
@@ -491,6 +485,8 @@
}
@Test
+ @SmallTest
+ @UiThreadTest
fun testCorrectViewTreeWithComponents() {
class B : Component() {
@@ -540,6 +536,8 @@
}
@Test
+ @MediumTest
+ @UiThreadTest
fun testCorrectViewTreeWithComponentWithMultipleRoots() {
class B : Component() {
@@ -592,6 +590,8 @@
}
@Test
+ @SmallTest
+ @UiThreadTest
fun testFrameTransition() {
var frameId: Int? = null
compose {
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/ReconciliationInternalTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ReconciliationInternalTests.kt
similarity index 100%
rename from compose/compose-runtime/src/test/java/androidx/compose/ReconciliationInternalTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ReconciliationInternalTests.kt
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/SlotTableTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/SlotTableTests.kt
similarity index 99%
rename from compose/compose-runtime/src/test/java/androidx/compose/SlotTableTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/SlotTableTests.kt
index b6bd33c..8d4f8ac 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/SlotTableTests.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/SlotTableTests.kt
@@ -16,9 +16,11 @@
package androidx.compose
+import androidx.test.filters.SmallTest
import junit.framework.TestCase
import org.junit.Assert
+@SmallTest
class SlotTableTests : TestCase() {
fun testCanCreate() {
SlotTable()
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/ViewComposerTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ViewComposerTests.kt
similarity index 92%
rename from compose/compose-runtime/src/test/java/androidx/compose/ViewComposerTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ViewComposerTests.kt
index 9acdbcc..08a097b 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/ViewComposerTests.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/ViewComposerTests.kt
@@ -24,14 +24,17 @@
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
-import junit.framework.TestCase
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import junit.framework.TestCase.assertEquals
+import org.junit.Ignore
+import org.junit.Rule
import org.junit.runner.RunWith
import org.junit.Test
-import org.robolectric.Robolectric
-import org.robolectric.RuntimeEnvironment
-import org.robolectric.annotation.Config
-private class TestActivity : Activity() {
+class TestActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(LinearLayout(this).apply {
@@ -41,15 +44,15 @@
}
}
-@RunWith(ComposeRobolectricTestRunner::class)
-@Config(
- manifest = Config.NONE,
- minSdk = 23,
- maxSdk = 23
-)
-class NewCodeGenTests : TestCase() {
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NewCodeGenTests {
+
+ @get:Rule
+ val activityRule = ActivityTestRule(TestActivity::class.java)
@Test
+ @UiThreadTest
fun testStaticComposition() {
val tv1Id = 100
val tv2Id = 200
@@ -81,6 +84,7 @@
}
@Test
+ @UiThreadTest
fun testUpdatedComposition() {
val tv1Id = 100
val tv2Id = 200
@@ -119,56 +123,7 @@
}
@Test
- fun testDisposeComposition() {
- class DisposeTestActivity : Activity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val root = FrameLayout(this)
- val log = mutableListOf<String>()
- val composable = @Composable {
- +onPreCommit {
- log.add("onPreCommit")
- onDispose {
- log.add("onPreCommitDispose")
- }
- }
- +onActive {
- log.add("onActive")
- onDispose {
- log.add("onActiveDispose")
- }
- }
- }
-
- val scheduler = RuntimeEnvironment.getMasterScheduler()
-
- log.clear()
- Compose.composeInto(container = root, composable = composable)
- scheduler.advanceToLastPostedRunnable()
- assertEquals("onPreCommit, onActive", log.joinToString())
-
- log.clear()
- Compose.composeInto(container = root, composable = composable)
- scheduler.advanceToLastPostedRunnable()
- assertEquals("onPreCommitDispose, onPreCommit", log.joinToString())
-
- log.clear()
- Compose.disposeComposition(container = root)
- scheduler.advanceToLastPostedRunnable()
- assertEquals("onActiveDispose, onPreCommitDispose", log.joinToString())
-
- log.clear()
- Compose.composeInto(container = root, composable = composable)
- scheduler.advanceToLastPostedRunnable()
- assertEquals("onPreCommit, onActive", log.joinToString())
- }
- }
-
- val controller = Robolectric.buildActivity(DisposeTestActivity::class.java)
- controller.create()
- }
-
- @Test
+ @UiThreadTest
fun testSingleView() {
val tvId = 237
var text = "Hello world"
@@ -190,6 +145,7 @@
}
@Test
+ @UiThreadTest
fun testViewGroup() {
val tvId = 258
val llId = 260
@@ -223,6 +179,7 @@
}
@Test
+ @UiThreadTest
fun testComposableFunctionInvocationOneParameter() {
data class Phone(val area: String, val prefix: String, val number: String)
@@ -262,6 +219,7 @@
}
@Test
+ @UiThreadTest
fun testComposableFunctionInvocationTwoParameters() {
val tvId = 279
var left = 0
@@ -317,6 +275,7 @@
}
@Test
+ @UiThreadTest
fun testStatelessComposableClassInvocationProperties() {
val tvId = 338
var addCalled = 0
@@ -381,6 +340,7 @@
}
@Test
+ @UiThreadTest
fun testStatelessComposableClassInvocationParameters() {
val tvId = 338
var addCalled = 0
@@ -439,6 +399,7 @@
}
@Test
+ @UiThreadTest
fun testStatefulComposableClassInvocation() {
val tvId = 470
val tvPrivateValue = 471
@@ -527,6 +488,7 @@
}
@Test
+ @UiThreadTest
fun testStatefulComposableClassWithCtorParametersInvocation() {
val tvId = 604
val tvOffsetId = 605
@@ -598,6 +560,7 @@
}
@Test
+ @UiThreadTest
fun testStatefulComposableClassWithPivotalProperty() {
val tvId = 604
val tvOffsetId = 605
@@ -668,6 +631,7 @@
}
@Test
+ @UiThreadTest
fun testMoveComponents() {
val data = mutableListOf(1, 2, 3, 4, 5)
compose {
@@ -688,6 +652,7 @@
}
@Test
+ @UiThreadTest
fun testViewClassWithCtorParametersInvocation() {
val tvId = 749
@@ -715,6 +680,7 @@
}
@Test
+ @UiThreadTest
fun testViewClassWithMutableCtorParameter() {
val tvId = 749
@@ -743,6 +709,7 @@
}
@Test
+ @UiThreadTest
fun testEmittingAnEmittable() {
class MyEmittable : MockEmittable() {
@@ -772,6 +739,7 @@
}
@Test
+ @UiThreadTest
fun testCGEmittingAnEmittable() {
class MyEmittable : MockEmittable() {
@@ -814,6 +782,7 @@
}
@Test
+ @UiThreadTest
fun testCGEmittableAsRoot() {
class MyEmittable : MockEmittable() {
var message: String = ""
@@ -898,9 +867,9 @@
}
fun compose(block: ViewComposition.() -> Unit) =
- CompositionTest(block)
+ CompositionTest(activityRule.activity, block)
- class CompositionTest(val composable: ViewComposition.() -> Unit) {
+ class CompositionTest(val activity: Activity, val composable: ViewComposition.() -> Unit) {
inner class ActiveTest(val composition: ViewComposition, val activity: Activity) {
private fun compose() {
@@ -918,8 +887,6 @@
}
fun then(block: (activity: Activity) -> Unit): ActiveTest {
- val controller = Robolectric.buildActivity(TestActivity::class.java)
- val activity = controller.create().get()
val composition = ViewComposition(
ViewComposer(activity.root, activity, object : Recomposer() {
override fun scheduleChangesDispatch() {}
@@ -953,13 +920,16 @@
}
fun composeCG(block: TestContext.(activity: Activity) -> Unit) =
- CompositionCodeGenTest(block)
+ CompositionCodeGenTest(activityRule.activity, block)
private class Root : Component() {
override fun compose() {}
}
- class CompositionCodeGenTest(val composable: TestContext.(activity: Activity) -> Unit) {
+ class CompositionCodeGenTest(
+ val activity: Activity,
+ val composable: TestContext.(activity: Activity) -> Unit
+ ) {
inner class ActiveTest(
val activity: Activity,
val context: TestContext,
@@ -980,8 +950,6 @@
}
fun then(block: TestContext.(activity: Activity) -> Unit): ActiveTest {
- val controller = Robolectric.buildActivity(TestActivity::class.java)
- val activity = controller.create().get()
val root = activity.root
val component = Root()
val cc = Compose.createCompositionContext(root.context, root, component, null)
@@ -990,4 +958,56 @@
}
}
+@RunWith(AndroidJUnit4::class)
+class DisposeTests {
+
+ @get:Rule
+ val disposeActivityRule = ActivityTestRule(DisposeTestActivity::class.java)
+
+ class DisposeTestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val root = FrameLayout(this)
+ val log = mutableListOf<String>()
+ val composable = @Composable {
+ +onPreCommit {
+ log.add("onPreCommit")
+ onDispose {
+ log.add("onPreCommitDispose")
+ }
+ }
+ +onActive {
+ log.add("onActive")
+ onDispose {
+ log.add("onActiveDispose")
+ }
+ }
+ }
+
+ log.clear()
+ Compose.composeInto(container = root, composable = composable)
+ assertEquals("onPreCommit, onActive", log.joinToString())
+
+ log.clear()
+ Compose.composeInto(container = root, composable = composable)
+ assertEquals("onPreCommitDispose, onPreCommit", log.joinToString())
+
+ log.clear()
+ Compose.disposeComposition(container = root)
+ assertEquals("onActiveDispose, onPreCommitDispose", log.joinToString())
+
+ log.clear()
+ Compose.composeInto(container = root, composable = composable)
+ assertEquals("onPreCommit, onActive", log.joinToString())
+ }
+ }
+
+ @Test
+ @SmallTest
+ @Ignore("TODO(b/138720405): Investigate synchronisation issues in tests")
+ fun testDisposeComposition() {
+ disposeActivityRule.activity
+ }
+}
+
private val Activity.root get() = findViewById(ComposerComposeTestCase.ROOT_ID) as ViewGroup
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/frames/FramesTests.kt b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/frames/FramesTests.kt
similarity index 97%
rename from compose/compose-runtime/src/test/java/androidx/compose/frames/FramesTests.kt
rename to compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/frames/FramesTests.kt
index 8c4e3e3..7c2e9c4 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/frames/FramesTests.kt
+++ b/compose/compose-runtime/integration-tests/android-tests/src/androidTest/java/androidx/compose/frames/FramesTests.kt
@@ -1,5 +1,22 @@
+/*
+ * Copyright 2019 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.compose.frames
+import androidx.test.filters.SmallTest
import junit.framework.TestCase
import org.junit.Assert
import java.util.ArrayDeque
@@ -10,6 +27,7 @@
const val NEW_STREET = "456 New Street"
const val NEW_CITY = "AnyCity"
+@SmallTest
class FrameTest : TestCase() {
fun testCreatingAddress() {
@@ -134,7 +152,7 @@
}
speculate()
address.street = NEW_STREET
- val speculation = androidx.compose.frames.suspend()
+ val speculation = suspend()
frame {
Assert.assertEquals(OLD_STREET, address.street)
}
@@ -1075,7 +1093,7 @@
open(false)
try {
block()
- return androidx.compose.frames.suspend()
+ return suspend()
} catch (e: Exception) {
abortHandler()
throw e
diff --git a/compose/compose-runtime/integration-tests/android-tests/src/main/AndroidManifest.xml b/compose/compose-runtime/integration-tests/android-tests/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..50d6c6b3
--- /dev/null
+++ b/compose/compose-runtime/integration-tests/android-tests/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 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.
+ -->
+<manifest
+ package="androidx.compose.integrationtests.androidtests" xmlns:android="http://schemas.android.com/apk/res/android">
+ <application/>
+</manifest>
diff --git a/compose/compose-runtime/integration-tests/build.gradle b/compose/compose-runtime/integration-tests/build.gradle
new file mode 100644
index 0000000..122416d
--- /dev/null
+++ b/compose/compose-runtime/integration-tests/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 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.
+ */
+
+
+import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+dependencies {
+ implementation(project(":compose:compose-runtime"))
+ implementation(KOTLIN_COMPOSE_STDLIB)
+
+ implementation(JUNIT)
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 18
+ }
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/integration-tests/src/main/AndroidManifest.xml b/compose/compose-runtime/integration-tests/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..50d6c6b3
--- /dev/null
+++ b/compose/compose-runtime/integration-tests/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 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.
+ -->
+<manifest
+ package="androidx.compose.integrationtests.androidtests" xmlns:android="http://schemas.android.com/apk/res/android">
+ <application/>
+</manifest>
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/frames/Address.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/frames/Address.kt
similarity index 100%
rename from compose/compose-runtime/src/test/java/androidx/compose/frames/Address.kt
rename to compose/compose-runtime/integration-tests/src/main/java/androidx/compose/frames/Address.kt
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/ComposeContact.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ComposeContact.kt
similarity index 75%
rename from compose/compose-runtime/src/test/java/androidx/compose/mock/ComposeContact.kt
rename to compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ComposeContact.kt
index f53c431..7409e90 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/mock/ComposeContact.kt
+++ b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ComposeContact.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2019 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.compose.mock
// <linear>
diff --git a/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ComposePoints.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ComposePoints.kt
new file mode 100644
index 0000000..8a62094
--- /dev/null
+++ b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ComposePoints.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 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.compose.mock
+
+fun MockViewComposition.point(point: Point) {
+ text("X: ${point.x} Y: ${point.y}")
+}
+
+fun MockViewValidator.point(point: Point) {
+ text("X: ${point.x} Y: ${point.y}")
+}
+
+object SLPoints
+
+fun MockViewComposition.points(points: Iterable<Point>) {
+ repeat(of = points) {
+ memoize(SLPoints, it) { point(it) }
+ }
+}
+
+fun MockViewValidator.points(points: Iterable<Point>) {
+ repeat(of = points) {
+ point(it)
+ }
+}
diff --git a/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ComposeReport.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ComposeReport.kt
new file mode 100644
index 0000000..cf50387
--- /dev/null
+++ b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ComposeReport.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 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.compose.mock
+
+fun MockViewComposition.reportsTo(report: Report) {
+ text(report.from)
+ text("reports to")
+ text(report.to)
+}
+fun MockViewValidator.reportsTo(report: Report) {
+ text(report.from)
+ text("reports to")
+ text(report.to)
+}
+
+fun MockViewComposition.reportsReport(reports: Iterable<Report>) {
+ linear {
+ repeat(of = reports) { report ->
+ reportsTo(report)
+ }
+ }
+}
+
+fun MockViewValidator.reportsReport(reports: Iterable<Report>) {
+ linear {
+ repeat(of = reports) { report ->
+ reportsTo(report)
+ }
+ }
+}
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/Contact.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/Contact.kt
similarity index 100%
rename from compose/compose-runtime/src/test/java/androidx/compose/mock/Contact.kt
rename to compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/Contact.kt
diff --git a/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ContactModel.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ContactModel.kt
new file mode 100644
index 0000000..622cb0d
--- /dev/null
+++ b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ContactModel.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 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.compose.mock
+
+class ContactModel(
+ var filter: String = "",
+ val contacts: MutableList<Contact>,
+ var selected: Contact? = null
+) {
+ val filtered get() = contacts.filter { it.name.contains(filter) }
+
+ fun add(contact: Contact, after: Contact? = null) {
+ if (after == null) {
+ contacts.add(contact)
+ } else {
+ contacts.add(find(after) + 1, contact)
+ }
+ }
+
+ fun move(contact: Contact, after: Contact?) {
+ if (after == null) {
+ contacts.removeAt(find(contact))
+ contacts.add(0, contact)
+ } else {
+ contacts.removeAt(find(contact))
+ contacts.add(find(after) + 1, contact)
+ }
+ }
+
+ private fun find(contact: Contact): Int {
+ val index = contacts.indexOf(contact)
+ if (index < 0) error("Contact $contact not found")
+ return index
+ }
+}
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/MockViewValidator.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/MockViewValidator.kt
similarity index 77%
rename from compose/compose-runtime/src/test/java/androidx/compose/mock/MockViewValidator.kt
rename to compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/MockViewValidator.kt
index 5e12eb8..6d3377d 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/mock/MockViewValidator.kt
+++ b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/MockViewValidator.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2019 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.compose.mock
import org.junit.Assert
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/Point.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/Point.kt
similarity index 100%
rename from compose/compose-runtime/src/test/java/androidx/compose/mock/Point.kt
rename to compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/Point.kt
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/Report.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/Report.kt
similarity index 100%
rename from compose/compose-runtime/src/test/java/androidx/compose/mock/Report.kt
rename to compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/Report.kt
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/View.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/View.kt
similarity index 74%
rename from compose/compose-runtime/src/test/java/androidx/compose/mock/View.kt
rename to compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/View.kt
index ae356bc..fd36381 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/mock/View.kt
+++ b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/View.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2019 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.compose.mock
fun indent(indent: Int) {
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/ViewComposer.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ViewComposer.kt
similarity index 90%
rename from compose/compose-runtime/src/test/java/androidx/compose/mock/ViewComposer.kt
rename to compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ViewComposer.kt
index 6381542..e60ef68 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/mock/ViewComposer.kt
+++ b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/ViewComposer.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2019 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.compose.mock
import androidx.compose.Applier
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/Views.kt b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/Views.kt
similarity index 68%
rename from compose/compose-runtime/src/test/java/androidx/compose/mock/Views.kt
rename to compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/Views.kt
index 6e21d32..f86c36f 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/mock/Views.kt
+++ b/compose/compose-runtime/integration-tests/src/main/java/androidx/compose/mock/Views.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2019 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.compose.mock
class SourceLocation(val name: String) {
diff --git a/compose/compose-runtime/src/main/AndroidManifest.xml b/compose/compose-runtime/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from compose/compose-runtime/src/main/AndroidManifest.xml
rename to compose/compose-runtime/src/androidMain/AndroidManifest.xml
diff --git a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ActualAndroid.kt b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ActualAndroid.kt
new file mode 100644
index 0000000..60234d1
--- /dev/null
+++ b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ActualAndroid.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019 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.compose
+
+// TODO(b/137794549): Remove View System-related expect/actuals
+actual typealias ViewParent = android.view.ViewParent
+
+// TODO(b/137794549): Remove View System-related expect/actuals
+actual typealias View = android.view.View
+
+// TODO(b/137794549): Remove View System-related expect/actuals
+actual val View.parent: ViewParent
+ get() = parent
+
+// TODO(b/137794549): Remove View System-related expect/actuals
+actual val View.context: Context
+ get() = context
+
+// TODO(b/137794549): Remove View System-related expect/actuals
+actual typealias ViewGroup = android.view.ViewGroup
+
+// TODO(b/137794549): Remove View System-related expect/actuals
+actual typealias Context = android.content.Context
+
+// TODO(b/137794549): Remove View System-related expect/actuals
+actual typealias FrameLayout = android.widget.FrameLayout
+
+// TODO(b/137794558): Create portable abstraction for scheduling
+actual typealias Looper = android.os.Looper
+
+// TODO(b/137794558): Create portable abstraction for scheduling
+actual object LooperWrapper {
+ actual fun getMainLooper(): Looper = android.os.Looper.getMainLooper()
+}
+
+actual fun isMainThread(): Boolean {
+ return android.os.Looper.myLooper() == android.os.Looper.getMainLooper()
+}
+
+// TODO(b/137794558): Create portable abstraction for scheduling
+actual class Handler {
+ val handler: android.os.Handler
+
+ actual constructor(looper: Looper) {
+ handler = android.os.Handler(looper)
+ }
+ actual fun postAtFrontOfQueue(block: () -> Unit): Boolean {
+ return handler.postAtFrontOfQueue { block() }
+ }
+}
+
+// TODO(b/137794558): Create portable abstraction for scheduling
+actual object Choreographer {
+ actual fun postFrameCallback(callback: (Long) -> Unit) {
+ android.view.Choreographer.getInstance().postFrameCallback(callback)
+ }
+ actual fun postFrameCallbackDelayed(delayMillis: Long, callback: (Long) -> Unit) {
+ android.view.Choreographer.getInstance().postFrameCallbackDelayed(callback, delayMillis)
+ }
+ actual fun removeFrameCallback(callback: (Long) -> Unit) {
+ android.view.Choreographer.getInstance().removeFrameCallback(callback)
+ }
+}
+
+actual object Trace {
+ actual fun beginSection(name: String) = android.os.Trace.beginSection(name)
+ actual fun endSection() = android.os.Trace.endSection()
+}
diff --git a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ActualJvm.kt b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ActualJvm.kt
new file mode 100644
index 0000000..7762a4c
--- /dev/null
+++ b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ActualJvm.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 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.compose
+
+actual typealias BitSet = java.util.BitSet
+
+actual open class ThreadLocal<T> actual constructor() : java.lang.ThreadLocal<T>() {
+ actual override fun get(): T? {
+ return super.get()
+ }
+
+ actual override fun set(value: T?) {
+ super.set(value)
+ }
+
+ actual override fun initialValue(): T? {
+ return super.initialValue()
+ }
+}
+
+actual typealias WeakHashMap<K, V> = java.util.WeakHashMap<K, V>
+
+internal actual fun arraycopy(source: Any, sourcePos: Int, dest: Any, destPos: Int, len: Int) =
+ System.arraycopy(source, sourcePos, dest, destPos, len)
+
+actual fun identityHashCode(instance: Any?): Int = System.identityHashCode(instance)
+
+actual inline fun <R> synchronized(lock: Any, block: () -> R): R {
+ kotlin.synchronized(lock) {
+ return block()
+ }
+}
+
+actual typealias WeakReference<T> = java.lang.ref.WeakReference<T>
+
+actual typealias MainThread = androidx.annotation.MainThread
+
+actual typealias TestOnly = org.jetbrains.annotations.TestOnly
+
+actual typealias CheckResult = androidx.annotation.CheckResult
diff --git a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ComposeAndroid.kt b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ComposeAndroid.kt
new file mode 100644
index 0000000..3e0763a
--- /dev/null
+++ b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ComposeAndroid.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 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.compose
+
+import android.app.Activity
+
+/**
+ * Sets the contentView of an activity to a FrameLayout, and composes the contents of the layout
+ * with the passed in [composable]. This is a convenience method around [Compose.composeInto].
+ *
+ * @see Compose.composeInto
+ * @see Activity.setContentView
+ */
+fun Activity.setViewContent(composable: @Composable() () -> Unit): CompositionContext? {
+ // If there is already a FrameLayout in the root, we assume we want to compose
+ // into it instead of create a new one. This allows for `setContent` to be
+ // called multiple times.
+ val root = window
+ .decorView
+ .findViewById<ViewGroup>(android.R.id.content)
+ .getChildAt(0) as? ViewGroup
+ ?: FrameLayout(this).also { setContentView(it) }
+ return root.setViewContent(composable)
+}
+
+/**
+ * Disposes of a composition that was started using [setContent]. This is a convenience method
+ * around [Compose.disposeComposition].
+ *
+ * @see setContent
+ * @see Compose.disposeComposition
+ */
+fun Activity.disposeComposition() {
+ val view = window
+ .decorView
+ .findViewById<ViewGroup>(android.R.id.content)
+ .getChildAt(0) as? ViewGroup
+ ?: error("No root view found")
+ Compose.disposeComposition(view, null)
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/Recomposer.kt b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/Recomposer.kt
new file mode 100644
index 0000000..6f92528
--- /dev/null
+++ b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/Recomposer.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 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.compose
+
+import android.view.Choreographer
+
+private class AndroidRecomposer : Recomposer() {
+
+ private var frameScheduled = false
+
+ private val frameCallback = Choreographer.FrameCallback {
+ frameScheduled = false
+ dispatchRecomposes()
+ }
+
+ override fun scheduleChangesDispatch() {
+ if (!frameScheduled) {
+ frameScheduled = true
+ Choreographer.getInstance().postFrameCallback(frameCallback)
+ }
+ }
+
+ override fun hasPendingChanges(): Boolean = frameScheduled
+}
+
+internal actual fun createRecomposer(): Recomposer {
+ return AndroidRecomposer()
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ViewComposer.kt
similarity index 97%
rename from compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt
rename to compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ViewComposer.kt
index 90ba367..c93eaf0 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt
+++ b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ViewComposer.kt
@@ -398,7 +398,7 @@
}
}
-internal val currentComposerNonNull
+internal actual val currentComposerNonNull
get() = currentComposer ?: emptyComposition()
private fun emptyComposition(): Nothing =
@@ -406,10 +406,10 @@
val composer get() = ViewComposition(currentComposerNonNull as ViewComposer)
-internal var currentComposer: Composer<*>? = null
+internal actual var currentComposer: Composer<*>? = null
private set
-fun <T> Composer<*>.runWithCurrent(block: () -> T): T {
+actual fun <T> Composer<*>.runWithCurrent(block: () -> T): T {
val prev = currentComposer
try {
currentComposer = this
@@ -425,14 +425,10 @@
typealias ViewUpdater<T> = ComposerUpdater<Any, T>
-@PublishedApi
-internal val invocation = Object()
-
-@PublishedApi
-internal val provider = Object()
-
-@PublishedApi
-internal val consumer = Object()
-
-@PublishedApi
-internal val reference = Object()
+internal actual fun createComposer(
+ root: Any,
+ context: Context,
+ recomposer: Recomposer
+): Composer<*> {
+ return ViewComposer(root, context, recomposer)
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/adapters/ViewAdapter.kt b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/adapters/ViewAdapter.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/adapters/ViewAdapter.kt
rename to compose/compose-runtime/src/androidMain/kotlin/androidx/compose/adapters/ViewAdapter.kt
diff --git a/compose/compose-runtime/src/androidTest/AndroidManifest.xml b/compose/compose-runtime/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..356bbf4
--- /dev/null
+++ b/compose/compose-runtime/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 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.
+ -->
+<manifest package="androidx.ui.material" xmlns:android="http://schemas.android.com/apk/res/android">
+ <application/>
+
+</manifest>
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Ambient.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Ambient.kt
similarity index 97%
rename from compose/compose-runtime/src/main/java/androidx/compose/Ambient.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Ambient.kt
index 806b474f..4b74acb 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Ambient.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Ambient.kt
@@ -87,7 +87,7 @@
* will be thrown. This factory will not be executed more than once.
*/
inline fun <reified T> of(
- key: String = T::class.java.simpleName,
+ key: String = T::class.simpleName!!,
noinline defaultFactory: (() -> T)? = null
) = Ambient(key, defaultFactory)
}
@@ -113,8 +113,7 @@
@Composable
fun Provider(
value: T,
- @Children
- children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
with(currentComposerNonNull) {
val holder = +memo {
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Applier.kt
similarity index 99%
rename from compose/compose-runtime/src/main/java/androidx/compose/Applier.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Applier.kt
index f94b0a5..59787e0 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Applier.kt
@@ -73,4 +73,4 @@
fun reset() {
stack.clear()
}
-}
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Children.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Children.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/Children.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Children.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Component.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Component.kt
similarity index 95%
rename from compose/compose-runtime/src/main/java/androidx/compose/Component.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Component.kt
index 1bc0663..809eb78 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Component.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Component.kt
@@ -55,7 +55,8 @@
@Suppress("PLUGIN_ERROR")
abstract class Component {
@HiddenAttribute
- internal var recomposeCallback: ((sync: Boolean) -> Unit)? = null
+ // TODO(138720404): Investigate if Compose APIs can be internal despite MPP limitations
+ var recomposeCallback: ((sync: Boolean) -> Unit)? = null
private var composing = false
/**
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Composable.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composable.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/Composable.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composable.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Compose.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt
similarity index 84%
rename from compose/compose-runtime/src/main/java/androidx/compose/Compose.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt
index 71a5973..7565647 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Compose.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt
@@ -16,15 +16,6 @@
package androidx.compose
-import android.app.Activity
-import android.content.Context
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import androidx.annotation.MainThread
-import org.jetbrains.annotations.TestOnly
-import java.util.WeakHashMap
-
// TODO(lmr): consider moving this to the ViewComposer directly
/**
* A global namespace to hold some Compose utility methods, such as [Compose.composeInto] and
@@ -53,8 +44,8 @@
return view.getTag(TAG_ROOT_COMPONENT) as? Component
}
- // TODO(lmr): used by tests only. consider ways to remove.
- internal fun findRoot(view: View): Component? {
+ // TODO(b/138254844): Make findRoot/setRoot test-only & Android-only
+ fun findRoot(view: View): Component? {
var node: View? = view
while (node != null) {
val cc = node.getTag(TAG_ROOT_COMPONENT) as? Component
@@ -72,6 +63,7 @@
return EMITTABLE_ROOT_COMPONENT[emittable]
}
+ // TODO(b/138254844): Make findRoot/setRoot test-only & Android-only
private fun setRoot(emittable: Emittable, component: Component) {
EMITTABLE_ROOT_COMPONENT[emittable] = component
}
@@ -235,41 +227,6 @@
}
/**
- * Sets the contentView of an activity to a FrameLayout, and composes the contents of the layout
- * with the passed in [composable]. This is a convenience method around [Compose.composeInto].
- *
- * @see Compose.composeInto
- * @see Activity.setContentView
- */
-fun Activity.setViewContent(composable: @Composable() () -> Unit): CompositionContext? {
- // If there is already a FrameLayout in the root, we assume we want to compose
- // into it instead of create a new one. This allows for `setContent` to be
- // called multiple times.
- val root = window
- .decorView
- .findViewById<ViewGroup>(android.R.id.content)
- .getChildAt(0) as? ViewGroup
- ?: FrameLayout(this).also { setContentView(it) }
- return root.setViewContent(composable)
-}
-
-/**
- * Disposes of a composition that was started using [setContent]. This is a convenience method
- * around [Compose.disposeComposition].
- *
- * @see setContent
- * @see Compose.disposeComposition
- */
-fun Activity.disposeComposition() {
- val view = window
- .decorView
- .findViewById<ViewGroup>(android.R.id.content)
- .getChildAt(0) as? ViewGroup
- ?: error("No root view found")
- Compose.disposeComposition(view, null)
-}
-
-/**
* Composes the children of the view with the passed in [composable]. This is a convenience method
* around [Compose.composeInto].
*
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composer.kt
similarity index 98%
rename from compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composer.kt
index f92a9e5..d0f032a 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composer.kt
@@ -16,6 +16,8 @@
package androidx.compose
+import kotlin.collections.isNotEmpty
+
internal typealias Change<N> = (
applier: Applier<N>,
slots: SlotWriter,
@@ -54,7 +56,7 @@
var groupIndex: Int = 0
init {
- assert(startIndex >= 0) { "Invalid start index" }
+ require(startIndex >= 0) { "Invalid start index" }
}
var nodeCount = parentKeyInfo.nodes
@@ -352,7 +354,7 @@
}
override fun emitNode(node: N) {
- assert(inserting) { "emitNode() called when not inserting" }
+ require(inserting) { "emitNode() called when not inserting" }
val insertIndex = nodeIndexStack.peek()
// see emitNode
pending?.let { it.nodeCount++ }
@@ -366,7 +368,7 @@
}
override fun useNode(): N {
- assert(!inserting) { "useNode() called while inserting" }
+ require(!inserting) { "useNode() called while inserting" }
recordDown()
val result = slots.next()
childrenAllowed = true
@@ -595,7 +597,7 @@
invalidateStack.let { if (it.isNotEmpty()) it.peek() else null }
private fun start(key: Any, action: SlotAction) {
- assert(childrenAllowed) { "A call to createNode(), emitNode() or useNode() expected" }
+ require(childrenAllowed) { "A call to createNode(), emitNode() or useNode() expected" }
// Check for the insert fast path. If we are already inserting (creating nodes) then
// there is no need to track insert, deletes and moves with a pending changes object.
@@ -883,7 +885,7 @@
trace("Compose:recordEnters") {
while (true) {
skipToGroupContaining(location)
- assert(
+ require(
slots.isGroup && location >= slots.current &&
location < slots.current + slots.groupSize
) {
@@ -1134,14 +1136,14 @@
internal fun finalizeCompose() {
finalRealizeSlots()
- assert(pendingStack.isEmpty()) { "Start end imbalance" }
+ require(pendingStack.isEmpty()) { "Start end imbalance" }
pending = null
nodeIndex = 0
groupNodeCount = 0
}
private fun recordSlotNext(count: Int = 1) {
- assert(count >= 1) { "Invalid call to recordSlotNext()" }
+ require(count >= 1) { "Invalid call to recordSlotNext()" }
val actionsSize = slotActions.size
if (actionsSize > 0) {
// If the last action was also a skip just add this one to the last one
@@ -1194,7 +1196,7 @@
private fun recordRemoveNode(nodeIndex: Int, count: Int) {
if (count > 0) {
- assert(nodeIndex >= 0) { "Invalid remove index $nodeIndex" }
+ require(nodeIndex >= 0) { "Invalid remove index $nodeIndex" }
if (previousRemove == nodeIndex) previousCount += count
else {
realizeMovement()
@@ -1344,13 +1346,13 @@
fun add(action: SlotAction) {
if (size >= actions.size) {
- actions = actions.copyOf(Math.max(size, actions.size * 2))
+ actions = actions.copyOf(kotlin.math.max(size, actions.size * 2))
}
actions[size++] = action
}
fun remove(count: Int) {
- assert(count <= size) { "Removing too many actions" }
+ require(count <= size) { "Removing too many actions" }
size -= count
}
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Composition.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composition.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/Composition.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composition.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/CompositionContext.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/CompositionContext.kt
similarity index 95%
rename from compose/compose-runtime/src/main/java/androidx/compose/CompositionContext.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/CompositionContext.kt
index 09ce471..0455c6f 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/CompositionContext.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/CompositionContext.kt
@@ -16,8 +16,6 @@
package androidx.compose
-import android.content.Context
-
// TODO(lmr): this is really only needed for "composition management", but that could maybe move
// somewhere else. Consider ways to remove this class. Maybe should merge with FrameManager?
class CompositionContext private constructor(val component: Component, val composer: Composer<*>) {
@@ -30,7 +28,7 @@
) = prepare(
component,
compositionReference
- ) { ViewComposer(root, context, this) }
+ ) { createComposer(root, context, this) }
fun prepare(
component: Component,
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/CompositionLifecycleObserver.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/CompositionLifecycleObserver.kt
similarity index 95%
rename from compose/compose-runtime/src/main/java/androidx/compose/CompositionLifecycleObserver.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/CompositionLifecycleObserver.kt
index 934ace9..e5e6907 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/CompositionLifecycleObserver.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/CompositionLifecycleObserver.kt
@@ -43,5 +43,5 @@
override fun equals(other: Any?): Boolean =
other === instance || other is CompositionLifecycleObserverHolder &&
instance === other.instance
- override fun hashCode(): Int = System.identityHashCode(instance)
-}
+ override fun hashCode(): Int = identityHashCode(instance)
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/CompositionReference.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/CompositionReference.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/CompositionReference.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/CompositionReference.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Effects.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Effects.kt
similarity index 95%
rename from compose/compose-runtime/src/main/java/androidx/compose/Effects.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Effects.kt
index c3826a2..c8560cb 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Effects.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Effects.kt
@@ -18,8 +18,6 @@
package androidx.compose
-import android.view.Choreographer
-import androidx.annotation.CheckResult
import androidx.compose.annotations.Hide
import androidx.compose.frames.AbstractRecord
import androidx.compose.frames.Framed
@@ -33,7 +31,8 @@
* This is just a sentinel object that represents the absence of an explicit key being defined. This is necessary because
* we want `null` to be a valid key, and not the absence of one.
*/
-private val absentKey = object {}
+// TODO delete the explicit type after https://youtrack.jetbrains.com/issue/KT-20996
+private val absentKey: Any = object {}
/**
* This creates a composite key of any value to be used as the key for the group of an effect
@@ -167,7 +166,7 @@
private var disposeCallback = emptyDispose
override fun onDispose(callback: () -> Unit) {
- assert(disposeCallback === emptyDispose) {
+ require(disposeCallback === emptyDispose) {
"onDispose(...) should only be called once"
}
disposeCallback = callback
@@ -185,26 +184,25 @@
@PublishedApi
internal class PostCommitScopeImpl(
internal val onCommit: CommitScope.() -> Unit
-) : CommitScope, CompositionLifecycleObserver, Choreographer.FrameCallback {
+) : CommitScope, CompositionLifecycleObserver {
private var disposeCallback = emptyDispose
private var hasRun = false
override fun onDispose(callback: () -> Unit) {
- assert(disposeCallback === emptyDispose) {
+ require(disposeCallback === emptyDispose) {
"onDispose(...) should only be called once"
}
disposeCallback = callback
}
- override fun doFrame(frameTimeNanos: Long) {
+ private val doFrame: (Long) -> Unit = {
hasRun = true
onCommit(this)
}
override fun onEnter() {
- // TODO(lmr): we should eventually move this to an expect/actual "scheduler" of some sort
- Choreographer.getInstance().postFrameCallback(this)
+ Choreographer.postFrameCallback(doFrame)
}
override fun onLeave() {
@@ -213,7 +211,7 @@
if (hasRun) {
disposeCallback()
} else {
- Choreographer.getInstance().removeFrameCallback(this)
+ Choreographer.removeFrameCallback(doFrame)
}
}
}
@@ -233,7 +231,7 @@
* @param v1 The value to use as the key. This will be compared to its previous value using `Object.equals`
* @param block The block to execute other effects in
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun <T, V1> key(v1: V1, block: Effect<T>.() -> T) =
Effect(block, v1)
@@ -255,7 +253,7 @@
* @param v2 The second value to use as a key. This will be compared to its previous value using `Object.equals`
* @param block The block to execute other effects in
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun <T, V1, V2> key(v1: V1, v2: V2, block: Effect<T>.() -> T) =
Effect(block, joinKey(v1, v2))
@@ -274,7 +272,7 @@
* @param inputs The set of values to be used to create a compound key. This will be compared to its previous value using `Object.equals`
* @param block The block to execute other effects in
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun <T> key(vararg inputs: Any?, block: Effect<T>.() -> T) =
Effect(
block,
@@ -286,7 +284,7 @@
* @param calculation A function to produce the result
* @return The result of the calculation, or the cached value from the composition
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T> memo(/* crossinline */ calculation: () -> T) = effectOf<T> {
context.remember(calculation)
}
@@ -298,7 +296,7 @@
* @param calculation A function to produce the result
* @return The result of the calculation, or the cached value from the composition
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T, /* reified */ V1> memo(
v1: V1,
/* crossinline */
@@ -315,7 +313,7 @@
* @param calculation A function to produce the result
* @return The result of the calculation, or the cached value from the composition
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T, /* reified */ V1, /* reified */ V2> memo(
v1: V1,
v2: V2,
@@ -332,7 +330,7 @@
* @param calculation A function to produce the result
* @return The result of the calculation, or the cached value from the composition
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun <T> memo(vararg inputs: Any?, calculation: () -> T) = effectOf<T> {
context.remember(*inputs) { calculation() }
}
@@ -351,7 +349,7 @@
* @see [onPreCommit]
* @see [onDispose]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun onActive(callback: CommitScope.() -> Unit) = effectOf<Unit> {
context.remember { PostCommitScopeImpl(callback) }
}
@@ -367,7 +365,7 @@
* @see [onPreCommit]
* @see [onActive]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun onDispose(callback: () -> Unit) = onActive { onDispose(callback) }
/**
@@ -382,7 +380,7 @@
* @see [onPreCommit]
* @see [onActive]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun onCommit(callback: CommitScope.() -> Unit) = effectOf<Unit> {
context.changed(PostCommitScopeImpl(callback))
}
@@ -400,7 +398,7 @@
* @see [onPreCommit]
* @see [onActive]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun </* reified */ V1> onCommit(
v1: V1,
/* noinline */
@@ -423,7 +421,7 @@
* @see [onPreCommit]
* @see [onActive]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun </* reified */ V1, /* reified */ V2> onCommit(
v1: V1,
v2: V2,
@@ -446,7 +444,7 @@
* @see [onPreCommit]
* @see [onActive]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun onCommit(vararg inputs: Any?, callback: CommitScope.() -> Unit) =
effectOf<Unit> {
context.remember(*inputs) { PostCommitScopeImpl(callback) }
@@ -466,7 +464,7 @@
* @see [onPreCommit]
* @see [onActive]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun onPreCommit(callback: CommitScope.() -> Unit) =
effectOf<Unit> {
context.changed(PreCommitScopeImpl(callback))
@@ -487,7 +485,7 @@
* @see [onCommit]
* @see [onActive]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun </* reified */ V1> onPreCommit(
v1: V1,
/* noinline */
@@ -512,7 +510,7 @@
* @see [onCommit]
* @see [onActive]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun </* reified */ V1, /* reified */ V2> onPreCommit(
v1: V1,
v2: V2,
@@ -537,7 +535,7 @@
* @see [onCommit]
* @see [onActive]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun onPreCommit(vararg inputs: Any?, callback: CommitScope.() -> Unit) =
effectOf<Unit> {
context.remember(*inputs) { PreCommitScopeImpl(callback) }
@@ -604,7 +602,7 @@
* @see [model]
* @see [modelFor]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T> state(/* crossinline */ init: () -> T) =
memo { State(init()) }
@@ -625,7 +623,7 @@
* @see [model]
* @see [modelFor]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T, /* reified */ V1> stateFor(v1: V1, /* crossinline */ init: () -> T) =
memo(v1) { State(init()) }
@@ -647,7 +645,7 @@
* @see [model]
* @see [modelFor]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T, /* reified */ V1, /* reified */ V2> stateFor(
v1: V1,
v2: V2,
@@ -672,7 +670,7 @@
* @see [model]
* @see [modelFor]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T> stateFor(vararg inputs: Any?, /* crossinline */ init: () -> T) =
memo(*inputs) { State(init()) }
@@ -686,8 +684,9 @@
* @see [state]
* @see [stateFor]
*/
+// TODO(138720404): Investigate if Compose APIs can be internal despite MPP limitations
@Model
-class State<T> @PublishedApi internal constructor(value: T) : Framed {
+class State<T> /*@PublishedApi internal*/ constructor(value: T) : Framed {
/* NOTE(lmr): When this module is compiled with IR, we will need to remove the below Framed implementation */
@Suppress("UNCHECKED_CAST")
@@ -784,7 +783,7 @@
* @see [state]
* @see [stateFor]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T> model(/* crossinline */ init: () -> T) = memo { init() }
/**
@@ -802,7 +801,7 @@
* @see [state]
* @see [stateFor]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T, /* reified */ V1> modelFor(v1: V1, /* crossinline */ init: () -> T) =
memo(v1) { init() }
@@ -822,7 +821,7 @@
* @see [state]
* @see [stateFor]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <
T,
/* reified */ V1,
@@ -845,7 +844,7 @@
* @see [state]
* @see [stateFor]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
/* inline */ fun <T> modelFor(vararg inputs: Any?, /* crossinline */ init: () -> T) =
memo(*inputs) { init() }
@@ -857,7 +856,7 @@
*
* @see [Ambient]
*/
-@CheckResult(suggest = "+")
+@CheckResult("+")
fun <T> ambient(key: Ambient<T>) = effectOf<T> {
context.consume(key)
}
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/EffectsDsl.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/EffectsDsl.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/EffectsDsl.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/EffectsDsl.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Emittable.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Emittable.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/Emittable.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Emittable.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Expect.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Expect.kt
new file mode 100644
index 0000000..59307db
--- /dev/null
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Expect.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019 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.compose
+
+expect class BitSet() {
+ fun set(bitIndex: Int)
+ fun or(set: BitSet)
+ fun clear(bitIndex: Int)
+ operator fun get(bitIndex: Int): Boolean
+}
+
+expect open class ThreadLocal<T>() {
+ fun get(): T?
+ fun set(value: T?)
+ protected open fun initialValue(): T?
+}
+
+expect class WeakHashMap<K, V>() : MutableMap<K, V>
+
+internal expect fun arraycopy(source: Any, sourcePos: Int, dest: Any, destPos: Int, len: Int)
+
+expect fun identityHashCode(instance: Any?): Int
+
+expect interface ViewParent
+
+expect open class View {
+ fun getTag(key: Int): Any
+ fun setTag(key: Int, tag: Any?)
+}
+expect val View.parent: ViewParent
+expect val View.context: Context
+
+expect abstract class ViewGroup : View {
+ fun removeAllViews()
+}
+
+expect abstract class Context
+
+expect class FrameLayout(context: Context)
+
+expect inline fun <R> synchronized(lock: Any, block: () -> R): R
+
+expect class WeakReference<T>(instance: T) {
+ fun get(): T?
+}
+
+expect class Looper
+
+expect fun isMainThread(): Boolean
+
+expect object LooperWrapper {
+ fun getMainLooper(): Looper
+}
+
+expect class Handler(looper: Looper) {
+ fun postAtFrontOfQueue(block: () -> Unit): Boolean
+}
+
+expect object Choreographer {
+ fun postFrameCallback(callback: (Long) -> Unit)
+ fun postFrameCallbackDelayed(delayMillis: Long, callback: (Long) -> Unit)
+ fun removeFrameCallback(callback: (Long) -> Unit)
+}
+
+@MustBeDocumented
+@Retention(AnnotationRetention.BINARY)
+@Target(
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.CONSTRUCTOR
+)
+expect annotation class MainThread()
+
+@MustBeDocumented
+@Retention(AnnotationRetention.SOURCE)
+@Target(
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.CONSTRUCTOR
+)
+expect annotation class TestOnly()
+
+@Target(AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER)
+@Retention(AnnotationRetention.BINARY)
+@MustBeDocumented
+expect annotation class CheckResult(
+ val suggest: String
+)
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/FrameManager.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/FrameManager.kt
similarity index 97%
rename from compose/compose-runtime/src/main/java/androidx/compose/FrameManager.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/FrameManager.kt
index 0937e0f..f5abf9a 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/FrameManager.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/FrameManager.kt
@@ -16,8 +16,6 @@
package androidx.compose
-import android.os.Handler
-import android.os.Looper
import androidx.compose.frames.open
import androidx.compose.frames.commit
import androidx.compose.frames.suspend
@@ -40,7 +38,7 @@
private var invalidations = ObserverMap<Any, RecomposeScope>()
private var removeCommitObserver: (() -> Unit)? = null
- private val handler by lazy { Handler(Looper.getMainLooper()) }
+ private val handler by lazy { Handler(LooperWrapper.getMainLooper()) }
fun ensureStarted() {
if (!started) {
@@ -163,4 +161,4 @@
private inline fun schedule(crossinline block: () -> Unit) {
handler.postAtFrontOfQueue { block() }
}
-}
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/HiddenAttribute.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/HiddenAttribute.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/HiddenAttribute.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/HiddenAttribute.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Immutable.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Immutable.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/Immutable.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Immutable.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/JoinedKey.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/JoinedKey.kt
similarity index 96%
rename from compose/compose-runtime/src/main/java/androidx/compose/JoinedKey.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/JoinedKey.kt
index 601fbf1..bd37846 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/JoinedKey.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/JoinedKey.kt
@@ -16,6 +16,8 @@
package androidx.compose
+import kotlin.jvm.JvmField
+
internal data class JoinedKey(
@JvmField val left: Any?,
@JvmField val right: Any?
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Key.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Key.kt
similarity index 95%
rename from compose/compose-runtime/src/main/java/androidx/compose/Key.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Key.kt
index 00c0414..41e9c56 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Key.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Key.kt
@@ -71,6 +71,6 @@
@Composable
@Suppress("PLUGIN_ERROR")
/* inline */
-fun Key(@Suppress("UNUSED_PARAMETER") @Pivotal key: Any?, @Children children: @Composable() () -> Unit) {
+fun Key(@Suppress("UNUSED_PARAMETER") @Pivotal key: Any?, children: @Composable() () -> Unit) {
children()
}
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Model.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Model.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/Model.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Model.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Observe.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Observe.kt
similarity index 94%
rename from compose/compose-runtime/src/main/java/androidx/compose/Observe.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Observe.kt
index 3a05644..6aa9dcb 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Observe.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Observe.kt
@@ -31,7 +31,7 @@
*/
@Composable
@Suppress("PLUGIN_ERROR")
-fun Observe(@Children body: @Composable() () -> Unit) =
+fun Observe(body: @Composable() () -> Unit) =
currentComposerNonNull.let { composer ->
trace("Compose:Observe") {
composer.startGroup(observer)
@@ -42,4 +42,4 @@
}
}
-private val observer = Object()
\ No newline at end of file
+private val observer = Any()
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/ObserverMap.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ObserverMap.kt
similarity index 97%
rename from compose/compose-runtime/src/main/java/androidx/compose/ObserverMap.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ObserverMap.kt
index 65623e7b..d0e4c36 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/ObserverMap.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ObserverMap.kt
@@ -16,8 +16,6 @@
package androidx.compose
-import java.lang.ref.WeakReference
-
/**
* A map from a key to a set of values used for keeping the relation between some
* entities and a models changes of which this entities are observing.
@@ -104,7 +102,7 @@
*/
private class WeakIdentity<T : Any>(value: T) {
// Save the hash code of value as it might be reclaimed making value.hashCode inaccessible
- private val myHc = System.identityHashCode(value)
+ private val myHc = identityHashCode(value)
// Preserve a weak reference to the value to prevent read observers from leaking observed values
private val weakValue = WeakReference(value)
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Pivotal.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Pivotal.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/Pivotal.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Pivotal.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Recompose.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt
similarity index 96%
rename from compose/compose-runtime/src/main/java/androidx/compose/Recompose.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt
index 421d431..fb57067 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Recompose.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt
@@ -59,7 +59,7 @@
* @see invalidate
*/
@Composable
-fun Recompose(@Children body: @Composable() (recompose: () -> Unit) -> Unit) {
+fun Recompose(body: @Composable() (recompose: () -> Unit) -> Unit) {
val composer = currentComposerNonNull
val recomposer = RecomposeHelper()
val callback = composer.startJoin(false) {
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Recomposer.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recomposer.kt
similarity index 76%
rename from compose/compose-runtime/src/main/java/androidx/compose/Recomposer.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recomposer.kt
index 79ed5df..baa8521 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Recomposer.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recomposer.kt
@@ -16,10 +16,6 @@
package androidx.compose
-import android.annotation.SuppressLint
-import android.os.Looper
-import android.view.Choreographer
-
abstract class Recomposer {
companion object {
@@ -31,17 +27,19 @@
*/
fun hasPendingChanges() = current().hasPendingChanges()
- @SuppressLint("SyntheticAccessor")
internal fun current(): Recomposer {
- assert(Looper.myLooper() == Looper.getMainLooper())
+ require(isMainThread()) {
+ "No Recomposer for this Thread"
+ }
return threadRecomposer.get() ?: error("No Recomposer for this Thread")
}
internal fun recompose(component: Component, composer: Composer<*>) =
current().recompose(component, composer)
- private val threadRecomposer = object : ThreadLocal<Recomposer>() {
- override fun initialValue(): Recomposer? = AndroidRecomposer()
+ // TODO delete the explicit type after https://youtrack.jetbrains.com/issue/KT-20996
+ private val threadRecomposer: ThreadLocal<Recomposer> = object : ThreadLocal<Recomposer>() {
+ override fun initialValue(): Recomposer? = createRecomposer()
}
}
@@ -83,7 +81,8 @@
}
}
- internal abstract fun hasPendingChanges(): Boolean
+ // TODO(138720404): Investigate if Compose APIs can be internal despite MPP limitations
+ protected abstract fun hasPendingChanges(): Boolean
internal fun scheduleRecompose(composer: Composer<*>) {
composers.add(composer)
@@ -104,21 +103,4 @@
}
}
-private class AndroidRecomposer : Recomposer() {
-
- private var frameScheduled = false
-
- private val frameCallback = Choreographer.FrameCallback {
- frameScheduled = false
- dispatchRecomposes()
- }
-
- override fun scheduleChangesDispatch() {
- if (!frameScheduled) {
- frameScheduled = true
- Choreographer.getInstance().postFrameCallback(frameCallback)
- }
- }
-
- override fun hasPendingChanges(): Boolean = frameScheduled
-}
\ No newline at end of file
+internal expect fun createRecomposer(): Recomposer
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/SlotTable.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/SlotTable.kt
similarity index 90%
rename from compose/compose-runtime/src/main/java/androidx/compose/SlotTable.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/SlotTable.kt
index 4193169..d93deac2 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/SlotTable.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/SlotTable.kt
@@ -172,7 +172,7 @@
nodeCount = 0
if (validate) {
val groupStart = advance().asGroupStart
- assert(groupStart.kind == kind) { "Group kind changed" }
+ require(groupStart.kind == kind) { "Group kind changed" }
currentEnd = current + groupStart.slots
}
}
@@ -197,16 +197,16 @@
internal fun recordEndGroup(writing: Boolean, inserting: Boolean, uncertain: Boolean): Int {
var count = nodeCount
- assert(!startStack.isEmpty()) {
+ require(startStack.isNotEmpty()) {
"Invalid state. Unbalanced calls to startGroup() and endGroup()"
}
- assert(inserting || current == currentEnd) { "Expected to be at the end of a group" }
+ require(inserting || current == currentEnd) { "Expected to be at the end of a group" }
// Update group length
val startLocation = startStack.pop()
val groupKind = groupKindStack.pop()
val effectiveStartLocation = effectiveIndex(startLocation)
- assert(slots[effectiveStartLocation] === SlotTable.EMPTY ||
+ require(slots[effectiveStartLocation] === SlotTable.EMPTY ||
slots[effectiveStartLocation] is GroupStart
) {
"Invalid state. Start location stack doesn't refer to a start location"
@@ -217,7 +217,7 @@
} else {
val start = slots[effectiveStartLocation].asGroupStart
// A node count < 0 means that it was reported as uncertain while reading
- assert(start.slots == len && (nodeCount == start.nodes || uncertain)) {
+ require(start.slots == len && (nodeCount == start.nodes || uncertain)) {
"Invalid endGroup call, expected ${start.slots} slots and ${
start.nodes} nodes but received, $len slots and $nodeCount nodes"
}
@@ -261,7 +261,7 @@
*/
fun previous() {
if (emptyCount <= 0) {
- assert(current > 0) { "Invalid call to previous" }
+ require(current > 0) { "Invalid call to previous" }
current--
}
}
@@ -280,7 +280,7 @@
* End reporting empty for calls to net() and get().
*/
fun endEmpty() {
- assert(emptyCount > 0) { "Unbalanced begin/end empty" }
+ require(emptyCount > 0) { "Unbalanced begin/end empty" }
emptyCount--
}
@@ -302,7 +302,7 @@
* Skip a group. Must be called at the start of a group.
*/
fun skipGroup(): Int {
- assert(emptyCount == 0) { "Cannot skip while in an empty region" }
+ require(emptyCount == 0) { "Cannot skip while in an empty region" }
return advanceToNextGroup()
}
@@ -310,8 +310,8 @@
* Skip the to the end of the group.
*/
fun skipEnclosingGroup(): Int {
- assert(emptyCount == 0) { "Cannot skip the enclosing group while in an empty region" }
- assert(startStack.isNotEmpty()) { "No enclosing group to skip" }
+ require(emptyCount == 0) { "Cannot skip the enclosing group while in an empty region" }
+ require(startStack.isNotEmpty()) { "No enclosing group to skip" }
val startLocation = startStack.peek()
val start = get(startLocation).asGroupStart
current = currentEnd
@@ -347,7 +347,7 @@
* Skip the current item
*/
fun skipItem(): Int {
- assert(emptyCount == 0) { "Cannot skip an item in an empty region" }
+ require(emptyCount == 0) { "Cannot skip an item in an empty region" }
return advanceToNextItem()
}
@@ -376,7 +376,10 @@
}
override fun toString(): String {
- return "${javaClass.simpleName}(current=$current, size=${slots.size - table.gapLen}, gap=${
+ return "${this::class.simpleName}" +
+ "(current=$current, " +
+ "size=${slots.size - table.gapLen}, " +
+ "gap=${
if (table.gapLen > 0) "$table.gapStart-${table.gapStart + table.gapLen - 1}" else "none"}${
if (inEmpty) ", in empty" else ""})"
}
@@ -420,7 +423,7 @@
* current to be before the key.
*/
fun previous() {
- assert(current > 0) { "Invalid call to previous" }
+ require(current > 0) { "Invalid call to previous" }
current--
}
@@ -436,7 +439,7 @@
* Ends inserting.
*/
fun endInsert() {
- assert(insertCount > 0) { "Unbalenced begin/end insert" }
+ require(insertCount > 0) { "Unbalenced begin/end insert" }
insertCount--
}
@@ -458,7 +461,7 @@
* Skip a group. Must be called at the start of a group.
*/
fun skipGroup(): Int {
- assert(insertCount == 0) { "Cannot skip while inserting" }
+ require(insertCount == 0) { "Cannot skip while inserting" }
return advanceToNextGroup()
}
@@ -473,7 +476,7 @@
* a keyed group is expected.
*/
fun moveItem(offset: Int) {
- assert(insertCount == 0) { "Cannot move an item while inserting" }
+ require(insertCount == 0) { "Cannot move an item while inserting" }
val oldCurrent = current
val oldNodeCount = nodeCount
@@ -495,7 +498,7 @@
val newMoveLocation = moveLocation + moveLen
current = oldCurrent
nodeCount = oldNodeCount
- System.arraycopy(
+ arraycopy(
slots,
effectiveIndex(newMoveLocation),
slots,
@@ -508,14 +511,14 @@
// Remove the now duplicate entries
val anchorsRemoved = remove(moveLocation + moveLen, moveLen)
- assert(!anchorsRemoved) { "Unexpectedly removed anchors" }
+ require(!anchorsRemoved) { "Unexpectedly removed anchors" }
}
/**
* Remove an item. Must be called at the startGroup of an item.
*/
fun removeItem(): Boolean {
- assert(insertCount == 0) { "Cannot remove and item while inserting" }
+ require(insertCount == 0) { "Cannot remove and item while inserting" }
val oldCurrent = current
val count = advanceToNextItem()
val anchorsRemoved = remove(oldCurrent, current - oldCurrent)
@@ -560,7 +563,7 @@
* Skip the current item
*/
fun skipItem(): Int {
- assert(insertCount == 0) { "Cannot skip an item while inserting" }
+ require(insertCount == 0) { "Cannot skip an item while inserting" }
return advanceToNextItem()
}
@@ -579,10 +582,10 @@
if (table.anchors.isNotEmpty()) table.updateAnchors(index)
if (index < table.gapStart) {
val len = table.gapStart - index
- System.arraycopy(slots, index, slots, index + table.gapLen, len)
+ arraycopy(slots, index, slots, index + table.gapLen, len)
} else {
val len = index - table.gapStart
- System.arraycopy(
+ arraycopy(
slots,
table.gapStart + table.gapLen,
slots,
@@ -607,8 +610,8 @@
val oldCapacity = slots.size
val oldSize = slots.size - table.gapLen
// Double the size of the array, but at least MIN_GROWTH_SIZE and >= size
- val newCapacity = Math.max(
- Math.max(oldCapacity * 2, oldSize + size),
+ val newCapacity = kotlin.math.max(
+ kotlin.math.max(oldCapacity * 2, oldSize + size),
MIN_GROWTH_SIZE
)
val newSlots = arrayOfNulls<Any?>(newCapacity)
@@ -616,8 +619,8 @@
val oldGapEnd = table.gapStart + table.gapLen
val newGapEnd = table.gapStart + newGapLen
// Copy the old array into the new array
- System.arraycopy(slots, 0, newSlots, 0, table.gapStart)
- System.arraycopy(slots, oldGapEnd, newSlots, newGapEnd, oldCapacity - oldGapEnd)
+ arraycopy(slots, 0, newSlots, 0, table.gapStart)
+ arraycopy(slots, oldGapEnd, newSlots, newGapEnd, oldCapacity - oldGapEnd)
// Update the anchors
if (table.anchors.isNotEmpty()) table.anchorGapResize(newGapLen - table.gapLen)
@@ -638,7 +641,8 @@
}
}
- internal fun remove(start: Int, len: Int): Boolean {
+ // TODO(138720404): Investigate if Compose APIs can be internal despite MPP limitations
+ fun remove(start: Int, len: Int): Boolean {
return if (len > 0) {
pendingClear = false
var anchorsRemoved = false
@@ -667,7 +671,10 @@
pendingClear = false
table.clearGap()
}
- return "${javaClass.simpleName}(current=$current, size=${slots.size - table.gapLen}, gap=${
+ return "${this::class.simpleName}" +
+ "(current=$current, " +
+ "size=${slots.size - table.gapLen}, " +
+ "gap=${
if (table.gapLen > 0) "$table.gapStart-${table.gapStart + table.gapLen - 1}" else "none"}${
if (insertCount > 0) ", inserting" else ""})"
}
@@ -708,12 +715,12 @@
}
internal fun close(reader: SlotReader) {
- assert(reader.table === this && readers > 0) { "Unexpected reader close()" }
+ require(reader.table === this && readers > 0) { "Unexpected reader close()" }
readers--
}
internal fun close(writer: SlotWriter) {
- assert(writer.table === this && this.writer) { "Unexpected writer close()" }
+ require(writer.table === this && this.writer) { "Unexpected writer close()" }
this.writer = false
clearGap()
}
@@ -722,7 +729,8 @@
internal fun clearGap() = repeat(gapLen) { i -> slots[gapStart + i] = null }
- internal fun anchor(index: Int): Anchor {
+ // TODO(138720404): Investigate if Compose APIs can be internal despite MPP limitations
+ fun anchor(index: Int): Anchor {
// TODO: Consider a buffer gap list of anchors if middle inserts and deletes are common
val anchorIndex = effectiveIndex(index)
val location = anchors.search(anchorIndex)
@@ -819,7 +827,8 @@
}
}
- internal fun anchorLocation(anchor: Anchor) = anchor.loc.let {
+ // TODO(138720404): Investigate if Compose APIs can be internal despite MPP limitations
+ fun anchorLocation(anchor: Anchor) = anchor.loc.let {
if (it > gapStart) it - gapLen else it
}
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/StableMarker.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/StableMarker.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/StableMarker.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/StableMarker.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Stack.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stack.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/Stack.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stack.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Stateful.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stateful.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/Stateful.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stateful.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Trace.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Trace.kt
similarity index 92%
rename from compose/compose-runtime/src/main/java/androidx/compose/Trace.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Trace.kt
index e451296..24e89e2 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Trace.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Trace.kt
@@ -16,7 +16,10 @@
package androidx.compose
-import android.os.Trace
+expect object Trace {
+ fun beginSection(name: String)
+ fun endSection()
+}
/**
* Wrap the specified [block] in calls to [Trace.beginSection] (with the supplied [sectionName])
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/UnionType.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/UnionType.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/UnionType.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/UnionType.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ViewComposerCommon.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ViewComposerCommon.kt
new file mode 100644
index 0000000..a6a573c6
--- /dev/null
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ViewComposerCommon.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 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.compose
+
+internal expect val currentComposerNonNull: Composer<*>
+internal expect var currentComposer: Composer<*>?
+
+internal expect fun createComposer(root: Any, context: Context, recomposer: Recomposer): Composer<*>
+expect fun <T> Composer<*>.runWithCurrent(block: () -> T): T
+
+// TODO(138720404): Investigate if Compose APIs can be internal despite MPP limitations
+/*@PublishedApi
+internal*/ val invocation = Any()
+
+@PublishedApi
+internal val provider = Any()
+
+@PublishedApi
+internal val consumer = Any()
+
+@PublishedApi
+internal val reference = Any()
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/annotations/Hide.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/annotations/Hide.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/annotations/Hide.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/annotations/Hide.kt
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/frames/FrameContainers.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/FrameContainers.kt
similarity index 81%
rename from compose/compose-runtime/src/main/java/androidx/compose/frames/FrameContainers.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/FrameContainers.kt
index 596d8fa0..716b22b 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/frames/FrameContainers.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/FrameContainers.kt
@@ -1,5 +1,23 @@
+/*
+ * Copyright 2019 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.compose.frames
+import kotlin.jvm.JvmField
+
class ModelList<T> : MutableList<T>, Framed {
private var myFirst: Record =
ArrayContainer<T>()
@@ -12,16 +30,16 @@
@Suppress("UNCHECKED_CAST") private val readable: ArrayContainer<T>
get() =
- _readable(
- myFirst,
- this
- ) as ArrayContainer<T>
+ _readable(
+ myFirst,
+ this
+ ) as ArrayContainer<T>
@Suppress("UNCHECKED_CAST") private val writable: ArrayContainer<T>
get() =
- _writable(
- myFirst,
- this
- ) as ArrayContainer<T>
+ _writable(
+ myFirst,
+ this
+ ) as ArrayContainer<T>
fun asMutable(): MutableList<T> = writable.list
@@ -53,12 +71,13 @@
writable.list.subList(fromIndex, toIndex)
private class ArrayContainer<T> : AbstractRecord() {
- @JvmField var list: ArrayList<T> = arrayListOf<T>()
+ @JvmField
+ var list: ArrayList<T> = arrayListOf<T>()
override fun assign(value: Record) {
@Suppress("UNCHECKED_CAST")
(value as? ArrayContainer<T>)?.let {
- this.list = it.list.clone() as ArrayList<T>
+ this.list = it.list.toList() as ArrayList<T>
}
}
@@ -145,23 +164,26 @@
override fun retainAll(elements: Collection<T>): Boolean = error()
}
+// TODO delete the explicit type after https://youtrack.jetbrains.com/issue/KT-20996
private fun <T> immutableIterator(
iterator: MutableIterator<T>
-) = object : MutableIterator<T> by iterator {
+): MutableIterator<T> = object : MutableIterator<T> by iterator {
override fun remove() = error()
}
+// TODO delete the explicit type after https://youtrack.jetbrains.com/issue/KT-20996
private fun <T> immutableListIterator(
iterator: MutableListIterator<T>
-) = object : MutableListIterator<T> by iterator {
+): MutableListIterator<T> = object : MutableListIterator<T> by iterator {
override fun add(element: T) = error()
override fun remove() = error()
override fun set(element: T) = error()
}
+// TODO delete the explicit type after https://youtrack.jetbrains.com/issue/KT-20996
private fun <T> immutableCollection(
collection: MutableCollection<T>
-) = object : MutableCollection<T> by collection {
+): MutableCollection<T> = object : MutableCollection<T> by collection {
override fun add(element: T): Boolean = error()
override fun addAll(elements: Collection<T>): Boolean = error()
override fun clear() = error()
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/frames/Frames.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/Frames.kt
similarity index 93%
rename from compose/compose-runtime/src/main/java/androidx/compose/frames/Frames.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/Frames.kt
index d70216e..02ead93 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/frames/Frames.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/Frames.kt
@@ -1,11 +1,48 @@
+/*
+ * Copyright 2019 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.compose.frames
-import java.util.BitSet
-import java.util.HashSet
+import androidx.compose.BitSet
+import androidx.compose.ThreadLocal
+import androidx.compose.synchronized
class FrameAborted(val frame: Frame) : RuntimeException("Frame aborted")
/**
+ * The frame records are created with frame ID CREATION_FRAME when not in a frame.
+ * This allows framed object to be created in the in static initializers when a
+ * frame could not have been created yet.
+ *
+ * The value 2 was chosen because it must be greater than 0, as 0 is reserved to
+ * indicated an invalid frame (in order to avoid an uninitialized record begin
+ * treated a valid record) and 1 is odd and treated as a speculation frame. That
+ * leaves 2 as the lowest valid frame.
+ */
+private const val CREATION_FRAME = 2
+
+/**
+ * Base implementation of a frame record
+ */
+abstract class AbstractRecord : Record {
+ override var frameId: Int = threadFrame.get()?.id ?: CREATION_FRAME
+ override var next: Record? = null
+}
+
+/**
* Frame local values of a framed object.
*/
interface Record {
@@ -30,32 +67,17 @@
fun create(): Record
}
-/**
- * The frame records are created with frame ID CREATION_FRAME when not in a frame.
- * This allows framed object to be created in the in static initializers when a
- * frame could not have been created yet.
- *
- * The value 2 was chosen because it must be greater than 0, as 0 is reserved to
- * indicated an invalid frame (in order to avoid an uninitialized record begin
- * treated a valid record) and 1 is odd and treated as a speculation frame. That
- * leaves 2 as the lowest valid frame.
- */
-private const val CREATION_FRAME = 2
-
-/**
- * Base implementation of a frame record
- */
-abstract class AbstractRecord : Record {
- override var frameId: Int = threadFrame.get()?.id ?: CREATION_FRAME
- override var next: Record? = null
+interface Framed {
+ val firstFrameRecord: Record
+ fun prependFrameRecord(value: Record)
}
-internal val threadFrame = ThreadLocal<Frame>()
-
typealias FrameReadObserver = (read: Any) -> Unit
typealias FrameWriteObserver = (write: Any) -> Unit
typealias FrameCommitObserver = (committed: Set<Any>) -> Unit
+private val threadFrame = ThreadLocal<Frame>()
+
/**
* Information about a frame including the frame id and whether or not it is read only.
*/
@@ -88,7 +110,8 @@
) {
internal val modified = if (readOnly) null else HashSet<Framed>()
- internal val readObservers = mutableListOf<FrameReadObserver>()
+ // TODO(138720404): Investigate if Compose APIs can be internal despite MPP limitations
+ val readObservers = mutableListOf<FrameReadObserver>()
init {
if (readObserver != null) {
@@ -129,7 +152,7 @@
val inFrame: Boolean get() = threadFrame.get() != null
// A global synchronization object
-private val sync = Object()
+private val sync = Any()
// The following variables should only be written when sync is taken
private val openFrames = BitSet()
@@ -418,11 +441,6 @@
fun _writable(r: Record, framed: Framed): Record = r.writable(framed)
fun _created(framed: Framed) = threadFrame.get()?.writeObserver?.let { it(framed) }
-interface Framed {
- val firstFrameRecord: Record
- fun prependFrameRecord(value: Record)
-}
-
fun <T : Record> T.writable(framed: Framed): T {
return this.writable(framed, currentFrame())
}
@@ -494,8 +512,8 @@
@Suppress("UNCHECKED_CAST")
(used(framed, id, frame.invalid) as T?)?.apply { frameId = Int.MAX_VALUE }
?: readData.create().apply {
- frameId = Int.MAX_VALUE; framed.prependFrameRecord(this as T)
- } as T
+ frameId = Int.MAX_VALUE; framed.prependFrameRecord(this as T)
+ } as T
}
newData.assign(readData)
newData.frameId = id
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/sourceLocation.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/sourceLocation.kt
similarity index 100%
rename from compose/compose-runtime/src/main/java/androidx/compose/sourceLocation.kt
rename to compose/compose-runtime/src/commonMain/kotlin/androidx/compose/sourceLocation.kt
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/ComposeRobolectricTestRunner.kt b/compose/compose-runtime/src/test/java/androidx/compose/ComposeRobolectricTestRunner.kt
deleted file mode 100644
index ff656ad..0000000
--- a/compose/compose-runtime/src/test/java/androidx/compose/ComposeRobolectricTestRunner.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2019 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.compose
-
-import org.junit.runners.model.FrameworkMethod
-import org.robolectric.RobolectricTestRunner
-import org.robolectric.internal.bytecode.InstrumentationConfiguration
-
-class ComposeRobolectricTestRunner(testClass: Class<*>) : RobolectricTestRunner(testClass) {
- override fun createClassLoaderConfig(method: FrameworkMethod?): InstrumentationConfiguration {
- val builder = InstrumentationConfiguration.Builder(super.createClassLoaderConfig(method))
- builder.doNotInstrumentPackage("androidx.compose")
- return builder.build()
- }
-}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/ComposePoints.kt b/compose/compose-runtime/src/test/java/androidx/compose/mock/ComposePoints.kt
deleted file mode 100644
index 9bf3948..0000000
--- a/compose/compose-runtime/src/test/java/androidx/compose/mock/ComposePoints.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package androidx.compose.mock
-
-fun MockViewComposition.point(point: Point) {
- text("X: ${point.x} Y: ${point.y}")
-}
-
-fun MockViewValidator.point(point: Point) {
- text("X: ${point.x} Y: ${point.y}")
-}
-
-object SLPoints
-
-fun MockViewComposition.points(points: Iterable<Point>) {
- repeat(of = points) {
- memoize(SLPoints, it) { point(it) }
- }
-}
-
-fun MockViewValidator.points(points: Iterable<Point>) {
- repeat(of = points) {
- point(it)
- }
-}
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/ComposeReport.kt b/compose/compose-runtime/src/test/java/androidx/compose/mock/ComposeReport.kt
deleted file mode 100644
index 057f9ab..0000000
--- a/compose/compose-runtime/src/test/java/androidx/compose/mock/ComposeReport.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package androidx.compose.mock
-
-fun MockViewComposition.reportsTo(report: Report) {
- text(report.from)
- text("reports to")
- text(report.to)
-}
-fun MockViewValidator.reportsTo(report: Report) {
- text(report.from)
- text("reports to")
- text(report.to)
-}
-
-fun MockViewComposition.reportsReport(reports: Iterable<Report>) {
- linear {
- repeat(of = reports) { report ->
- reportsTo(report)
- }
- }
-}
-
-fun MockViewValidator.reportsReport(reports: Iterable<Report>) {
- linear {
- repeat(of = reports) { report ->
- reportsTo(report)
- }
- }
-}
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/mock/ContactModel.kt b/compose/compose-runtime/src/test/java/androidx/compose/mock/ContactModel.kt
deleted file mode 100644
index 3d16cf7..0000000
--- a/compose/compose-runtime/src/test/java/androidx/compose/mock/ContactModel.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package androidx.compose.mock
-
-class ContactModel(
- var filter: String = "",
- val contacts: MutableList<Contact>,
- var selected: Contact? = null
-) {
- val filtered get() = contacts.filter { it.name.contains(filter) }
-
- fun add(contact: Contact, after: Contact? = null) {
- if (after == null) {
- contacts.add(contact)
- } else {
- contacts.add(find(after) + 1, contact)
- }
- }
-
- fun move(contact: Contact, after: Contact?) {
- if (after == null) {
- contacts.removeAt(find(contact))
- contacts.add(0, contact)
- } else {
- contacts.removeAt(find(contact))
- contacts.add(find(after) + 1, contact)
- }
- }
-
- private fun find(contact: Contact): Int {
- val index = contacts.indexOf(contact)
- if (index < 0) error("Contact $contact not found")
- return index
- }
-}
diff --git a/content/build.gradle b/content/build.gradle
index 4a27186..cafb452 100644
--- a/content/build.gradle
+++ b/content/build.gradle
@@ -26,7 +26,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(JUNIT)
diff --git a/coordinatorlayout/build.gradle b/coordinatorlayout/build.gradle
index 6050e84..ae2a733 100644
--- a/coordinatorlayout/build.gradle
+++ b/coordinatorlayout/build.gradle
@@ -11,7 +11,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
// TODO: change to 1.1.0-alpha04 after release
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.0.0")
api("androidx.customview:customview:1.0.0")
diff --git a/dynamic-animation/build.gradle b/dynamic-animation/build.gradle
index 6ad3e31..fe7bd58 100644
--- a/dynamic-animation/build.gradle
+++ b/dynamic-animation/build.gradle
@@ -9,7 +9,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
index 4e13514..0b811cb 100644
--- a/emoji/core/build.gradle
+++ b/emoji/core/build.gradle
@@ -22,7 +22,7 @@
// treats this as local jar and package it inside the aar.
api files(configurations.repackage)
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation(project(':collection:collection'))
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index 777cb59..669429a 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -18,7 +18,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
api("androidx.collection:collection:1.1.0")
api("androidx.viewpager:viewpager:1.0.0")
api("androidx.loader:loader:1.0.0")
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index f5e1ba4..a3e453a 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/gridlayout/build.gradle b/gridlayout/build.gradle
index 58ab795..efb7375 100644
--- a/gridlayout/build.gradle
+++ b/gridlayout/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api(ANDROIDX_ANNOTATION)
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/leanback/build.gradle b/leanback/build.gradle
index df32fcf..7588656 100644
--- a/leanback/build.gradle
+++ b/leanback/build.gradle
@@ -11,7 +11,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
api("androidx.interpolator:interpolator:1.0.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.0.0")
api("androidx.media:media:1.0.0")
api("androidx.fragment:fragment:1.0.0")
diff --git a/legacy/core-utils/build.gradle b/legacy/core-utils/build.gradle
index c4fed24..7a331a9 100644
--- a/legacy/core-utils/build.gradle
+++ b/legacy/core-utils/build.gradle
@@ -9,7 +9,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
api(project(":documentfile"))
api(project(":loader:loader"))
api(project(":localbroadcastmanager"))
diff --git a/media/build.gradle b/media/build.gradle
index facba79..913a501 100644
--- a/media/build.gradle
+++ b/media/build.gradle
@@ -9,7 +9,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.0.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/media2/integration-tests/testapp/build.gradle b/media2/integration-tests/testapp/build.gradle
index ffefd37..b269e6a 100644
--- a/media2/integration-tests/testapp/build.gradle
+++ b/media2/integration-tests/testapp/build.gradle
@@ -30,7 +30,7 @@
implementation(project(":media2:media2-player"))
implementation(project(":media2:media2-widget"))
implementation("androidx.appcompat:appcompat:1.0.2")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
}
android {
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/MediaBrowserLegacyTest.java b/media2/session/src/androidTest/java/androidx/media2/session/MediaBrowserLegacyTest.java
index 33d62d8..061098b 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/MediaBrowserLegacyTest.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/MediaBrowserLegacyTest.java
@@ -40,6 +40,7 @@
import androidx.media2.session.MediaBrowser.BrowserCallback;
import androidx.media2.session.MediaLibraryService.LibraryParams;
import androidx.media2.session.MockMediaBrowserServiceCompat.Proxy;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import com.google.common.util.concurrent.ListenableFuture;
@@ -47,6 +48,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@@ -57,6 +59,7 @@
/**
* Tests {@link MediaBrowser} with {@link MediaBrowserServiceCompat}.
*/
+@RunWith(AndroidJUnit4.class)
@LargeTest
public class MediaBrowserLegacyTest extends MediaSessionTestBase {
private static final String TAG = "MediaBrowserLegacyTest";
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/MediaBrowserTest.java b/media2/session/src/androidTest/java/androidx/media2/session/MediaBrowserTest.java
index a063c09..68165d2 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/MediaBrowserTest.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/MediaBrowserTest.java
@@ -391,8 +391,7 @@
}
@Test
- public void testBrowserCallback_onChildrenChangedIsCalledWhenSubscribed()
- throws InterruptedException {
+ public void testBrowserCallback_onChildrenChangedIsCalledWhenSubscribed() throws Exception {
// This test uses MediaLibrarySession.notifyChildrenChanged().
prepareLooper();
final String expectedParentId = "expectedParentId";
@@ -434,7 +433,10 @@
TestServiceRegistry.getInstance().setSessionCallback(sessionCallback);
MockMediaLibraryService.setAssertLibraryParams(testParams);
- createBrowser(controllerCallbackProxy).subscribe(expectedParentId, testParams);
+ LibraryResult result = createBrowser(controllerCallbackProxy)
+ .subscribe(expectedParentId, testParams)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
// onChildrenChanged() should be called.
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -497,8 +499,7 @@
}
@Test
- public void testBrowserCallback_onChildrenChangedIsCalledWhenSubscribed2()
- throws InterruptedException {
+ public void testBrowserCallback_onChildrenChangedIsCalledWhenSubscribed2() throws Exception {
// This test uses MediaLibrarySession.notifyChildrenChanged(ControllerInfo).
prepareLooper();
final String expectedParentId = "expectedParentId";
@@ -540,7 +541,10 @@
};
TestServiceRegistry.getInstance().setSessionCallback(sessionCallback);
- createBrowser(controllerCallbackProxy).subscribe(expectedParentId, null);
+ LibraryResult result = createBrowser(controllerCallbackProxy)
+ .subscribe(expectedParentId, null)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
// onChildrenChanged() should be called.
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java b/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java
index 57af209..a839548 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java
@@ -24,7 +24,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import android.app.PendingIntent;
import android.content.Context;
@@ -143,14 +142,11 @@
}
@Test
- public void testPlay() {
+ public void testPlay() throws Exception {
prepareLooper();
- mController.play();
- try {
- assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } catch (InterruptedException e) {
- fail(e.getMessage());
- }
+ SessionResult result = mController.play().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
+ assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mPlayCalled);
}
@@ -161,46 +157,39 @@
final MockPlayer player = new MockPlayer(2);
player.mLastPlayerState = SessionPlayer.PLAYER_STATE_IDLE;
mSession.updatePlayer(player);
- mController.play();
+ SessionResult result = mController.play().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(player.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(player.mPlayCalled);
assertTrue(player.mPrepareCalled);
}
@Test
- public void testPause() {
+ public void testPause() throws Exception {
prepareLooper();
- mController.pause();
- try {
- assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } catch (InterruptedException e) {
- fail(e.getMessage());
- }
+ SessionResult result = mController.pause().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
+ assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mPauseCalled);
}
@Test
- public void testPrepare() {
+ public void testPrepare() throws Exception {
prepareLooper();
- mController.prepare();
- try {
- assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } catch (InterruptedException e) {
- fail(e.getMessage());
- }
+ SessionResult result = mController.prepare().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
+ assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mPrepareCalled);
}
@Test
- public void testSeekTo() {
+ public void testSeekTo() throws Exception {
prepareLooper();
final long seekPosition = 12125L;
- mController.seekTo(seekPosition);
- try {
- assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } catch (InterruptedException e) {
- fail(e.getMessage());
- }
+ SessionResult result = mController.seekTo(seekPosition)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
+ assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSeekToCalled);
assertEquals(seekPosition, mPlayer.mSeekPosition);
}
@@ -270,7 +259,7 @@
MockPlayer player = new MockPlayer(0);
player.mLastPlayerState = testState;
- player.setAudioAttributes(testAudioAttributes);
+ player.mAudioAttributes = testAudioAttributes;
player.mPlaylist = testPlaylist;
mSession.updatePlayer(player);
@@ -290,10 +279,12 @@
}
@Test
- public void testSetPlaylist() throws InterruptedException {
+ public void testSetPlaylist() throws Exception {
prepareLooper();
final List<String> list = TestUtils.createMediaIds(2);
- mController.setPlaylist(list, null /* Metadata */);
+ SessionResult result = mController.setPlaylist(list, null /* Metadata */)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSetPlaylistCalled);
@@ -311,9 +302,9 @@
public void testSetMediaItem() throws Exception {
prepareLooper();
String mediaId = "testSetMediaItem";
- int resultCode = mController.setMediaItem(mediaId).get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
- .getResultCode();
- assertEquals(RESULT_SUCCESS, resultCode);
+ SessionResult result = mController.setMediaItem(mediaId)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertNull(mPlayer.mMetadata);
@@ -386,10 +377,12 @@
}
@Test
- public void testUpdatePlaylistMetadata() throws InterruptedException {
+ public void testUpdatePlaylistMetadata() throws Exception {
prepareLooper();
final MediaMetadata testMetadata = TestUtils.createMetadata();
- mController.updatePlaylistMetadata(testMetadata);
+ SessionResult result = mController.updatePlaylistMetadata(testMetadata)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mUpdatePlaylistMetadataCalled);
@@ -430,7 +423,9 @@
public void testSetPlaybackSpeed() throws Exception {
prepareLooper();
final float speed = 1.5f;
- mController.setPlaybackSpeed(speed);
+ SessionResult result = mController.setPlaybackSpeed(speed)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(speed, mPlayer.mPlaybackSpeed, 0.0f);
}
@@ -445,7 +440,7 @@
public void testGetPlaybackSpeed() throws InterruptedException {
prepareLooper();
final float speed = 1.5f;
- mPlayer.setPlaybackSpeed(speed);
+ mPlayer.mPlaybackSpeed = speed;
final CountDownLatch latch = new CountDownLatch(1);
final MediaController controller =
@@ -562,7 +557,7 @@
};
final MediaController controller = createController(mSession.getToken(), true, null,
callback);
- mSession.getPlayer().setPlaylist(testPlaylist, null);
+ mPlayer.mPlaylist = testPlaylist;
mPlayer.mBufferedPosition = testBufferingPosition;
mPlayer.notifyBufferingStateChanged(testItem, testBufferingState);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -606,7 +601,7 @@
prepareLooper();
final int listSize = 5;
final List<MediaItem> list = TestUtils.createMediaItems(listSize);
- mPlayer.setPlaylist(list, null);
+ mPlayer.mPlaylist = list;
final int index = 3;
final MediaItem currentItem = list.get(index);
@@ -639,22 +634,25 @@
assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Known DSD should be notified through the onCurrentMediaItemChanged.
- mPlayer.skipToPlaylistItem(index);
+ mPlayer.mIndex = index;
+ mPlayer.mCurrentMediaItem = mPlayer.mItem = mPlayer.mPlaylist.get(index);
mPlayer.notifyCurrentMediaItemChanged(currentItem);
assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Null DSD becomes null MediaItem.
- mPlayer.setMediaItem(null);
+ mPlayer.mCurrentMediaItem = mPlayer.mItem = null;
mPlayer.notifyCurrentMediaItemChanged(null);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
- public void testAddPlaylistItem() throws InterruptedException {
+ public void testAddPlaylistItem() throws Exception {
prepareLooper();
final int testIndex = 12;
final String testId = "testAddPlaylistItem";
- mController.addPlaylistItem(testIndex, testId);
+ SessionResult result = mController.addPlaylistItem(testIndex, testId)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mAddPlaylistItemCalled);
@@ -663,7 +661,7 @@
}
@Test
- public void testRemovePlaylistItem() throws InterruptedException {
+ public void testRemovePlaylistItem() throws Exception {
prepareLooper();
mPlayer.mPlaylist = TestUtils.createMediaItems(2);
@@ -672,7 +670,9 @@
// player.
MediaController controller = createController(mSession.getToken());
int targetIndex = 0;
- controller.removePlaylistItem(targetIndex);
+ SessionResult result = controller.removePlaylistItem(targetIndex)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mRemovePlaylistItemCalled);
@@ -680,11 +680,13 @@
}
@Test
- public void testReplacePlaylistItem() throws InterruptedException {
+ public void testReplacePlaylistItem() throws Exception {
prepareLooper();
final int testIndex = 12;
final String testId = "testAddPlaylistItem";
- mController.replacePlaylistItem(testIndex, testId);
+ SessionResult result = mController.replacePlaylistItem(testIndex, testId)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mReplacePlaylistItemCalled);
@@ -693,29 +695,35 @@
}
@Test
- public void testSkipToPreviousItem() throws InterruptedException {
+ public void testSkipToPreviousItem() throws Exception {
prepareLooper();
- mController.skipToPreviousPlaylistItem();
+ SessionResult result = mController.skipToPreviousPlaylistItem()
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSkipToPreviousItemCalled);
}
@Test
- public void testSkipToNextItem() throws InterruptedException {
+ public void testSkipToNextItem() throws Exception {
prepareLooper();
- mController.skipToNextPlaylistItem();
+ SessionResult result = mController.skipToNextPlaylistItem()
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSkipToNextItemCalled);
}
@Test
- public void testSkipToPlaylistItem() throws InterruptedException {
+ public void testSkipToPlaylistItem() throws Exception {
prepareLooper();
List<MediaItem> playlist = TestUtils.createMediaItems(2);
int targetIndex = 1;
mPlayer.mPlaylist = playlist;
MediaController controller = createController(mSession.getToken());
- controller.skipToPlaylistItem(targetIndex);
+ SessionResult result = controller.skipToPlaylistItem(targetIndex)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSkipToPlaylistItemCalled);
@@ -745,10 +753,12 @@
}
@Test
- public void testSetShuffleMode() throws InterruptedException {
+ public void testSetShuffleMode() throws Exception {
prepareLooper();
final int testShuffleMode = SessionPlayer.SHUFFLE_MODE_GROUP;
- mController.setShuffleMode(testShuffleMode);
+ SessionResult result = mController.setShuffleMode(testShuffleMode)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSetShuffleModeCalled);
@@ -778,10 +788,12 @@
}
@Test
- public void testSetRepeatMode() throws InterruptedException {
+ public void testSetRepeatMode() throws Exception {
prepareLooper();
final int testRepeatMode = SessionPlayer.REPEAT_MODE_GROUP;
- mController.setRepeatMode(testRepeatMode);
+ SessionResult result = mController.setRepeatMode(testRepeatMode)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSetRepeatModeCalled);
@@ -875,7 +887,9 @@
final MediaController controller = createController(mSession.getToken(), true, null, null);
final int targetVolume = 50;
- controller.setVolumeTo(targetVolume, 0 /* flags */);
+ SessionResult result = controller.setVolumeTo(targetVolume, 0 /* flags */)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(remotePlayer.mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(remotePlayer.mSetVolumeToCalled);
assertEquals(targetVolume, (int) remotePlayer.mCurrentVolume);
@@ -894,7 +908,9 @@
final MediaController controller = createController(mSession.getToken(), true, null, null);
final int direction = AudioManager.ADJUST_RAISE;
- controller.adjustVolume(direction, 0 /* flags */);
+ SessionResult result = controller.adjustVolume(direction, 0 /* flags */)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(remotePlayer.mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(remotePlayer.mAdjustVolumeCalled);
assertEquals(direction, remotePlayer.mDirection);
@@ -923,7 +939,7 @@
AudioAttributesCompat attrs = new AudioAttributesCompat.Builder()
.setLegacyStreamType(stream)
.build();
- mPlayer.setAudioAttributes(attrs);
+ mPlayer.mAudioAttributes = attrs;
mSession.updatePlayer(mPlayer);
final int originalVolume = mAudioManager.getStreamVolume(stream);
@@ -931,7 +947,9 @@
? originalVolume + 1 : originalVolume - 1;
Log.d(TAG, "originalVolume=" + originalVolume + ", targetVolume=" + targetVolume);
- mController.setVolumeTo(targetVolume, AudioManager.FLAG_SHOW_UI);
+ SessionResult result = mController.setVolumeTo(targetVolume, AudioManager.FLAG_SHOW_UI)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
new PollingCheck(VOLUME_CHANGE_TIMEOUT_MS) {
@Override
protected boolean check() {
@@ -966,7 +984,7 @@
AudioAttributesCompat attrs = new AudioAttributesCompat.Builder()
.setLegacyStreamType(stream)
.build();
- mPlayer.setAudioAttributes(attrs);
+ mPlayer.mAudioAttributes = attrs;
mSession.updatePlayer(mPlayer);
final int originalVolume = mAudioManager.getStreamVolume(stream);
@@ -975,7 +993,9 @@
final int targetVolume = originalVolume + direction;
Log.d(TAG, "originalVolume=" + originalVolume + ", targetVolume=" + targetVolume);
- mController.adjustVolume(direction, AudioManager.FLAG_SHOW_UI);
+ SessionResult result = mController.adjustVolume(direction, AudioManager.FLAG_SHOW_UI)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
new PollingCheck(VOLUME_CHANGE_TIMEOUT_MS) {
@Override
protected boolean check() {
@@ -995,7 +1015,7 @@
}
@Test
- public void testSendCustomCommand() throws InterruptedException {
+ public void testSendCustomCommand() throws Exception {
prepareLooper();
// TODO(jaewan): Need to revisit with the permission.
final String command = "test_custom_command";
@@ -1031,7 +1051,9 @@
mSession = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback).setId(TAG).build();
final MediaController controller = createController(mSession.getToken());
- controller.sendCustomCommand(testCommand, testArgs);
+ SessionResult result = controller.sendCustomCommand(testCommand, testArgs)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@@ -1084,7 +1106,7 @@
}
@Test
- public void testFastForward() throws InterruptedException {
+ public void testFastForward() throws Exception {
prepareLooper();
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@@ -1100,13 +1122,14 @@
.setSessionCallback(sHandlerExecutor, callback)
.setId("testFastForward").build()) {
MediaController controller = createController(session.getToken());
- controller.fastForward();
+ SessionResult result = controller.fastForward().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
- public void testRewind() throws InterruptedException {
+ public void testRewind() throws Exception {
prepareLooper();
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@@ -1121,13 +1144,14 @@
.setSessionCallback(sHandlerExecutor, callback)
.setId("testRewind").build()) {
MediaController controller = createController(session.getToken());
- controller.rewind();
+ SessionResult result = controller.rewind().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
- public void testPlayFromSearch() throws InterruptedException {
+ public void testPlayFromSearch() throws Exception {
prepareLooper();
final String request = "random query";
final Bundle bundle = new Bundle();
@@ -1149,13 +1173,15 @@
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPlayFromSearch").build()) {
MediaController controller = createController(session.getToken());
- controller.playFromSearch(request, bundle);
+ SessionResult result = controller.playFromSearch(request, bundle)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
- public void testPlayFromUri() throws InterruptedException {
+ public void testPlayFromUri() throws Exception {
prepareLooper();
final Uri request = Uri.parse("foo://boo");
final Bundle bundle = new Bundle();
@@ -1176,13 +1202,15 @@
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPlayFromUri").build()) {
MediaController controller = createController(session.getToken());
- controller.playFromUri(request, bundle);
+ SessionResult result = controller.playFromUri(request, bundle)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
- public void testPlayFromMediaId() throws InterruptedException {
+ public void testPlayFromMediaId() throws Exception {
prepareLooper();
final String request = "media_id";
final Bundle bundle = new Bundle();
@@ -1203,13 +1231,15 @@
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPlayFromMediaId").build()) {
MediaController controller = createController(session.getToken());
- controller.playFromMediaId(request, bundle);
+ SessionResult result = controller.playFromMediaId(request, bundle)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
- public void testPrepareFromSearch() throws InterruptedException {
+ public void testPrepareFromSearch() throws Exception {
prepareLooper();
final String request = "random query";
final Bundle bundle = new Bundle();
@@ -1230,13 +1260,15 @@
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPrepareFromSearch").build()) {
MediaController controller = createController(session.getToken());
- controller.prepareFromSearch(request, bundle);
+ SessionResult result = controller.prepareFromSearch(request, bundle)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
- public void testPrepareFromUri() throws InterruptedException {
+ public void testPrepareFromUri() throws Exception {
prepareLooper();
final Uri request = Uri.parse("foo://boo");
final Bundle bundle = new Bundle();
@@ -1257,13 +1289,15 @@
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPrepareFromUri").build()) {
MediaController controller = createController(session.getToken());
- controller.prepareFromUri(request, bundle);
+ SessionResult result = controller.prepareFromUri(request, bundle)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
- public void testPrepareFromMediaId() throws InterruptedException {
+ public void testPrepareFromMediaId() throws Exception {
prepareLooper();
final String request = "media_id";
final Bundle bundle = new Bundle();
@@ -1284,13 +1318,15 @@
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPrepareFromMediaId").build()) {
MediaController controller = createController(session.getToken());
- controller.prepareFromMediaId(request, bundle);
+ SessionResult result = controller.prepareFromMediaId(request, bundle)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
- public void testSetRating() throws InterruptedException {
+ public void testSetRating() throws Exception {
prepareLooper();
final float ratingValue = 3.5f;
final Rating rating = new StarRating(5, ratingValue);
@@ -1314,7 +1350,9 @@
.setSessionCallback(sHandlerExecutor, callback)
.setId("testSetRating").build()) {
MediaController controller = createController(session.getToken());
- controller.setRating(mediaId, rating);
+ SessionResult result = controller.setRating(mediaId, rating)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@@ -1367,6 +1405,7 @@
});
final MediaController controller = createController(mSession.getToken());
testHandler.post(new Runnable() {
+ @SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run() {
final int state = SessionPlayer.PLAYER_STATE_ERROR;
@@ -1423,18 +1462,18 @@
}
@Test
- public void testConnectToService_sessionService() throws InterruptedException {
+ public void testConnectToService_sessionService() throws Exception {
prepareLooper();
testConnectToService(MockMediaSessionService.ID);
}
@Test
- public void testConnectToService_libraryService() throws InterruptedException {
+ public void testConnectToService_libraryService() throws Exception {
prepareLooper();
testConnectToService(MockMediaLibraryService.ID);
}
- public void testConnectToService(String id) throws InterruptedException {
+ public void testConnectToService(String id) throws Exception {
prepareLooper();
final CountDownLatch latch = new CountDownLatch(1);
final MediaLibrarySessionCallback sessionCallback = new MediaLibrarySessionCallback() {
@@ -1474,7 +1513,8 @@
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Test command from controller to session service.
- mController.play();
+ SessionResult result = mController.play().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mPlayCalled);
@@ -1492,7 +1532,7 @@
@LargeTest
@Test
- public void testControllerAfterSessionIsClosed_sessionService() throws InterruptedException {
+ public void testControllerAfterSessionIsClosed_sessionService() throws Exception {
prepareLooper();
testConnectToService(MockMediaSessionService.ID);
testControllerAfterSessionIsClosed(MockMediaSessionService.ID);
@@ -1601,7 +1641,7 @@
}
};
MediaController controller = createController(mSession.getToken(), true, null, callback);
- mPlayer.setMediaItem(item);
+ mPlayer.mCurrentMediaItem = mPlayer.mItem = item;
mPlayer.notifyCurrentMediaItemChanged(item);
assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
item.setMetadata(TestUtils.createMetadata(item.getMediaId(), duration));
@@ -1638,8 +1678,9 @@
}
};
MediaController controller = createController(mSession.getToken(), true, null, callback);
- mPlayer.setPlaylist(list, null);
- mPlayer.skipToPlaylistItem(currentItemIdx);
+ mPlayer.mPlaylist = list;
+ mPlayer.mIndex = currentItemIdx;
+ mPlayer.mCurrentMediaItem = mPlayer.mItem = mPlayer.mPlaylist.get(currentItemIdx);
mPlayer.notifyPlaylistChanged();
assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
list.get(1).setMetadata(newMetadata);
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/MediaSessionTest.java b/media2/session/src/androidTest/java/androidx/media2/session/MediaSessionTest.java
index b41b628..c6a1640 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/MediaSessionTest.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/MediaSessionTest.java
@@ -159,7 +159,7 @@
final AudioAttributesCompat attrs = new AudioAttributesCompat.Builder()
.setContentType(CONTENT_TYPE_MUSIC)
.build();
- player.setAudioAttributes(attrs);
+ player.mAudioAttributes = attrs;
final int maxVolume = 100;
final int currentVolume = 23;
@@ -217,37 +217,37 @@
}
@Test
- public void testPlay() throws Exception {
+ public void testPlay() {
prepareLooper();
- mSession.getPlayer().play();
+ assertNotNull(mSession.getPlayer().play());
assertTrue(mPlayer.mPlayCalled);
}
@Test
- public void testPause() throws Exception {
+ public void testPause() {
prepareLooper();
- mSession.getPlayer().pause();
+ assertNotNull(mSession.getPlayer().pause());
assertTrue(mPlayer.mPauseCalled);
}
@Test
- public void testPrepare() throws Exception {
+ public void testPrepare() {
prepareLooper();
- mSession.getPlayer().prepare();
+ assertNotNull(mSession.getPlayer().prepare());
assertTrue(mPlayer.mPrepareCalled);
}
@Test
- public void testSeekTo() throws Exception {
+ public void testSeekTo() {
prepareLooper();
final long pos = 1004L;
- mSession.getPlayer().seekTo(pos);
+ assertNotNull(mSession.getPlayer().seekTo(pos));
assertTrue(mPlayer.mSeekToCalled);
assertEquals(pos, mPlayer.mSeekPosition);
}
@Test
- public void testGetDuration() throws Exception {
+ public void testGetDuration() {
prepareLooper();
final long testDuration = 9999;
mPlayer.mDuration = testDuration;
@@ -256,19 +256,19 @@
}
@Test
- public void testSetPlaybackSpeed() throws Exception {
+ public void testSetPlaybackSpeed() {
prepareLooper();
final float speed = 1.5f;
- mSession.getPlayer().setPlaybackSpeed(speed);
+ assertNotNull(mSession.getPlayer().setPlaybackSpeed(speed));
assertTrue(mPlayer.mSetPlaybackSpeedCalled);
assertEquals(speed, mPlayer.mPlaybackSpeed, 0.0f);
}
@Test
- public void testGetPlaybackSpeed() throws Exception {
+ public void testGetPlaybackSpeed() {
prepareLooper();
final float speed = 1.5f;
- mPlayer.setPlaybackSpeed(speed);
+ mPlayer.mPlaybackSpeed = speed;
mPlayer.mLastPlayerState = SessionPlayer.PLAYER_STATE_PLAYING;
assertEquals(speed, mSession.getPlayer().getPlaybackSpeed(), 0.0f);
}
@@ -284,24 +284,24 @@
@Test
public void testSkipToPreviousItem() {
prepareLooper();
- mSession.getPlayer().skipToPreviousPlaylistItem();
+ assertNotNull(mSession.getPlayer().skipToPreviousPlaylistItem());
assertTrue(mPlayer.mSkipToPreviousItemCalled);
}
@Test
- public void testSkipToNextItem() throws Exception {
+ public void testSkipToNextItem() {
prepareLooper();
- mSession.getPlayer().skipToNextPlaylistItem();
+ assertNotNull(mSession.getPlayer().skipToNextPlaylistItem());
assertTrue(mPlayer.mSkipToNextItemCalled);
}
@Test
- public void testSkipToPlaylistItem() throws Exception {
+ public void testSkipToPlaylistItem() {
prepareLooper();
final List<MediaItem> list = TestUtils.createMediaItems(2);
int targetIndex = 0;
- mSession.getPlayer().setPlaylist(list, null);
- mSession.getPlayer().skipToPlaylistItem(targetIndex);
+ assertNotNull(mSession.getPlayer().setPlaylist(list, null));
+ assertNotNull(mSession.getPlayer().skipToPlaylistItem(targetIndex));
assertTrue(mPlayer.mSkipToPlaylistItemCalled);
assertSame(targetIndex, mPlayer.mIndex);
}
@@ -344,7 +344,7 @@
public void testSetPlaylist() {
prepareLooper();
final List<MediaItem> list = TestUtils.createMediaItems(2);
- mSession.getPlayer().setPlaylist(list, null);
+ assertNotNull(mSession.getPlayer().setPlaylist(list, null));
assertTrue(mPlayer.mSetPlaylistCalled);
assertSame(list, mPlayer.mPlaylist);
assertNull(mPlayer.mMetadata);
@@ -362,7 +362,7 @@
public void testUpdatePlaylistMetadata() {
prepareLooper();
final MediaMetadata testMetadata = TestUtils.createMetadata();
- mSession.getPlayer().updatePlaylistMetadata(testMetadata);
+ assertNotNull(mSession.getPlayer().updatePlaylistMetadata(testMetadata));
assertTrue(mPlayer.mUpdatePlaylistMetadataCalled);
assertSame(testMetadata, mPlayer.mMetadata);
}
@@ -380,7 +380,7 @@
prepareLooper();
final int testIndex = 12;
final MediaItem testMediaItem = TestUtils.createMediaItemWithMetadata();
- mSession.getPlayer().addPlaylistItem(testIndex, testMediaItem);
+ assertNotNull(mSession.getPlayer().addPlaylistItem(testIndex, testMediaItem));
assertTrue(mPlayer.mAddPlaylistItemCalled);
assertEquals(testIndex, mPlayer.mIndex);
assertSame(testMediaItem, mPlayer.mItem);
@@ -391,18 +391,18 @@
prepareLooper();
final List<MediaItem> list = TestUtils.createMediaItems(2);
int targetIndex = 0;
- mSession.getPlayer().setPlaylist(list, null);
- mSession.getPlayer().removePlaylistItem(targetIndex);
+ assertNotNull(mSession.getPlayer().setPlaylist(list, null));
+ assertNotNull(mSession.getPlayer().removePlaylistItem(targetIndex));
assertTrue(mPlayer.mRemovePlaylistItemCalled);
assertSame(targetIndex, mPlayer.mIndex);
}
@Test
- public void testReplacePlaylistItem() throws InterruptedException {
+ public void testReplacePlaylistItem() {
prepareLooper();
final int testIndex = 12;
final MediaItem testMediaItem = TestUtils.createMediaItemWithMetadata();
- mSession.getPlayer().replacePlaylistItem(testIndex, testMediaItem);
+ assertNotNull(mSession.getPlayer().replacePlaylistItem(testIndex, testMediaItem));
assertTrue(mPlayer.mReplacePlaylistItemCalled);
assertEquals(testIndex, mPlayer.mIndex);
assertSame(testMediaItem, mPlayer.mItem);
@@ -412,7 +412,7 @@
public void testSetShuffleMode() {
prepareLooper();
final int testShuffleMode = SessionPlayer.SHUFFLE_MODE_GROUP;
- mSession.getPlayer().setShuffleMode(testShuffleMode);
+ assertNotNull(mSession.getPlayer().setShuffleMode(testShuffleMode));
assertTrue(mPlayer.mSetShuffleModeCalled);
assertEquals(testShuffleMode, mPlayer.mShuffleMode);
}
@@ -421,13 +421,13 @@
public void testSetRepeatMode() {
prepareLooper();
final int testRepeatMode = SessionPlayer.REPEAT_MODE_GROUP;
- mSession.getPlayer().setRepeatMode(testRepeatMode);
+ assertNotNull(mSession.getPlayer().setRepeatMode(testRepeatMode));
assertTrue(mPlayer.mSetRepeatModeCalled);
assertEquals(testRepeatMode, mPlayer.mRepeatMode);
}
@Test
- public void testOnCommandCallback() throws InterruptedException {
+ public void testOnCommandCallback() throws Exception {
prepareLooper();
final MockOnCommandCallback callback = new MockOnCommandCallback();
sHandler.postAndSync(new Runnable() {
@@ -440,14 +440,16 @@
}
});
MediaController controller = createController(mSession.getToken());
- controller.pause();
+ SessionResult pauseResult = controller.pause().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_ERROR_INVALID_STATE, pauseResult.getResultCode());
assertFalse(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertFalse(mPlayer.mPauseCalled);
assertEquals(1, callback.commands.size());
assertEquals(SessionCommand.COMMAND_CODE_PLAYER_PAUSE,
- (long) callback.commands.get(0).getCommandCode());
+ callback.commands.get(0).getCommandCode());
- controller.play();
+ SessionResult playResult = controller.play().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, playResult.getResultCode());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mPlayCalled);
assertFalse(mPlayer.mPauseCalled);
@@ -495,7 +497,7 @@
}
@Test
- public void testSetCustomLayout() throws InterruptedException {
+ public void testSetCustomLayout() throws Exception {
prepareLooper();
final List<CommandButton> customLayout = new ArrayList<>();
customLayout.add(new CommandButton.Builder()
@@ -534,7 +536,9 @@
}
};
MediaController controller = createController(session.getToken(), true, null, callback);
- session.setCustomLayout(mTestControllerInfo, customLayout);
+ SessionResult result = session.setCustomLayout(mTestControllerInfo, customLayout)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@@ -571,7 +575,7 @@
}
@Test
- public void testSendCustomCommand() throws InterruptedException {
+ public void testSendCustomCommand() throws Exception {
prepareLooper();
final SessionCommand testCommand = new SessionCommand("test_command_code", null);
final Bundle testArgs = new Bundle();
@@ -597,7 +601,9 @@
ControllerInfo controllerInfo = getTestControllerInfo();
assertNotNull(controllerInfo);
// TODO(jaewan): Test receivers as well.
- mSession.sendCustomCommand(controllerInfo, testCommand, testArgs);
+ SessionResult result = mSession.sendCustomCommand(controllerInfo, testCommand, testArgs)
+ .get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertEquals(RESULT_SUCCESS, result.getResultCode());
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@@ -615,7 +621,7 @@
@Nullable
public SessionCommandGroup onConnect(@NonNull MediaSession session,
@NonNull ControllerInfo controller) {
- session.sendCustomCommand(controller, testCommand, null);
+ assertNotNull(session.sendCustomCommand(controller, testCommand, null));
return super.onConnect(session, controller);
}
};
@@ -659,7 +665,7 @@
@Override
public void onPostConnect(@NonNull MediaSession session,
@NonNull ControllerInfo controller) {
- session.sendCustomCommand(controller, testCommand, null);
+ assertNotNull(session.sendCustomCommand(controller, testCommand, null));
}
};
final ControllerCallback testControllerCallback = new ControllerCallback() {
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/MockMediaBrowserServiceCompat.java b/media2/session/src/androidTest/java/androidx/media2/session/MockMediaBrowserServiceCompat.java
index 2f3fd4f..a33ac3a 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/MockMediaBrowserServiceCompat.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/MockMediaBrowserServiceCompat.java
@@ -22,13 +22,12 @@
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.MediaSessionCompat.Callback;
+import androidx.annotation.GuardedBy;
import androidx.media.MediaBrowserServiceCompat;
import java.lang.reflect.Method;
import java.util.List;
-import javax.annotation.concurrent.GuardedBy;
-
/**
* Mock implementation of the browser.
*/
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/MockMediaLibraryService.java b/media2/session/src/androidTest/java/androidx/media2/session/MockMediaLibraryService.java
index f6c5185..c52bd8c 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/MockMediaLibraryService.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/MockMediaLibraryService.java
@@ -25,6 +25,7 @@
import static androidx.media2.session.LibraryResult.RESULT_SUCCESS;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import android.content.ComponentName;
import android.content.Context;
@@ -32,6 +33,7 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.media2.common.MediaItem;
import androidx.media2.common.MediaMetadata;
@@ -46,8 +48,6 @@
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-import javax.annotation.concurrent.GuardedBy;
-
/**
* Mock implementation of {@link MediaLibraryService} for testing.
*/
@@ -221,13 +221,13 @@
params);
} else if (SEARCH_QUERY_TAKES_TIME.equals(query)) {
// Searching takes some time. Notify after 5 seconds.
- Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
+ assertNotNull(Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
@Override
public void run() {
mSession.notifySearchResultChanged(
controllerInfo, query, SEARCH_RESULT_COUNT, params);
}
- }, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS);
+ }, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS));
} else if (SEARCH_QUERY_EMPTY_RESULT.equals(query)) {
mSession.notifySearchResultChanged(controllerInfo, query, 0, params);
} else {
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/MockPlayer.java b/media2/session/src/androidTest/java/androidx/media2/session/MockPlayer.java
index 4d935f2..12a672c 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/MockPlayer.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/MockPlayer.java
@@ -87,7 +87,7 @@
public boolean mSetRepeatModeCalled;
public boolean mSetShuffleModeCalled;
- private AudioAttributesCompat mAudioAttributes;
+ AudioAttributesCompat mAudioAttributes;
public MockPlayer(int count) {
this(count, false);
diff --git a/media2/session/src/main/java/androidx/media2/session/SessionTokenImplBase.java b/media2/session/src/main/java/androidx/media2/session/SessionTokenImplBase.java
index b0296b7..1db4c73 100644
--- a/media2/session/src/main/java/androidx/media2/session/SessionTokenImplBase.java
+++ b/media2/session/src/main/java/androidx/media2/session/SessionTokenImplBase.java
@@ -156,7 +156,7 @@
}
@Override
- @NonNull
+ @Nullable
public Bundle getExtras() {
return mExtras;
}
diff --git a/palette/palette/build.gradle b/palette/palette/build.gradle
index 87eb258b..1a785db 100644
--- a/palette/palette/build.gradle
+++ b/palette/palette/build.gradle
@@ -9,7 +9,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.1.0")
annotationProcessor(NULLAWAY)
diff --git a/percentlayout/percentlayout/build.gradle b/percentlayout/percentlayout/build.gradle
index e5c8470..5b2ac05 100644
--- a/percentlayout/percentlayout/build.gradle
+++ b/percentlayout/percentlayout/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api(ANDROIDX_ANNOTATION)
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/preference/build.gradle b/preference/build.gradle
index 8235c7a..43b0f07 100644
--- a/preference/build.gradle
+++ b/preference/build.gradle
@@ -30,7 +30,7 @@
implementation("androidx.annotation:annotation:1.1.0")
api("androidx.appcompat:appcompat:1.1.0-rc01")
// TODO: change to alpha05 after release
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.0.0")
api("androidx.fragment:fragment:1.1.0-rc01")
api("androidx.recyclerview:recyclerview:1.0.0")
diff --git a/recyclerview/recyclerview-selection/build.gradle b/recyclerview/recyclerview-selection/build.gradle
index a364be0..ac2f787 100644
--- a/recyclerview/recyclerview-selection/build.gradle
+++ b/recyclerview/recyclerview-selection/build.gradle
@@ -27,7 +27,7 @@
dependencies {
api(project(":recyclerview:recyclerview"))
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index 4ec3faf..16d7734 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -11,7 +11,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc02")
implementation("androidx.collection:collection:1.0.0")
api("androidx.customview:customview:1.0.0")
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index c14e5a8..0d1f6d9 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -5140,11 +5140,13 @@
void dispatchOnScrolled(int hresult, int vresult) {
mDispatchScrollCounter++;
- // Pass the current scrollX/scrollY values; no actual change in these properties occurred
- // but some general-purpose code may choose to respond to changes this way.
+ // Pass the current scrollX/scrollY values as current values. No actual change in these
+ // properties occurred. Pass negative hresult and vresult as old values so that
+ // postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt) in onScrollChanged
+ // sends the scrolled accessibility event correctly.
final int scrollX = getScrollX();
final int scrollY = getScrollY();
- onScrollChanged(scrollX, scrollY, scrollX, scrollY);
+ onScrollChanged(scrollX, scrollY, scrollX - hresult, scrollY - vresult);
// Pass the real deltas to onScrolled, the RecyclerView-specific method.
onScrolled(hresult, vresult);
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt
index 07d1f2c..5d04025 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt
@@ -32,8 +32,8 @@
val containing: DeclaredType,
val element: Element,
val bindingScope: BindingScope,
- // pass only if this is processed as a child of Embedded field
- val fieldParent: EmbeddedField?
+ val fieldParent: EmbeddedField?, // pass only if this is processed as a child of Embedded field
+ val onBindingError: (field: Field, errorMsg: String) -> Unit
) {
val context = baseContext.fork(element)
fun process(): Field {
@@ -82,20 +82,23 @@
field.statementBinder = adapter
field.cursorValueReader = adapter
field.affinity = adapterAffinity
- context.checker.check(adapter != null, field.element,
- ProcessorErrors.CANNOT_FIND_COLUMN_TYPE_ADAPTER)
+ if (adapter == null) {
+ onBindingError(field, ProcessorErrors.CANNOT_FIND_COLUMN_TYPE_ADAPTER)
+ }
}
BindingScope.BIND_TO_STMT -> {
field.statementBinder = context.typeAdapterStore
.findStatementValueBinder(field.type, field.affinity)
- context.checker.check(field.statementBinder != null, field.element,
- ProcessorErrors.CANNOT_FIND_STMT_BINDER)
+ if (field.statementBinder == null) {
+ onBindingError(field, ProcessorErrors.CANNOT_FIND_STMT_BINDER)
+ }
}
BindingScope.READ_FROM_CURSOR -> {
field.cursorValueReader = context.typeAdapterStore
.findCursorValueReader(field.type, field.affinity)
- context.checker.check(field.cursorValueReader != null, field.element,
- ProcessorErrors.CANNOT_FIND_CURSOR_READER)
+ if (field.cursorValueReader == null) {
+ onBindingError(field, ProcessorErrors.CANNOT_FIND_CURSOR_READER)
+ }
}
}
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/FtsTableEntityProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/FtsTableEntityProcessor.kt
index 40869db..1059368 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/FtsTableEntityProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/FtsTableEntityProcessor.kt
@@ -61,17 +61,14 @@
ProcessorErrors.ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY)
val entityAnnotation = element.toAnnotationBox(androidx.room.Entity::class)
val tableName: String
- val ignoredColumns: Set<String>
if (entityAnnotation != null) {
tableName = extractTableName(element, entityAnnotation.value)
- ignoredColumns = entityAnnotation.value.ignoredColumns.toSet()
context.checker.check(extractIndices(entityAnnotation, tableName).isEmpty(),
element, ProcessorErrors.INDICES_IN_FTS_ENTITY)
context.checker.check(extractForeignKeys(entityAnnotation).isEmpty(),
element, ProcessorErrors.FOREIGN_KEYS_IN_FTS_ENTITY)
} else {
tableName = element.simpleName.toString()
- ignoredColumns = emptySet()
}
val pojo = PojoProcessor.createFor(
@@ -79,8 +76,7 @@
element = element,
bindingScope = FieldProcessor.BindingScope.TWO_WAY,
parent = null,
- referenceStack = referenceStack,
- ignoredColumns = ignoredColumns).process()
+ referenceStack = referenceStack).process()
context.checker.check(pojo.relations.isEmpty(), element, ProcessorErrors.RELATION_IN_ENTITY)
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
index efebfac..235a457 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
@@ -79,7 +79,6 @@
val bindingScope: FieldProcessor.BindingScope,
val parent: EmbeddedField?,
val referenceStack: LinkedHashSet<Name> = LinkedHashSet(),
- val ignoredColumns: Set<String>,
private val delegate: Delegate
) {
val context = baseContext.fork(element)
@@ -97,8 +96,7 @@
element: TypeElement,
bindingScope: FieldProcessor.BindingScope,
parent: EmbeddedField?,
- referenceStack: LinkedHashSet<Name> = LinkedHashSet(),
- ignoredColumns: Set<String> = emptySet()
+ referenceStack: LinkedHashSet<Name> = LinkedHashSet()
): PojoProcessor {
val (pojoElement, delegate) = if (element.hasAnnotation(AutoValue::class)) {
val elementUtils = context.processingEnv.elementUtils
@@ -117,7 +115,6 @@
bindingScope = bindingScope,
parent = parent,
referenceStack = referenceStack,
- ignoredColumns = ignoredColumns,
delegate = delegate)
}
}
@@ -161,6 +158,10 @@
}
}
+ val ignoredColumns =
+ element.toAnnotationBox(androidx.room.Entity::class)?.value?.ignoredColumns?.toSet()
+ ?: emptySet()
+ val fieldBindingErrors = mutableMapOf<Field, String>()
val unfilteredMyFields = allFields[null]
?.map {
FieldProcessor(
@@ -168,10 +169,17 @@
containing = declaredType,
element = it,
bindingScope = bindingScope,
- fieldParent = parent).process()
+ fieldParent = parent,
+ onBindingError = { field, errorMsg ->
+ fieldBindingErrors[field] = errorMsg
+ }).process()
} ?: emptyList()
val myFields = unfilteredMyFields.filterNot { ignoredColumns.contains(it.columnName) }
-
+ myFields.forEach { field ->
+ fieldBindingErrors[field]?.let {
+ context.logger.e(field.element, it)
+ }
+ }
val unfilteredEmbeddedFields =
allFields[Embedded::class]
?.mapNotNull {
@@ -707,7 +715,9 @@
context.logger.e(field.element,
ProcessorErrors.tooManyMatchingGetters(field, matching))
})
- context.checker.check(success, field.element, CANNOT_FIND_GETTER_FOR_FIELD)
+ context.checker.check(
+ success || bindingScope == FieldProcessor.BindingScope.READ_FROM_CURSOR,
+ field.element, CANNOT_FIND_GETTER_FOR_FIELD)
}
private fun assignSetters(
@@ -756,7 +766,9 @@
context.logger.e(field.element,
ProcessorErrors.tooManyMatchingSetter(field, matching))
})
- context.checker.check(success, field.element, CANNOT_FIND_SETTER_FOR_FIELD)
+ context.checker.check(
+ success || bindingScope == FieldProcessor.BindingScope.BIND_TO_STMT,
+ field.element, CANNOT_FIND_SETTER_FOR_FIELD)
}
/**
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt
index a0b0c29..f570457 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt
@@ -65,19 +65,16 @@
val entityIndices: List<IndexInput>
val foreignKeyInputs: List<ForeignKeyInput>
val inheritSuperIndices: Boolean
- val ignoredColumns: Set<String>
if (annotationBox != null) {
tableName = extractTableName(element, annotationBox.value)
entityIndices = extractIndices(annotationBox, tableName)
inheritSuperIndices = annotationBox.value.inheritSuperIndices
foreignKeyInputs = extractForeignKeys(annotationBox)
- ignoredColumns = annotationBox.value.ignoredColumns.toSet()
} else {
tableName = element.simpleName.toString()
foreignKeyInputs = emptyList()
entityIndices = emptyList()
inheritSuperIndices = false
- ignoredColumns = emptySet()
}
context.checker.notBlank(tableName, element,
ProcessorErrors.ENTITY_TABLE_NAME_CANNOT_BE_EMPTY)
@@ -85,12 +82,12 @@
ProcessorErrors.ENTITY_TABLE_NAME_CANNOT_START_WITH_SQLITE)
val pojo = PojoProcessor.createFor(
- context = context,
- element = element,
- bindingScope = FieldProcessor.BindingScope.TWO_WAY,
- parent = null,
- referenceStack = referenceStack,
- ignoredColumns = ignoredColumns).process()
+ context = context,
+ element = element,
+ bindingScope = FieldProcessor.BindingScope.TWO_WAY,
+ parent = null,
+ referenceStack = referenceStack
+ ).process()
context.checker.check(pojo.relations.isEmpty(), element, RELATION_IN_ENTITY)
val fieldIndices = pojo.fields
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt
index f33bef3..887fec8 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt
@@ -46,7 +46,8 @@
roomSQLiteQueryVar: String,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val transactionWrapper = if (inTransaction) {
builder.transactionWrapper(dbField)
@@ -58,13 +59,16 @@
val cursorVar = scope.getTmpVar("_cursor")
transactionWrapper?.beginTransactionWithControlFlow()
builder.apply {
- addStatement("final $T $L = $T.query($N, $L, $L)",
- AndroidTypeNames.CURSOR,
- cursorVar,
- RoomTypeNames.DB_UTIL,
- dbField,
- roomSQLiteQueryVar,
- if (shouldCopyCursor) "true" else "false")
+ addStatement(
+ "final $T $L = $T.query($N, $L, $L, $L)",
+ AndroidTypeNames.CURSOR,
+ cursorVar,
+ RoomTypeNames.DB_UTIL,
+ dbField,
+ roomSQLiteQueryVar,
+ if (shouldCopyCursor) "true" else "false",
+ cancellationSignalVar
+ )
beginControlFlow("try").apply {
val adapterScope = scope.fork()
adapter?.convert(outVar, cursorVar, adapterScope)
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
index bc2fb8e..127d925 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
@@ -44,7 +44,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val callableImpl = CallableTypeSpecBuilder(typeArg.typeName()) {
createRunQueryAndReturnStatements(
@@ -53,7 +54,8 @@
canReleaseQuery = canReleaseQuery,
dbField = dbField,
inTransaction = inTransaction,
- scope = scope)
+ scope = scope,
+ cancellationSignalVar = cancellationSignalVar)
}.build()
scope.builder().apply {
@@ -75,7 +77,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val transactionWrapper = if (inTransaction) {
builder.transactionWrapper(dbField)
@@ -87,13 +90,14 @@
val cursorVar = scope.getTmpVar("_cursor")
transactionWrapper?.beginTransactionWithControlFlow()
builder.apply {
- addStatement("final $T $L = $T.query($N, $L, $L)",
+ addStatement("final $T $L = $T.query($N, $L, $L, $L)",
AndroidTypeNames.CURSOR,
cursorVar,
RoomTypeNames.DB_UTIL,
dbField,
roomSQLiteQueryVar,
- if (shouldCopyCursor) "true" else "false")
+ if (shouldCopyCursor) "true" else "false",
+ cancellationSignalVar)
beginControlFlow("try").apply {
val adapterScope = scope.fork()
adapter?.convert(outVar, cursorVar, adapterScope)
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
index d97ba61..906db3b 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
@@ -43,7 +43,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val callableImpl = CallableTypeSpecBuilder(typeArg.typeName()) {
createRunQueryAndReturnStatements(
@@ -52,7 +53,8 @@
canReleaseQuery = canReleaseQuery,
dbField = dbField,
inTransaction = inTransaction,
- scope = scope)
+ scope = scope,
+ cancellationSignalVar = cancellationSignalVar)
}.build()
scope.builder().apply {
@@ -72,7 +74,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val transactionWrapper = if (inTransaction) {
builder.transactionWrapper(dbField)
@@ -84,13 +87,16 @@
val cursorVar = scope.getTmpVar("_cursor")
transactionWrapper?.beginTransactionWithControlFlow()
builder.apply {
- addStatement("final $T $L = $T.query($N, $L, $L)",
+ addStatement(
+ "final $T $L = $T.query($N, $L, $L, $L)",
AndroidTypeNames.CURSOR,
cursorVar,
RoomTypeNames.DB_UTIL,
dbField,
roomSQLiteQueryVar,
- if (shouldCopyCursor) "true" else "false")
+ if (shouldCopyCursor) "true" else "false",
+ cancellationSignalVar
+ )
beginControlFlow("try").apply {
val adapterScope = scope.fork()
adapter?.convert(outVar, cursorVar, adapterScope)
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
index 9f8c9e7..9e9eb9c 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
@@ -29,7 +29,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val builder = scope.builder()
val transactionWrapper = if (inTransaction) {
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
index 9bf9860..2609a29 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
@@ -37,7 +37,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
scope.builder().apply {
val pagedListProvider = TypeSpec
@@ -48,7 +49,8 @@
roomSQLiteQueryVar = roomSQLiteQueryVar,
dbField = dbField,
inTransaction = inTransaction,
- scope = scope))
+ scope = scope,
+ cancellationSignalVar = cancellationSignalVar))
}.build()
addStatement("return $L", pagedListProvider)
}
@@ -58,7 +60,8 @@
roomSQLiteQueryVar: String,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
): MethodSpec = MethodSpec.methodBuilder("create").apply {
addAnnotation(Override::class.java)
addModifiers(Modifier.PUBLIC)
@@ -69,7 +72,8 @@
canReleaseQuery = true,
dbField = dbField,
inTransaction = inTransaction,
- scope = countedBinderScope)
+ scope = countedBinderScope,
+ cancellationSignalVar = cancellationSignalVar)
addCode(countedBinderScope.builder().build())
}.build()
}
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt
index 722242e..b3d8c2c 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt
@@ -41,7 +41,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
// Callable<T> // Note that this callable does not release the query object.
val callableImpl = CallableTypeSpecBuilder(typeArg.typeName()) {
@@ -50,7 +51,8 @@
roomSQLiteQueryVar = roomSQLiteQueryVar,
dbField = dbField,
inTransaction = inTransaction,
- scope = scope
+ scope = scope,
+ cancellationSignalVar = cancellationSignalVar
)
}.build()
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
index 156117c..48e322c 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
@@ -33,7 +33,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
scope.builder().apply {
addStatement("$N.assertNotSuspendingTransaction()", DaoWriter.dbField)
@@ -48,13 +49,16 @@
val shouldCopyCursor = adapter?.shouldCopyCursor() == true
val outVar = scope.getTmpVar("_result")
val cursorVar = scope.getTmpVar("_cursor")
- addStatement("final $T $L = $T.query($N, $L, $L)",
- AndroidTypeNames.CURSOR,
- cursorVar,
- RoomTypeNames.DB_UTIL,
- dbField,
- roomSQLiteQueryVar,
- if (shouldCopyCursor) "true" else "false")
+ addStatement(
+ "final $T $L = $T.query($N, $L, $L, $L)",
+ AndroidTypeNames.CURSOR,
+ cursorVar,
+ RoomTypeNames.DB_UTIL,
+ dbField,
+ roomSQLiteQueryVar,
+ if (shouldCopyCursor) "true" else "false",
+ cancellationSignalVar
+ )
beginControlFlow("try").apply {
adapter?.convert(outVar, cursorVar, scope)
transactionWrapper?.commitTransaction()
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt
index cd84015..e4e77d2 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt
@@ -48,7 +48,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val callableImpl = CallableTypeSpecBuilder(typeArg.typeName()) {
createRunQueryAndReturnStatements(
@@ -56,7 +57,8 @@
roomSQLiteQueryVar = roomSQLiteQueryVar,
inTransaction = inTransaction,
dbField = dbField,
- scope = scope
+ scope = scope,
+ cancellationSignalVar = "null" // LiveData can't be cancelled
)
}.apply {
if (canReleaseQuery) {
@@ -83,7 +85,8 @@
observerField: FieldSpec,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignal: String
): MethodSpec {
return MethodSpec.methodBuilder("compute").apply {
addAnnotation(Override::class.java)
@@ -104,7 +107,8 @@
roomSQLiteQueryVar = roomSQLiteQueryVar,
dbField = dbField,
inTransaction = inTransaction,
- scope = scope
+ scope = scope,
+ cancellationSignalVar = cancellationSignal
)
}.build()
}
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
index 8ddf99f..1efee13 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
@@ -43,7 +43,8 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
// first comma for table names comes from the string since it might be empty in which case
// we don't need a comma. If list is empty, this prevents generating bad code (it is still
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
index d3d6d8c..faa3854 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
@@ -36,6 +36,7 @@
canReleaseQuery: Boolean, // false if query is provided by the user
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String // "null" if cancellation isn't supported
)
}
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
index 71a4a2c..fc454fe 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
@@ -46,14 +46,16 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val callable = CallableTypeSpecBuilder(typeArg.typeName()) {
fillInCallMethod(
roomSQLiteQueryVar = roomSQLiteQueryVar,
dbField = dbField,
inTransaction = inTransaction,
- scope = scope)
+ scope = scope,
+ cancellationSignalVar = cancellationSignalVar)
}.apply {
if (canReleaseQuery) {
addMethod(createFinalizeMethod(roomSQLiteQueryVar))
@@ -72,7 +74,8 @@
roomSQLiteQueryVar: String,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val adapterScope = scope.fork()
val transactionWrapper = if (inTransaction) {
@@ -84,13 +87,14 @@
val shouldCopyCursor = adapter?.shouldCopyCursor() == true
val outVar = scope.getTmpVar("_result")
val cursorVar = scope.getTmpVar("_cursor")
- addStatement("final $T $L = $T.query($N, $L, $L)",
+ addStatement("final $T $L = $T.query($N, $L, $L, $L)",
AndroidTypeNames.CURSOR,
cursorVar,
RoomTypeNames.DB_UTIL,
dbField,
roomSQLiteQueryVar,
- if (shouldCopyCursor) "true" else "false")
+ if (shouldCopyCursor) "true" else "false",
+ cancellationSignalVar)
beginControlFlow("try").apply {
adapter?.convert(outVar, cursorVar, adapterScope)
addCode(adapterScope.generate())
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt
index d52e804..f042089 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt
@@ -43,14 +43,16 @@
canReleaseQuery: Boolean,
dbField: FieldSpec,
inTransaction: Boolean,
- scope: CodeGenScope
+ scope: CodeGenScope,
+ cancellationSignalVar: String
) {
val callableImpl = CallableTypeSpecBuilder(typeArg.typeName()) {
createRunQueryAndReturnStatements(builder = this,
roomSQLiteQueryVar = roomSQLiteQueryVar,
inTransaction = inTransaction,
dbField = dbField,
- scope = scope)
+ scope = scope,
+ cancellationSignalVar = cancellationSignalVar)
}.apply {
if (canReleaseQuery) {
addMethod(createFinalizeMethod(roomSQLiteQueryVar))
diff --git a/room/compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt b/room/compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
index 5d9dc90..d3a2106 100644
--- a/room/compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
@@ -273,7 +273,8 @@
canReleaseQuery = shouldReleaseQuery,
dbField = dbField,
inTransaction = method.inTransaction,
- scope = scope)
+ scope = scope,
+ cancellationSignalVar = "null")
}
addCode(scope.builder().build())
}.build()
@@ -424,7 +425,8 @@
canReleaseQuery = true,
dbField = dbField,
inTransaction = method.inTransaction,
- scope = scope)
+ scope = scope,
+ cancellationSignalVar = "null")
return scope.builder().build()
}
diff --git a/room/compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt b/room/compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt
index ae44b51..b2d3410 100644
--- a/room/compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt
@@ -133,13 +133,14 @@
val shouldCopyCursor = collector.rowAdapter.let {
it is PojoRowAdapter && it.relationCollectors.isNotEmpty()
}
- addStatement("final $T $L = $T.query($N, $L, $L)",
+ addStatement("final $T $L = $T.query($N, $L, $L, $L)",
AndroidTypeNames.CURSOR,
cursorVar,
RoomTypeNames.DB_UTIL,
DaoWriter.dbField,
stmtVar,
- if (shouldCopyCursor) "true" else "false")
+ if (shouldCopyCursor) "true" else "false",
+ "null")
beginControlFlow("try").apply {
if (relation.junction != null) {
diff --git a/room/compiler/src/test/data/daoWriter/output/ComplexDao.java b/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
index b3399a9..72e3734 100644
--- a/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
+++ b/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
@@ -47,7 +47,7 @@
int _argIndex = 1;
_statement.bindLong(_argIndex, id);
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfFullName = CursorUtil.getColumnIndexOrThrow(_cursor, "fullName");
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
@@ -73,7 +73,7 @@
int _argIndex = 1;
_statement.bindLong(_argIndex, id);
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
@@ -115,7 +115,7 @@
_statement.bindString(_argIndex, lastName);
}
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
@@ -158,7 +158,7 @@
_argIndex ++;
}
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
@@ -190,7 +190,7 @@
int _argIndex = 1;
_statement.bindLong(_argIndex, id);
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _result;
if(_cursor.moveToFirst()) {
@@ -221,7 +221,7 @@
_argIndex ++;
}
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int[] _result = new int[_cursor.getCount()];
int _index = 0;
@@ -258,7 +258,7 @@
_argIndex ++;
}
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final List<Integer> _result = new ArrayList<Integer>(_cursor.getCount());
while(_cursor.moveToNext()) {
@@ -286,7 +286,7 @@
return __db.getInvalidationTracker().createLiveData(new String[]{"user"}, false, new Callable<User>() {
@Override
public User call() throws Exception {
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
@@ -337,7 +337,7 @@
return __db.getInvalidationTracker().createLiveData(new String[]{"user"}, false, new Callable<List<User>>() {
@Override
public List<User> call() throws Exception {
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
@@ -405,7 +405,7 @@
_argIndex ++;
}
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final List<Integer> _result = new ArrayList<Integer>(_cursor.getCount());
while(_cursor.moveToNext()) {
@@ -429,7 +429,7 @@
final String _sql = "SELECT * FROM Child1";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
@@ -465,7 +465,7 @@
final String _sql = "SELECT * FROM Child2";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
__db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false);
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
diff --git a/room/compiler/src/test/kotlin/androidx/room/processor/FieldProcessorTest.kt b/room/compiler/src/test/kotlin/androidx/room/processor/FieldProcessorTest.kt
index a685e40..71d2a72f 100644
--- a/room/compiler/src/test/kotlin/androidx/room/processor/FieldProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/processor/FieldProcessorTest.kt
@@ -435,7 +435,7 @@
.processedWith(TestProcessor.builder()
.forAnnotations(androidx.room.Entity::class)
.nextRunHandler { invocation ->
- val (owner, field) = invocation.roundEnv
+ val (owner, fieldElement) = invocation.roundEnv
.getElementsAnnotatedWith(Entity::class.java)
.map {
Pair(it, invocation.processingEnv.elementUtils
@@ -451,9 +451,12 @@
val parser = FieldProcessor(
baseContext = entityContext,
containing = MoreTypes.asDeclared(owner.asType()),
- element = field!!,
+ element = fieldElement!!,
bindingScope = FieldProcessor.BindingScope.TWO_WAY,
- fieldParent = null)
+ fieldParent = null,
+ onBindingError = { field, errorMsg ->
+ invocation.context.logger.e(field.element, errorMsg) }
+ )
handler(parser.process(), invocation)
true
}
diff --git a/room/compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt b/room/compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
index 4dd648a..e51aad4 100644
--- a/room/compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
@@ -1364,6 +1364,8 @@
"""
package foo.bar;
import androidx.room.*;
+
+ @Entity(ignoredColumns = {"bar"})
public class ${MY_POJO.simpleName()} {
public String foo;
public String bar;
@@ -1372,7 +1374,6 @@
val pojo = PojoProcessor.createFor(context = invocation.context,
element = invocation.typeElement(MY_POJO.toString()),
bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
- ignoredColumns = setOf("bar"),
parent = null).process()
assertThat(pojo.fields.find { it.name == "foo" }, notNullValue())
assertThat(pojo.fields.find { it.name == "bar" }, nullValue())
@@ -1385,6 +1386,8 @@
"""
package foo.bar;
import androidx.room.*;
+
+ @Entity(ignoredColumns = {"bar"})
public class ${MY_POJO.simpleName()} {
private final String foo;
private final String bar;
@@ -1402,8 +1405,7 @@
val pojo = PojoProcessor.createFor(context = invocation.context,
element = invocation.typeElement(MY_POJO.toString()),
bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
- ignoredColumns = setOf("bar"),
- parent = null).process()
+ parent = null).process()
assertThat(pojo.fields.find { it.name == "foo" }, notNullValue())
assertThat(pojo.fields.find { it.name == "bar" }, nullValue())
}.compilesWithoutError()
@@ -1415,6 +1417,8 @@
"""
package foo.bar;
import androidx.room.*;
+
+ @Entity(ignoredColumns = {"bar"})
public class ${MY_POJO.simpleName()} {
private String foo;
private String bar;
@@ -1431,7 +1435,6 @@
val pojo = PojoProcessor.createFor(context = invocation.context,
element = invocation.typeElement(MY_POJO.toString()),
bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
- ignoredColumns = setOf("bar"),
parent = null).process()
assertThat(pojo.fields.find { it.name == "foo" }, notNullValue())
assertThat(pojo.fields.find { it.name == "bar" }, nullValue())
@@ -1444,6 +1447,8 @@
"""
package foo.bar;
import androidx.room.*;
+
+ @Entity(ignoredColumns = {"my_bar"})
public class ${MY_POJO.simpleName()} {
public String foo;
@ColumnInfo(name = "my_bar")
@@ -1453,7 +1458,6 @@
val pojo = PojoProcessor.createFor(context = invocation.context,
element = invocation.typeElement(MY_POJO.toString()),
bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
- ignoredColumns = setOf("my_bar"),
parent = null).process()
assertThat(pojo.fields.find { it.name == "foo" }, notNullValue())
assertThat(pojo.fields.find { it.name == "bar" }, nullValue())
@@ -1466,6 +1470,8 @@
"""
package foo.bar;
import androidx.room.*;
+
+ @Entity(ignoredColumns = {"no_such_column"})
public class ${MY_POJO.simpleName()} {
public String foo;
public String bar;
@@ -1474,7 +1480,6 @@
val pojo = PojoProcessor.createFor(context = invocation.context,
element = invocation.typeElement(MY_POJO.toString()),
bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
- ignoredColumns = setOf("no_such_column"),
parent = null).process()
assertThat(pojo.fields.find { it.name == "foo" }, notNullValue())
assertThat(pojo.fields.find { it.name == "bar" }, notNullValue())
@@ -1482,6 +1487,132 @@
ProcessorErrors.missingIgnoredColumns(listOf("no_such_column")))
}
+ @Test
+ fun noSetter_scopeBindStmt() {
+ simpleRun(
+ """
+ package foo.bar;
+ import androidx.room.*;
+ public class ${MY_POJO.simpleName()} {
+ private String foo;
+ private String bar;
+
+ public String getFoo() { return foo; }
+ public String getBar() { return bar; }
+ }
+ """.toJFO(MY_POJO.toString())) { invocation ->
+ PojoProcessor.createFor(context = invocation.context,
+ element = invocation.typeElement(MY_POJO.toString()),
+ bindingScope = FieldProcessor.BindingScope.BIND_TO_STMT,
+ parent = null).process()
+ }.compilesWithoutError()
+ }
+
+ @Test
+ fun noSetter_scopeTwoWay() {
+ simpleRun(
+ """
+ package foo.bar;
+ import androidx.room.*;
+ public class ${MY_POJO.simpleName()} {
+ private String foo;
+ private String bar;
+
+ public String getFoo() { return foo; }
+ public String getBar() { return bar; }
+ }
+ """.toJFO(MY_POJO.toString())) { invocation ->
+ PojoProcessor.createFor(context = invocation.context,
+ element = invocation.typeElement(MY_POJO.toString()),
+ bindingScope = FieldProcessor.BindingScope.TWO_WAY,
+ parent = null).process()
+ }.failsToCompile().withErrorContaining("Cannot find setter for field.")
+ }
+
+ @Test
+ fun noSetter_scopeReadFromCursor() {
+ simpleRun(
+ """
+ package foo.bar;
+ import androidx.room.*;
+ public class ${MY_POJO.simpleName()} {
+ private String foo;
+ private String bar;
+
+ public String getFoo() { return foo; }
+ public String getBar() { return bar; }
+ }
+ """.toJFO(MY_POJO.toString())) { invocation ->
+ PojoProcessor.createFor(context = invocation.context,
+ element = invocation.typeElement(MY_POJO.toString()),
+ bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+ parent = null).process()
+ }.failsToCompile().withErrorContaining("Cannot find setter for field.")
+ }
+
+ @Test
+ fun noGetter_scopeBindStmt() {
+ simpleRun(
+ """
+ package foo.bar;
+ import androidx.room.*;
+ public class ${MY_POJO.simpleName()} {
+ private String foo;
+ private String bar;
+
+ public void setFoo(String foo) { this.foo = foo; }
+ public void setBar(String bar) { this.bar = bar; }
+ }
+ """.toJFO(MY_POJO.toString())) { invocation ->
+ PojoProcessor.createFor(context = invocation.context,
+ element = invocation.typeElement(MY_POJO.toString()),
+ bindingScope = FieldProcessor.BindingScope.BIND_TO_STMT,
+ parent = null).process()
+ }.failsToCompile().withErrorContaining("Cannot find getter for field.")
+ }
+
+ @Test
+ fun noGetter_scopeTwoWay() {
+ simpleRun(
+ """
+ package foo.bar;
+ import androidx.room.*;
+ public class ${MY_POJO.simpleName()} {
+ private String foo;
+ private String bar;
+
+ public void setFoo(String foo) { this.foo = foo; }
+ public void setBar(String bar) { this.bar = bar; }
+ }
+ """.toJFO(MY_POJO.toString())) { invocation ->
+ PojoProcessor.createFor(context = invocation.context,
+ element = invocation.typeElement(MY_POJO.toString()),
+ bindingScope = FieldProcessor.BindingScope.TWO_WAY,
+ parent = null).process()
+ }.failsToCompile().withErrorContaining("Cannot find getter for field.")
+ }
+
+ @Test
+ fun noGetter_scopeReadCursor() {
+ simpleRun(
+ """
+ package foo.bar;
+ import androidx.room.*;
+ public class ${MY_POJO.simpleName()} {
+ private String foo;
+ private String bar;
+
+ public void setFoo(String foo) { this.foo = foo; }
+ public void setBar(String bar) { this.bar = bar; }
+ }
+ """.toJFO(MY_POJO.toString())) { invocation ->
+ PojoProcessor.createFor(context = invocation.context,
+ element = invocation.typeElement(MY_POJO.toString()),
+ bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+ parent = null).process()
+ }.compilesWithoutError()
+ }
+
private fun singleRun(
code: String,
vararg jfos: JavaFileObject,
@@ -1523,7 +1654,7 @@
handler.invoke(
PojoProcessor.createFor(context = invocation.context,
element = invocation.typeElement(MY_POJO.toString()),
- bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+ bindingScope = FieldProcessor.BindingScope.TWO_WAY,
parent = null).process(),
invocation
)
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
index 6cadb2f..8dae07b0 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
@@ -33,6 +33,7 @@
import androidx.room.integration.kotlintestapp.vo.BookWithPublisher
import androidx.room.integration.kotlintestapp.vo.DateConverter
import androidx.room.integration.kotlintestapp.vo.Lang
+import androidx.room.integration.kotlintestapp.vo.MiniBook
import androidx.room.integration.kotlintestapp.vo.Publisher
import androidx.room.integration.kotlintestapp.vo.PublisherWithBookSales
import androidx.room.integration.kotlintestapp.vo.PublisherWithBooks
@@ -116,6 +117,9 @@
@Insert
fun addBooks(vararg books: Book)
+ @Insert(entity = Book::class)
+ fun addMiniBook(miniBook: MiniBook)
+
@Insert
fun addBookAuthors(vararg bookAuthors: BookAuthor)
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Book.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Book.kt
index 6d7b7a7..21292d5 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Book.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Book.kt
@@ -16,6 +16,7 @@
package androidx.room.integration.kotlintestapp.vo
+import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@@ -30,7 +31,9 @@
@PrimaryKey val bookId: String,
val title: String,
val bookPublisherId: String,
+ @ColumnInfo(defaultValue = "0")
@field:TypeConverters(Lang::class)
val languages: Set<Lang>,
+ @ColumnInfo(defaultValue = "0")
val salesCnt: Int
)
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/JoinedKey.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/MiniBook.kt
similarity index 75%
copy from compose/compose-runtime/src/main/java/androidx/compose/JoinedKey.kt
copy to room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/MiniBook.kt
index 601fbf1..42d013a 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/JoinedKey.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/MiniBook.kt
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package androidx.compose
+package androidx.room.integration.kotlintestapp.vo
-internal data class JoinedKey(
- @JvmField val left: Any?,
- @JvmField val right: Any?
+import androidx.room.PrimaryKey
+
+data class MiniBook(
+ @PrimaryKey val bookId: String,
+ val title: String,
+ val bookPublisherId: String
)
\ No newline at end of file
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/TestDatabase.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/TestDatabase.java
index f32c5cc..8e36cd8 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/TestDatabase.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/TestDatabase.java
@@ -22,6 +22,7 @@
import androidx.room.TypeConverters;
import androidx.room.integration.testapp.dao.BlobEntityDao;
import androidx.room.integration.testapp.dao.FunnyNamedDao;
+import androidx.room.integration.testapp.dao.LibraryItemDao;
import androidx.room.integration.testapp.dao.PetCoupleDao;
import androidx.room.integration.testapp.dao.PetDao;
import androidx.room.integration.testapp.dao.ProductDao;
@@ -45,6 +46,7 @@
import androidx.room.integration.testapp.vo.PetWithUser;
import androidx.room.integration.testapp.vo.Product;
import androidx.room.integration.testapp.vo.Robot;
+import androidx.room.integration.testapp.vo.RoomLibraryPojo;
import androidx.room.integration.testapp.vo.School;
import androidx.room.integration.testapp.vo.Toy;
import androidx.room.integration.testapp.vo.User;
@@ -57,7 +59,7 @@
@Database(entities = {User.class, Pet.class, School.class, PetCouple.class, Toy.class,
BlobEntity.class, Product.class, FunnyNamedEntity.class, House.class,
- FriendsJunction.class, Hivemind.class, Robot.class},
+ FriendsJunction.class, Hivemind.class, Robot.class, RoomLibraryPojo.class},
views = {PetWithUser.class},
version = 1, exportSchema = false)
@TypeConverters(TestDatabase.Converters.class)
@@ -76,6 +78,7 @@
public abstract RawDao getRawDao();
public abstract UserHouseDao getUserHouseDao();
public abstract RobotsDao getRobotsDao();
+ public abstract LibraryItemDao getLibraryItemDao();
@SuppressWarnings("unused")
public static class Converters {
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/LibraryItemDao.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/LibraryItemDao.java
new file mode 100644
index 0000000..5f9bcb0
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/LibraryItemDao.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 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.room.integration.testapp.dao;
+
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.Query;
+import androidx.room.integration.testapp.vo.RoomLibraryPojo;
+
+import java.util.List;
+
+@Dao
+public interface LibraryItemDao {
+
+ @Query("SELECT mId, mName, mPrice FROM library_items")
+ List<RoomLibraryPojo> getAll();
+
+ @Query("SELECT mId, mName, mPrice FROM library_items WHERE mId IN (:ids)")
+ List<RoomLibraryPojo> loadAllByIds(int[] ids);
+
+ @Query("SELECT mId, mName, mPrice FROM library_items WHERE mName LIKE :name LIMIT 1")
+ RoomLibraryPojo findByName(String name);
+
+ @Insert
+ void insertAll(RoomLibraryPojo... item);
+
+ @Delete
+ void delete(RoomLibraryPojo item);
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/LibraryPojo.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/LibraryPojo.java
new file mode 100644
index 0000000..c9852f7
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/LibraryPojo.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019 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.room.integration.testapp.vo;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * A unmodifiable POJO that comes from a library.
+ */
+public class LibraryPojo {
+
+ private final JSONObject mJsonObj = new JSONObject();
+
+ public Long getPrice() {
+ return mJsonObj.optLong("price");
+ }
+
+ public Long getId() {
+ return mJsonObj.optLong("id");
+ }
+
+ public String getName() {
+ return mJsonObj.optString("name");
+ }
+
+ public void setPrice(Long price) {
+ try {
+ mJsonObj.put("price", price);
+ } catch (JSONException e) {
+ // ignored
+ }
+ }
+
+ public void setId(long id) {
+ try {
+ mJsonObj.put("id", id);
+ } catch (JSONException e) {
+ // ignored
+ }
+ }
+
+ public void setName(String name) {
+ try {
+ mJsonObj.put("name", name);
+ } catch (JSONException e) {
+ // ignored
+ }
+ }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/RoomLibraryPojo.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/RoomLibraryPojo.java
new file mode 100644
index 0000000..fc02ba4
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/RoomLibraryPojo.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 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.room.integration.testapp.vo;
+
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+/**
+ * A Room POJO is just wraps a POJO provided by some library.
+ */
+@Entity(tableName = "library_items", ignoredColumns = "mJsonObj")
+public class RoomLibraryPojo extends LibraryPojo {
+
+ @PrimaryKey
+ private long mId;
+ private String mName;
+ private Long mPrice;
+
+ public RoomLibraryPojo(long id, String name, Long price) {
+ setId(id);
+ setName(name);
+ setPrice(price);
+ }
+
+}
diff --git a/room/runtime/api/2.2.0-alpha02.txt b/room/runtime/api/2.2.0-alpha02.txt
index 7c34026..1d210e5 100644
--- a/room/runtime/api/2.2.0-alpha02.txt
+++ b/room/runtime/api/2.2.0-alpha02.txt
@@ -56,8 +56,9 @@
method @CallSuper public void init(androidx.room.DatabaseConfiguration);
method protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase);
method public boolean isOpen();
- method public android.database.Cursor! query(String!, Object![]?);
- method public android.database.Cursor! query(androidx.sqlite.db.SupportSQLiteQuery!);
+ method public android.database.Cursor query(String, Object![]?);
+ method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery);
+ method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery, android.os.CancellationSignal?);
method public void runInTransaction(Runnable);
method public <V> V! runInTransaction(java.util.concurrent.Callable<V!>);
method @Deprecated public void setTransactionSuccessful();
diff --git a/room/runtime/api/current.txt b/room/runtime/api/current.txt
index 7c34026..1d210e5 100644
--- a/room/runtime/api/current.txt
+++ b/room/runtime/api/current.txt
@@ -56,8 +56,9 @@
method @CallSuper public void init(androidx.room.DatabaseConfiguration);
method protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase);
method public boolean isOpen();
- method public android.database.Cursor! query(String!, Object![]?);
- method public android.database.Cursor! query(androidx.sqlite.db.SupportSQLiteQuery!);
+ method public android.database.Cursor query(String, Object![]?);
+ method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery);
+ method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery, android.os.CancellationSignal?);
method public void runInTransaction(Runnable);
method public <V> V! runInTransaction(java.util.concurrent.Callable<V!>);
method @Deprecated public void setTransactionSuccessful();
diff --git a/room/runtime/api/restricted_2.2.0-alpha02.txt b/room/runtime/api/restricted_2.2.0-alpha02.txt
index 170506d..8e744d7 100644
--- a/room/runtime/api/restricted_2.2.0-alpha02.txt
+++ b/room/runtime/api/restricted_2.2.0-alpha02.txt
@@ -95,8 +95,9 @@
method @CallSuper public void init(androidx.room.DatabaseConfiguration);
method protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase);
method public boolean isOpen();
- method public android.database.Cursor! query(String!, Object![]?);
- method public android.database.Cursor! query(androidx.sqlite.db.SupportSQLiteQuery!);
+ method public android.database.Cursor query(String, Object![]?);
+ method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery);
+ method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery, android.os.CancellationSignal?);
method public void runInTransaction(Runnable);
method public <V> V! runInTransaction(java.util.concurrent.Callable<V!>);
method @Deprecated public void setTransactionSuccessful();
@@ -233,7 +234,8 @@
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DBUtil {
method public static void dropFtsSyncTriggers(androidx.sqlite.db.SupportSQLiteDatabase!);
- method public static android.database.Cursor query(androidx.room.RoomDatabase!, androidx.sqlite.db.SupportSQLiteQuery!, boolean);
+ method @Deprecated public static android.database.Cursor query(androidx.room.RoomDatabase!, androidx.sqlite.db.SupportSQLiteQuery!, boolean);
+ method public static android.database.Cursor query(androidx.room.RoomDatabase, androidx.sqlite.db.SupportSQLiteQuery, boolean, android.os.CancellationSignal?);
method public static int readVersion(java.io.File) throws java.io.IOException;
}
diff --git a/room/runtime/api/restricted_current.txt b/room/runtime/api/restricted_current.txt
index 170506d..8e744d7 100644
--- a/room/runtime/api/restricted_current.txt
+++ b/room/runtime/api/restricted_current.txt
@@ -95,8 +95,9 @@
method @CallSuper public void init(androidx.room.DatabaseConfiguration);
method protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase);
method public boolean isOpen();
- method public android.database.Cursor! query(String!, Object![]?);
- method public android.database.Cursor! query(androidx.sqlite.db.SupportSQLiteQuery!);
+ method public android.database.Cursor query(String, Object![]?);
+ method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery);
+ method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery, android.os.CancellationSignal?);
method public void runInTransaction(Runnable);
method public <V> V! runInTransaction(java.util.concurrent.Callable<V!>);
method @Deprecated public void setTransactionSuccessful();
@@ -233,7 +234,8 @@
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DBUtil {
method public static void dropFtsSyncTriggers(androidx.sqlite.db.SupportSQLiteDatabase!);
- method public static android.database.Cursor query(androidx.room.RoomDatabase!, androidx.sqlite.db.SupportSQLiteQuery!, boolean);
+ method @Deprecated public static android.database.Cursor query(androidx.room.RoomDatabase!, androidx.sqlite.db.SupportSQLiteQuery!, boolean);
+ method public static android.database.Cursor query(androidx.room.RoomDatabase, androidx.sqlite.db.SupportSQLiteQuery, boolean, android.os.CancellationSignal?);
method public static int readVersion(java.io.File) throws java.io.IOException;
}
diff --git a/room/runtime/src/main/java/androidx/room/RoomDatabase.java b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
index 2e88a28..b038c89 100644
--- a/room/runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.database.Cursor;
import android.os.Build;
+import android.os.CancellationSignal;
import android.os.Looper;
import android.util.Log;
@@ -294,7 +295,8 @@
* @param args The bind arguments for the placeholders in the query
* @return A Cursor obtained by running the given query in the Room database.
*/
- public Cursor query(String query, @Nullable Object[] args) {
+ @NonNull
+ public Cursor query(@NonNull String query, @Nullable Object[] args) {
return mOpenHelper.getWritableDatabase().query(new SimpleSQLiteQuery(query, args));
}
@@ -304,10 +306,27 @@
* @param query The Query which includes the SQL and a bind callback for bind arguments.
* @return Result of the query.
*/
- public Cursor query(SupportSQLiteQuery query) {
+ @NonNull
+ public Cursor query(@NonNull SupportSQLiteQuery query) {
+ return query(query, null);
+ }
+
+ /**
+ * Wrapper for {@link SupportSQLiteDatabase#query(SupportSQLiteQuery)}.
+ *
+ * @param query The Query which includes the SQL and a bind callback for bind arguments.
+ * @param signal The cancellation signal to be attached to the query.
+ * @return Result of the query.
+ */
+ @NonNull
+ public Cursor query(@NonNull SupportSQLiteQuery query, @Nullable CancellationSignal signal) {
assertNotMainThread();
assertNotSuspendingTransaction();
- return mOpenHelper.getWritableDatabase().query(query);
+ if (signal != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ return mOpenHelper.getWritableDatabase().query(query, signal);
+ } else {
+ return mOpenHelper.getWritableDatabase().query(query);
+ }
}
/**
diff --git a/room/runtime/src/main/java/androidx/room/util/DBUtil.java b/room/runtime/src/main/java/androidx/room/util/DBUtil.java
index f7afd26..f802e6d 100644
--- a/room/runtime/src/main/java/androidx/room/util/DBUtil.java
+++ b/room/runtime/src/main/java/androidx/room/util/DBUtil.java
@@ -19,8 +19,10 @@
import android.database.AbstractWindowedCursor;
import android.database.Cursor;
import android.os.Build;
+import android.os.CancellationSignal;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteDatabase;
@@ -53,10 +55,32 @@
* @param sqLiteQuery The query to perform.
* @param maybeCopy True if the result cursor should maybe be copied, false otherwise.
* @return Result of the query.
+ *
+ * @deprecated This is only used in the generated code and shouldn't be called directly.
*/
+ @Deprecated
@NonNull
public static Cursor query(RoomDatabase db, SupportSQLiteQuery sqLiteQuery, boolean maybeCopy) {
- final Cursor cursor = db.query(sqLiteQuery);
+ return query(db, sqLiteQuery, maybeCopy, null);
+ }
+
+ /**
+ * Performs the SQLiteQuery on the given database.
+ * <p>
+ * This util method encapsulates copying the cursor if the {@code maybeCopy} parameter is
+ * {@code true} and either the api level is below a certain threshold or the full result of the
+ * query does not fit in a single window.
+ *
+ * @param db The database to perform the query on.
+ * @param sqLiteQuery The query to perform.
+ * @param maybeCopy True if the result cursor should maybe be copied, false otherwise.
+ * @param signal The cancellation signal to be attached to the query.
+ * @return Result of the query.
+ */
+ @NonNull
+ public static Cursor query(@NonNull RoomDatabase db, @NonNull SupportSQLiteQuery sqLiteQuery,
+ boolean maybeCopy, @Nullable CancellationSignal signal) {
+ final Cursor cursor = db.query(sqLiteQuery, signal);
if (maybeCopy && cursor instanceof AbstractWindowedCursor) {
AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
int rowsInCursor = windowedCursor.getCount(); // Should fill the window.
diff --git a/room/runtime/src/test/java/androidx/room/InvalidationTrackerTest.java b/room/runtime/src/test/java/androidx/room/InvalidationTrackerTest.java
index f3caf34..f5026e1 100644
--- a/room/runtime/src/test/java/androidx/room/InvalidationTrackerTest.java
+++ b/room/runtime/src/test/java/androidx/room/InvalidationTrackerTest.java
@@ -187,7 +187,8 @@
@Test
public void refreshCheckTasks() throws Exception {
- when(mRoomDatabase.query(any(SimpleSQLiteQuery.class))).thenReturn(mock(Cursor.class));
+ when(mRoomDatabase.query(any(SimpleSQLiteQuery.class)))
+ .thenReturn(mock(Cursor.class));
mTracker.refreshVersionsAsync();
mTracker.refreshVersionsAsync();
verify(mTaskExecutorRule.getTaskExecutor()).executeOnDiskIO(mTracker.mRefreshRunnable);
diff --git a/sharetarget/build.gradle b/sharetarget/build.gradle
index cc27624..b750a70 100644
--- a/sharetarget/build.gradle
+++ b/sharetarget/build.gradle
@@ -25,7 +25,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
implementation("androidx.collection:collection:1.0.0")
api(GUAVA_LISTENABLE_FUTURE)
implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha02")
diff --git a/slices/builders/build.gradle b/slices/builders/build.gradle
index 63bbe8d..914633c 100644
--- a/slices/builders/build.gradle
+++ b/slices/builders/build.gradle
@@ -28,7 +28,7 @@
implementation(project(":slice-core"))
api(project(":remotecallback"))
implementation "androidx.annotation:annotation:1.1.0"
- implementation "androidx.core:core:1.1.0"
+ implementation "androidx.core:core:1.1.0-rc01"
implementation project(':collection:collection')
}
diff --git a/slidingpanelayout/build.gradle b/slidingpanelayout/build.gradle
index 7738665..b9732d5 100644
--- a/slidingpanelayout/build.gradle
+++ b/slidingpanelayout/build.gradle
@@ -9,7 +9,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
api(project(":customview"))
}
diff --git a/swiperefreshlayout/build.gradle b/swiperefreshlayout/build.gradle
index 46c3da4..1309851 100644
--- a/swiperefreshlayout/build.gradle
+++ b/swiperefreshlayout/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
api("androidx.interpolator:interpolator:1.0.0")
androidTestImplementation(JUNIT)
diff --git a/textclassifier/build.gradle b/textclassifier/build.gradle
index 92508eb..d2cd121 100644
--- a/textclassifier/build.gradle
+++ b/textclassifier/build.gradle
@@ -13,7 +13,7 @@
api("androidx.annotation:annotation:1.1.0")
implementation("androidx.collection:collection:1.0.0")
// TODO: change to 1.1.0-alpha04 after release
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/tv-provider/build.gradle b/tv-provider/build.gradle
index 902b0d5..b4cbc11 100644
--- a/tv-provider/build.gradle
+++ b/tv-provider/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt
index 7004525..c5297e2 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt
@@ -25,10 +25,14 @@
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
+import androidx.compose.Children
+import androidx.compose.Composable
import androidx.compose.CompositionContext
+import androidx.compose.FrameManager
import androidx.ui.core.AndroidCraneView
import androidx.ui.core.ComponentNode
import androidx.ui.core.DrawNode
+import androidx.ui.core.setContent
import com.google.common.truth.Truth
import org.junit.Assert
@@ -41,7 +45,8 @@
private val renderNode = RenderNode("test")
private var canvas: Canvas? = null
- var view: ViewGroup
+ lateinit var view: ViewGroup
+ private set
init {
val displayMetrics = DisplayMetrics()
@@ -51,10 +56,13 @@
screenWithSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST)
screenHeightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
- view = activity.findViewById(android.R.id.content)
}
- abstract fun setupContent(activity: Activity)
+ fun setupContent(activity: Activity) {
+ view = setupContentInternal(activity)
+ }
+
+ protected abstract fun setupContentInternal(activity: Activity): ViewGroup
/**
* Runs all the steps leading into drawing first pixels. Useful to get into the initial state
@@ -122,13 +130,28 @@
abstract class AndroidTestCase(
activity: Activity
-) : TestCase(activity)
+) : TestCase(activity) {
+
+ override fun setupContentInternal(activity: Activity) = createViewContent(activity)
+ .apply { activity.setContentView(this) }
+
+ abstract fun createViewContent(activity: Activity): ViewGroup
+}
abstract class ComposeTestCase(
activity: Activity
) : TestCase(activity) {
lateinit var compositionContext: CompositionContext
+ private set
+
+ override fun setupContentInternal(activity: Activity): ViewGroup {
+ compositionContext = setComposeContent(activity)
+ FrameManager.nextFrame()
+ return findComposeView()!!
+ }
+
+ abstract fun setComposeContent(activity: Activity): CompositionContext
}
/**
@@ -213,4 +236,33 @@
val imageView = ImageView(activity)
imageView.setImageBitmap(Bitmap.createBitmap(picture))
activity.setContentView(imageView)
+}
+
+/**
+ * Returns the first found [AndroidCraneView] in the content view hierarchy:
+ *
+ * override fun setupContent(activity: Activity) {
+ * activity.setContent { ... }
+ * view = findComposeView()!!
+ * FrameManager.nextFrame()
+ * }
+ */
+fun ComposeTestCase.findComposeView(): AndroidCraneView? {
+ return findComposeView(activity.findViewById(android.R.id.content))
+}
+
+private fun findComposeView(view: View): AndroidCraneView? {
+ if (view is AndroidCraneView) {
+ return view
+ }
+
+ if (view is ViewGroup) {
+ for (i in 0 until view.childCount) {
+ val composeView = findComposeView(view.getChildAt(i))
+ if (composeView != null) {
+ return composeView
+ }
+ }
+ }
+ return null
}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
index fa19488..dd5abdd 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
@@ -17,8 +17,10 @@
package androidx.ui.test.cases
import android.app.Activity
+import android.view.View
import androidx.compose.composer
import androidx.compose.Composable
+import androidx.compose.CompositionContext
import androidx.compose.FrameManager
import androidx.compose.State
import androidx.compose.state
@@ -34,6 +36,7 @@
import androidx.ui.material.surface.Surface
import androidx.ui.test.ComposeTestCase
import androidx.ui.test.ToggleableTestCase
+import androidx.ui.test.findComposeView
/**
* Test case that puts the given amount of checkboxes into a column of rows and makes changes by
@@ -46,29 +49,26 @@
private val states = mutableListOf<State<Boolean>>()
- override fun setupContent(activity: Activity) {
- compositionContext = activity.setContent {
- MaterialTheme {
- Surface {
- Column {
- repeat(amountOfCheckboxes) {
- FlexRow {
- inflexible {
- Text(text = "Check Me!")
- }
- expanded(1f) {
- Align(alignment = Alignment.CenterRight) {
- CheckboxWithState()
- }
+ override fun setComposeContent(activity: Activity) = activity.setContent {
+ MaterialTheme {
+ Surface {
+ Column {
+ repeat(amountOfCheckboxes) {
+ FlexRow {
+ inflexible {
+ Text(text = "Check Me!")
+ }
+ expanded(1f) {
+ Align(alignment = Alignment.CenterRight) {
+ CheckboxWithState()
}
}
}
}
}
}
- }!!
- FrameManager.nextFrame()
- }
+ }
+ }!!
override fun toggleState() {
val state = states.first()
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
index d33c624..6660cf6 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
@@ -17,6 +17,8 @@
package androidx.ui.test.cases
import android.app.Activity
+import androidx.compose.Composable
+import androidx.compose.CompositionContext
import androidx.compose.composer
import androidx.compose.FrameManager
import androidx.compose.Model
@@ -46,22 +48,19 @@
private val model = RectanglesInColumnTestCaseColorModel(Color.Black)
- override fun setupContent(activity: Activity) {
- compositionContext = activity.setContent {
- MaterialTheme {
- Column {
- repeat(amountOfRectangles) { i ->
- if (i == 0) {
- ColoredRect(color = model.color, width = 100.dp, height = 50.dp)
- } else {
- ColoredRect(color = Color.Green, width = 100.dp, height = 50.dp)
- }
+ override fun setComposeContent(activity: Activity) = activity.setContent {
+ MaterialTheme {
+ Column {
+ repeat(amountOfRectangles) { i ->
+ if (i == 0) {
+ ColoredRect(color = model.color, width = 100.dp, height = 50.dp)
+ } else {
+ ColoredRect(color = Color.Green, width = 100.dp, height = 50.dp)
}
}
}
- }!!
- FrameManager.nextFrame()
- }
+ }
+ }!!
override fun toggleState() {
if (model.color == Color.Purple) {
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
index 526c984..0f25cc2 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
@@ -19,6 +19,7 @@
import android.app.Activity
import androidx.compose.composer
import androidx.compose.Composable
+import androidx.compose.CompositionContext
import androidx.compose.FrameManager
import androidx.compose.State
import androidx.compose.state
@@ -46,20 +47,17 @@
private val states = mutableListOf<State<Color>>()
- override fun setupContent(activity: Activity) {
- compositionContext = activity.setContent {
- MaterialTheme {
- Surface {
- Column {
- repeat(amountOfRectangles) {
- ColoredRectWithModel()
- }
+ override fun setComposeContent(activity: Activity) = activity.setContent {
+ MaterialTheme {
+ Surface {
+ Column {
+ repeat(amountOfRectangles) {
+ ColoredRectWithModel()
}
}
}
- }!!
- FrameManager.nextFrame()
- }
+ }
+ }!!
override fun toggleState() {
val state = states.first()
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
index 1d93e85..8941a79 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
@@ -36,7 +36,7 @@
private val checkboxes = mutableListOf<CheckBox>()
- override fun setupContent(activity: Activity) {
+ override fun createViewContent(activity: Activity): ViewGroup {
val column = LinearLayout(activity)
column.orientation = LinearLayout.VERTICAL
column.layoutParams = ViewGroup.LayoutParams(
@@ -73,7 +73,7 @@
row.addView(checkbox)
column.addView(row)
}
- activity.setContentView(column)
+ return column
}
fun toggleState() {
diff --git a/ui/settings.gradle b/ui/settings.gradle
index 75431ad..2063fef 100644
--- a/ui/settings.gradle
+++ b/ui/settings.gradle
@@ -55,6 +55,10 @@
includeProject(":ui:ui-test", "ui-test")
includeProject(":ui:ui-text", "ui-text")
includeProject(":ui:ui-text:integration-tests:ui-text-demos", "ui-text/integration-tests/text-demos")
+includeProject(":ui:ui-vector", "ui-vector")
+includeProject(":compose:compose-runtime:integration-tests", "../compose/compose-runtime/integration-tests")
+includeProject(":compose:compose-runtime:integration-tests:android-tests", "../compose/compose-runtime/integration-tests/android-tests")
+
/////////////////////////////
//
diff --git a/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt b/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
index c1a49d5..f3f6dd7 100644
--- a/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
+++ b/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
@@ -46,7 +46,7 @@
fun <T> Transition(
definition: TransitionDefinition<T>,
toState: T,
- @Children children: @Composable() (state: TransitionState) -> Unit
+ children: @Composable() (state: TransitionState) -> Unit
) {
if (transitionsEnabled) {
// TODO: This null is workaround for b/132148894
diff --git a/ui/ui-core/api/1.0.0-alpha01.txt b/ui/ui-core/api/1.0.0-alpha01.txt
index d0525d8..195ecdf 100644
--- a/ui/ui-core/api/1.0.0-alpha01.txt
+++ b/ui/ui-core/api/1.0.0-alpha01.txt
@@ -479,7 +479,9 @@
public static final class Px.Companion {
method public androidx.ui.core.Px getInfinity();
+ method public androidx.ui.core.Px getZero();
property public final androidx.ui.core.Px Infinity;
+ property public final androidx.ui.core.Px Zero;
}
public final class PxBounds {
@@ -1320,6 +1322,255 @@
}
+package androidx.ui.graphics.vectorgraphics {
+
+ public interface Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ }
+
+ public final class BrushKt {
+ ctor public BrushKt();
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient HorizontalGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startX, androidx.ui.core.Px endX, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.Brush HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startX, androidx.ui.core.Px endX, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient LinearGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.RadialGradient RadialGradient(androidx.ui.graphics.Color![] colors, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient VerticalGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startY, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startY, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.Brush getEmptyBrush();
+ method public static androidx.ui.graphics.vectorgraphics.Brush obtainBrush(Object? brush);
+ }
+
+ public final class GroupComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public GroupComponent(String name);
+ ctor public GroupComponent();
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getClipPathNodes();
+ method public String getName();
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSize();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public void insertAt(int index, androidx.ui.graphics.vectorgraphics.VNode instance);
+ method public void move(int from, int to, int count);
+ method public void remove(int index, int count);
+ method public void setClipPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] value);
+ method public void setPivotX(float value);
+ method public void setPivotY(float value);
+ method public void setRotation(float value);
+ method public void setScaleX(float value);
+ method public void setScaleY(float value);
+ method public void setTranslationX(float value);
+ method public void setTranslationY(float value);
+ property public final androidx.ui.graphics.vectorgraphics.PathNode![] clipPathNodes;
+ property public final float pivotX;
+ property public final float pivotY;
+ property public final float rotation;
+ property public final float scaleX;
+ property public final float scaleY;
+ property public final int size;
+ property public final float translationX;
+ property public final float translationY;
+ }
+
+ public final class LinearGradient implements androidx.ui.graphics.vectorgraphics.Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public java.util.List<androidx.ui.graphics.Color> component1();
+ method public java.util.List<java.lang.Float>? component2();
+ method public androidx.ui.core.Px component3();
+ method public androidx.ui.core.Px component4();
+ method public androidx.ui.core.Px component5();
+ method public androidx.ui.core.Px component6();
+ method public androidx.ui.painting.TileMode component7();
+ method public androidx.ui.graphics.vectorgraphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode);
+ method public java.util.List<androidx.ui.graphics.Color> getColors();
+ method public androidx.ui.core.Px getEndX();
+ method public androidx.ui.core.Px getEndY();
+ method public androidx.ui.core.Px getStartX();
+ method public androidx.ui.core.Px getStartY();
+ method public java.util.List<java.lang.Float>? getStops();
+ method public androidx.ui.painting.TileMode getTileMode();
+ }
+
+ public final class PathBuilder {
+ ctor public PathBuilder();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder arcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder arcToRelative(float a, float b, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder close();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder curveTo(float x1, float y1, float x2, float y2, float x3, float y3);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder curveToRelative(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getNodes();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder horizontalLineTo(float x);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder horizontalLineToRelative(float x);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder lineTo(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder lineToRelative(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder moveTo(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder moveToRelative(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder quadTo(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder quadToRelative(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveCurveTo(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveCurveToRelative(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveQuadTo(float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveQuadToRelative(float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder verticalLineTo(float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder verticalLineToRelative(float y);
+ }
+
+ public enum PathCommand {
+ method public final char toKey();
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ArcTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand Close;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand CurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand HorizontalLineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand LineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand MoveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand QuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ReflectiveCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ReflectiveQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeArcTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeClose;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeHorizontalTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeLineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeMoveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeReflectiveCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeReflectiveQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeVerticalTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand VerticalLineTo;
+ }
+
+ public final class PathCommandKt {
+ ctor public PathCommandKt();
+ method public static androidx.ui.graphics.vectorgraphics.PathCommand toPathCommand(char);
+ }
+
+ public final class PathComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public PathComponent(String name);
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.graphics.vectorgraphics.Brush getFill();
+ method public float getFillAlpha();
+ method public String getName();
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getPathNodes();
+ method public androidx.ui.graphics.vectorgraphics.Brush getStroke();
+ method public float getStrokeAlpha();
+ method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+ method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+ method public float getStrokeLineMiter();
+ method public float getStrokeLineWidth();
+ method public void setFill(androidx.ui.graphics.vectorgraphics.Brush value);
+ method public void setFillAlpha(float value);
+ method public void setPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] value);
+ method public void setStroke(androidx.ui.graphics.vectorgraphics.Brush value);
+ method public void setStrokeAlpha(float value);
+ method public void setStrokeLineCap(androidx.ui.painting.StrokeCap value);
+ method public void setStrokeLineJoin(androidx.ui.painting.StrokeJoin value);
+ method public void setStrokeLineMiter(float value);
+ method public void setStrokeLineWidth(float value);
+ property public final androidx.ui.graphics.vectorgraphics.Brush fill;
+ property public final float fillAlpha;
+ property public final androidx.ui.graphics.vectorgraphics.PathNode![] pathNodes;
+ property public final androidx.ui.graphics.vectorgraphics.Brush stroke;
+ property public final float strokeAlpha;
+ property public final androidx.ui.painting.StrokeCap strokeLineCap;
+ property public final androidx.ui.painting.StrokeJoin strokeLineJoin;
+ property public final float strokeLineMiter;
+ property public final float strokeLineWidth;
+ }
+
+ public final class PathDelegate {
+ ctor public PathDelegate(kotlin.jvm.functions.Function1<? super androidx.ui.graphics.vectorgraphics.PathBuilder,kotlin.Unit> delegate);
+ method public kotlin.jvm.functions.Function1<androidx.ui.graphics.vectorgraphics.PathBuilder,kotlin.Unit> getDelegate();
+ }
+
+ public final class PathNode {
+ ctor public PathNode(androidx.ui.graphics.vectorgraphics.PathCommand command, float[] args);
+ method public androidx.ui.graphics.vectorgraphics.PathCommand component1();
+ method public float[] component2();
+ method public androidx.ui.graphics.vectorgraphics.PathNode copy(androidx.ui.graphics.vectorgraphics.PathCommand command, float[] args);
+ method public float[] getArgs();
+ method public androidx.ui.graphics.vectorgraphics.PathCommand getCommand();
+ }
+
+ public final class PathNodeKt {
+ ctor public PathNodeKt();
+ method public static operator StringBuilder plus(StringBuilder, androidx.ui.graphics.vectorgraphics.PathNode node);
+ }
+
+ public final class PathParser {
+ ctor public PathParser();
+ method public androidx.ui.graphics.vectorgraphics.PathParser addPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] nodes);
+ method public void clear();
+ method public androidx.ui.graphics.vectorgraphics.PathParser parsePathString(String pathData);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] toNodes();
+ method public androidx.ui.painting.Path toPath(androidx.ui.painting.Path target = androidx.ui.painting.Path());
+ }
+
+ public final class PathParserKt {
+ ctor public PathParserKt();
+ }
+
+ public final class RadialGradient implements androidx.ui.graphics.vectorgraphics.Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public androidx.ui.graphics.vectorgraphics.RadialGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode);
+ }
+
+ public final class SolidColor implements androidx.ui.graphics.vectorgraphics.Brush {
+ ctor public SolidColor(androidx.ui.graphics.Color value);
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public androidx.ui.graphics.vectorgraphics.SolidColor copy(androidx.ui.graphics.Color value);
+ }
+
+ public abstract sealed class VNode {
+ method public abstract void draw(androidx.ui.painting.Canvas canvas);
+ }
+
+ public final class VectorComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public VectorComponent(String name, float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight);
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public androidx.ui.graphics.vectorgraphics.GroupComponent getRoot();
+ method public int getSize();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
+ method public void setDefaultHeight(androidx.ui.core.Px p);
+ method public void setDefaultWidth(androidx.ui.core.Px p);
+ method public void setViewportHeight(float p);
+ method public void setViewportWidth(float p);
+ property public final androidx.ui.graphics.vectorgraphics.GroupComponent root;
+ property public final int size;
+ }
+
+ public final class VectorKt {
+ ctor public VectorKt();
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] createPath(Object? pathData);
+ method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
+ method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] getEmptyPath();
+ field public static final float DefaultAlpha = 1.0f;
+ field public static final String DefaultGroupName = "";
+ field public static final String DefaultPathName = "";
+ field public static final float DefaultPivotX = 0.0f;
+ field public static final float DefaultPivotY = 0.0f;
+ field public static final float DefaultRotation = 0.0f;
+ field public static final float DefaultScaleX = 1.0f;
+ field public static final float DefaultScaleY = 1.0f;
+ field public static final float DefaultStrokeLineMiter = 4.0f;
+ field public static final float DefaultStrokeLineWidth = 0.0f;
+ field public static final float DefaultTranslationX = 0.0f;
+ field public static final float DefaultTranslationY = 0.0f;
+ }
+
+}
+
package androidx.ui.painting {
public final class AndroidCanvasKt {
@@ -1441,7 +1692,7 @@
public static final class Gradient.Companion {
method public androidx.ui.painting.Gradient linear(androidx.ui.engine.geometry.Offset from, androidx.ui.engine.geometry.Offset to, java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? colorStops = null, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
- method public androidx.ui.painting.Gradient radial(androidx.ui.engine.geometry.Offset center, float radius, java.util.List<androidx.ui.graphics.Color> color, java.util.List<java.lang.Float>? colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, androidx.ui.vectormath64.Matrix4 matrix4, androidx.ui.engine.geometry.Offset? focal, float focalRadius);
+ method public androidx.ui.painting.Gradient radial(androidx.ui.engine.geometry.Offset center, float radius, java.util.List<androidx.ui.graphics.Color> color, java.util.List<java.lang.Float>? colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, androidx.ui.vectormath64.Matrix4 matrix4);
method public androidx.ui.painting.Gradient sweep(androidx.ui.engine.geometry.Offset center, java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float> colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, float startAngle, float endAngle = 6.2831855f, androidx.ui.vectormath64.Matrix4 matrix4);
}
@@ -1685,6 +1936,7 @@
method public static androidx.ui.core.PointerInputChange consume(androidx.ui.core.PointerInputChange, float dx = 0f, float dy = 0f, boolean downChange = false);
method public static androidx.ui.core.PointerInputChange down(int id = 0, androidx.ui.core.Timestamp timestamp = 0.millisecondsToTimestamp(), float x = 0f, float y = 0f);
method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverAllPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, java.util.List<androidx.ui.core.PointerInputChange> pointerInputChanges);
+ method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverAllPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, androidx.ui.core.PointerInputChange... pointerInputChanges);
method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, java.util.List<androidx.ui.core.PointerInputChange> pointerInputChanges, androidx.ui.core.PointerEventPass... pointerEventPasses);
method public static androidx.ui.core.PointerInputChange moveBy(androidx.ui.core.PointerInputChange, androidx.ui.core.Duration duration, float dx = 0f, float dy = 0f);
method public static androidx.ui.core.PointerInputChange moveTo(androidx.ui.core.PointerInputChange, androidx.ui.core.Timestamp timestamp, float x = 0f, float y = 0f);
diff --git a/ui/ui-core/api/current.txt b/ui/ui-core/api/current.txt
index d0525d8..195ecdf 100644
--- a/ui/ui-core/api/current.txt
+++ b/ui/ui-core/api/current.txt
@@ -479,7 +479,9 @@
public static final class Px.Companion {
method public androidx.ui.core.Px getInfinity();
+ method public androidx.ui.core.Px getZero();
property public final androidx.ui.core.Px Infinity;
+ property public final androidx.ui.core.Px Zero;
}
public final class PxBounds {
@@ -1320,6 +1322,255 @@
}
+package androidx.ui.graphics.vectorgraphics {
+
+ public interface Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ }
+
+ public final class BrushKt {
+ ctor public BrushKt();
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient HorizontalGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startX, androidx.ui.core.Px endX, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.Brush HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startX, androidx.ui.core.Px endX, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient LinearGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.RadialGradient RadialGradient(androidx.ui.graphics.Color![] colors, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient VerticalGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startY, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startY, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.Brush getEmptyBrush();
+ method public static androidx.ui.graphics.vectorgraphics.Brush obtainBrush(Object? brush);
+ }
+
+ public final class GroupComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public GroupComponent(String name);
+ ctor public GroupComponent();
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getClipPathNodes();
+ method public String getName();
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSize();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public void insertAt(int index, androidx.ui.graphics.vectorgraphics.VNode instance);
+ method public void move(int from, int to, int count);
+ method public void remove(int index, int count);
+ method public void setClipPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] value);
+ method public void setPivotX(float value);
+ method public void setPivotY(float value);
+ method public void setRotation(float value);
+ method public void setScaleX(float value);
+ method public void setScaleY(float value);
+ method public void setTranslationX(float value);
+ method public void setTranslationY(float value);
+ property public final androidx.ui.graphics.vectorgraphics.PathNode![] clipPathNodes;
+ property public final float pivotX;
+ property public final float pivotY;
+ property public final float rotation;
+ property public final float scaleX;
+ property public final float scaleY;
+ property public final int size;
+ property public final float translationX;
+ property public final float translationY;
+ }
+
+ public final class LinearGradient implements androidx.ui.graphics.vectorgraphics.Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public java.util.List<androidx.ui.graphics.Color> component1();
+ method public java.util.List<java.lang.Float>? component2();
+ method public androidx.ui.core.Px component3();
+ method public androidx.ui.core.Px component4();
+ method public androidx.ui.core.Px component5();
+ method public androidx.ui.core.Px component6();
+ method public androidx.ui.painting.TileMode component7();
+ method public androidx.ui.graphics.vectorgraphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode);
+ method public java.util.List<androidx.ui.graphics.Color> getColors();
+ method public androidx.ui.core.Px getEndX();
+ method public androidx.ui.core.Px getEndY();
+ method public androidx.ui.core.Px getStartX();
+ method public androidx.ui.core.Px getStartY();
+ method public java.util.List<java.lang.Float>? getStops();
+ method public androidx.ui.painting.TileMode getTileMode();
+ }
+
+ public final class PathBuilder {
+ ctor public PathBuilder();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder arcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder arcToRelative(float a, float b, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder close();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder curveTo(float x1, float y1, float x2, float y2, float x3, float y3);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder curveToRelative(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getNodes();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder horizontalLineTo(float x);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder horizontalLineToRelative(float x);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder lineTo(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder lineToRelative(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder moveTo(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder moveToRelative(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder quadTo(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder quadToRelative(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveCurveTo(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveCurveToRelative(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveQuadTo(float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveQuadToRelative(float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder verticalLineTo(float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder verticalLineToRelative(float y);
+ }
+
+ public enum PathCommand {
+ method public final char toKey();
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ArcTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand Close;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand CurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand HorizontalLineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand LineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand MoveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand QuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ReflectiveCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ReflectiveQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeArcTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeClose;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeHorizontalTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeLineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeMoveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeReflectiveCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeReflectiveQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeVerticalTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand VerticalLineTo;
+ }
+
+ public final class PathCommandKt {
+ ctor public PathCommandKt();
+ method public static androidx.ui.graphics.vectorgraphics.PathCommand toPathCommand(char);
+ }
+
+ public final class PathComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public PathComponent(String name);
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.graphics.vectorgraphics.Brush getFill();
+ method public float getFillAlpha();
+ method public String getName();
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getPathNodes();
+ method public androidx.ui.graphics.vectorgraphics.Brush getStroke();
+ method public float getStrokeAlpha();
+ method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+ method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+ method public float getStrokeLineMiter();
+ method public float getStrokeLineWidth();
+ method public void setFill(androidx.ui.graphics.vectorgraphics.Brush value);
+ method public void setFillAlpha(float value);
+ method public void setPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] value);
+ method public void setStroke(androidx.ui.graphics.vectorgraphics.Brush value);
+ method public void setStrokeAlpha(float value);
+ method public void setStrokeLineCap(androidx.ui.painting.StrokeCap value);
+ method public void setStrokeLineJoin(androidx.ui.painting.StrokeJoin value);
+ method public void setStrokeLineMiter(float value);
+ method public void setStrokeLineWidth(float value);
+ property public final androidx.ui.graphics.vectorgraphics.Brush fill;
+ property public final float fillAlpha;
+ property public final androidx.ui.graphics.vectorgraphics.PathNode![] pathNodes;
+ property public final androidx.ui.graphics.vectorgraphics.Brush stroke;
+ property public final float strokeAlpha;
+ property public final androidx.ui.painting.StrokeCap strokeLineCap;
+ property public final androidx.ui.painting.StrokeJoin strokeLineJoin;
+ property public final float strokeLineMiter;
+ property public final float strokeLineWidth;
+ }
+
+ public final class PathDelegate {
+ ctor public PathDelegate(kotlin.jvm.functions.Function1<? super androidx.ui.graphics.vectorgraphics.PathBuilder,kotlin.Unit> delegate);
+ method public kotlin.jvm.functions.Function1<androidx.ui.graphics.vectorgraphics.PathBuilder,kotlin.Unit> getDelegate();
+ }
+
+ public final class PathNode {
+ ctor public PathNode(androidx.ui.graphics.vectorgraphics.PathCommand command, float[] args);
+ method public androidx.ui.graphics.vectorgraphics.PathCommand component1();
+ method public float[] component2();
+ method public androidx.ui.graphics.vectorgraphics.PathNode copy(androidx.ui.graphics.vectorgraphics.PathCommand command, float[] args);
+ method public float[] getArgs();
+ method public androidx.ui.graphics.vectorgraphics.PathCommand getCommand();
+ }
+
+ public final class PathNodeKt {
+ ctor public PathNodeKt();
+ method public static operator StringBuilder plus(StringBuilder, androidx.ui.graphics.vectorgraphics.PathNode node);
+ }
+
+ public final class PathParser {
+ ctor public PathParser();
+ method public androidx.ui.graphics.vectorgraphics.PathParser addPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] nodes);
+ method public void clear();
+ method public androidx.ui.graphics.vectorgraphics.PathParser parsePathString(String pathData);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] toNodes();
+ method public androidx.ui.painting.Path toPath(androidx.ui.painting.Path target = androidx.ui.painting.Path());
+ }
+
+ public final class PathParserKt {
+ ctor public PathParserKt();
+ }
+
+ public final class RadialGradient implements androidx.ui.graphics.vectorgraphics.Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public androidx.ui.graphics.vectorgraphics.RadialGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode);
+ }
+
+ public final class SolidColor implements androidx.ui.graphics.vectorgraphics.Brush {
+ ctor public SolidColor(androidx.ui.graphics.Color value);
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public androidx.ui.graphics.vectorgraphics.SolidColor copy(androidx.ui.graphics.Color value);
+ }
+
+ public abstract sealed class VNode {
+ method public abstract void draw(androidx.ui.painting.Canvas canvas);
+ }
+
+ public final class VectorComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public VectorComponent(String name, float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight);
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public androidx.ui.graphics.vectorgraphics.GroupComponent getRoot();
+ method public int getSize();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
+ method public void setDefaultHeight(androidx.ui.core.Px p);
+ method public void setDefaultWidth(androidx.ui.core.Px p);
+ method public void setViewportHeight(float p);
+ method public void setViewportWidth(float p);
+ property public final androidx.ui.graphics.vectorgraphics.GroupComponent root;
+ property public final int size;
+ }
+
+ public final class VectorKt {
+ ctor public VectorKt();
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] createPath(Object? pathData);
+ method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
+ method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] getEmptyPath();
+ field public static final float DefaultAlpha = 1.0f;
+ field public static final String DefaultGroupName = "";
+ field public static final String DefaultPathName = "";
+ field public static final float DefaultPivotX = 0.0f;
+ field public static final float DefaultPivotY = 0.0f;
+ field public static final float DefaultRotation = 0.0f;
+ field public static final float DefaultScaleX = 1.0f;
+ field public static final float DefaultScaleY = 1.0f;
+ field public static final float DefaultStrokeLineMiter = 4.0f;
+ field public static final float DefaultStrokeLineWidth = 0.0f;
+ field public static final float DefaultTranslationX = 0.0f;
+ field public static final float DefaultTranslationY = 0.0f;
+ }
+
+}
+
package androidx.ui.painting {
public final class AndroidCanvasKt {
@@ -1441,7 +1692,7 @@
public static final class Gradient.Companion {
method public androidx.ui.painting.Gradient linear(androidx.ui.engine.geometry.Offset from, androidx.ui.engine.geometry.Offset to, java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? colorStops = null, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
- method public androidx.ui.painting.Gradient radial(androidx.ui.engine.geometry.Offset center, float radius, java.util.List<androidx.ui.graphics.Color> color, java.util.List<java.lang.Float>? colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, androidx.ui.vectormath64.Matrix4 matrix4, androidx.ui.engine.geometry.Offset? focal, float focalRadius);
+ method public androidx.ui.painting.Gradient radial(androidx.ui.engine.geometry.Offset center, float radius, java.util.List<androidx.ui.graphics.Color> color, java.util.List<java.lang.Float>? colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, androidx.ui.vectormath64.Matrix4 matrix4);
method public androidx.ui.painting.Gradient sweep(androidx.ui.engine.geometry.Offset center, java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float> colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, float startAngle, float endAngle = 6.2831855f, androidx.ui.vectormath64.Matrix4 matrix4);
}
@@ -1685,6 +1936,7 @@
method public static androidx.ui.core.PointerInputChange consume(androidx.ui.core.PointerInputChange, float dx = 0f, float dy = 0f, boolean downChange = false);
method public static androidx.ui.core.PointerInputChange down(int id = 0, androidx.ui.core.Timestamp timestamp = 0.millisecondsToTimestamp(), float x = 0f, float y = 0f);
method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverAllPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, java.util.List<androidx.ui.core.PointerInputChange> pointerInputChanges);
+ method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverAllPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, androidx.ui.core.PointerInputChange... pointerInputChanges);
method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, java.util.List<androidx.ui.core.PointerInputChange> pointerInputChanges, androidx.ui.core.PointerEventPass... pointerEventPasses);
method public static androidx.ui.core.PointerInputChange moveBy(androidx.ui.core.PointerInputChange, androidx.ui.core.Duration duration, float dx = 0f, float dy = 0f);
method public static androidx.ui.core.PointerInputChange moveTo(androidx.ui.core.PointerInputChange, androidx.ui.core.Timestamp timestamp, float x = 0f, float y = 0f);
diff --git a/ui/ui-core/api/restricted_1.0.0-alpha01.txt b/ui/ui-core/api/restricted_1.0.0-alpha01.txt
index d0525d8..195ecdf 100644
--- a/ui/ui-core/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-core/api/restricted_1.0.0-alpha01.txt
@@ -479,7 +479,9 @@
public static final class Px.Companion {
method public androidx.ui.core.Px getInfinity();
+ method public androidx.ui.core.Px getZero();
property public final androidx.ui.core.Px Infinity;
+ property public final androidx.ui.core.Px Zero;
}
public final class PxBounds {
@@ -1320,6 +1322,255 @@
}
+package androidx.ui.graphics.vectorgraphics {
+
+ public interface Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ }
+
+ public final class BrushKt {
+ ctor public BrushKt();
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient HorizontalGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startX, androidx.ui.core.Px endX, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.Brush HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startX, androidx.ui.core.Px endX, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient LinearGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.RadialGradient RadialGradient(androidx.ui.graphics.Color![] colors, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient VerticalGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startY, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startY, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.Brush getEmptyBrush();
+ method public static androidx.ui.graphics.vectorgraphics.Brush obtainBrush(Object? brush);
+ }
+
+ public final class GroupComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public GroupComponent(String name);
+ ctor public GroupComponent();
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getClipPathNodes();
+ method public String getName();
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSize();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public void insertAt(int index, androidx.ui.graphics.vectorgraphics.VNode instance);
+ method public void move(int from, int to, int count);
+ method public void remove(int index, int count);
+ method public void setClipPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] value);
+ method public void setPivotX(float value);
+ method public void setPivotY(float value);
+ method public void setRotation(float value);
+ method public void setScaleX(float value);
+ method public void setScaleY(float value);
+ method public void setTranslationX(float value);
+ method public void setTranslationY(float value);
+ property public final androidx.ui.graphics.vectorgraphics.PathNode![] clipPathNodes;
+ property public final float pivotX;
+ property public final float pivotY;
+ property public final float rotation;
+ property public final float scaleX;
+ property public final float scaleY;
+ property public final int size;
+ property public final float translationX;
+ property public final float translationY;
+ }
+
+ public final class LinearGradient implements androidx.ui.graphics.vectorgraphics.Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public java.util.List<androidx.ui.graphics.Color> component1();
+ method public java.util.List<java.lang.Float>? component2();
+ method public androidx.ui.core.Px component3();
+ method public androidx.ui.core.Px component4();
+ method public androidx.ui.core.Px component5();
+ method public androidx.ui.core.Px component6();
+ method public androidx.ui.painting.TileMode component7();
+ method public androidx.ui.graphics.vectorgraphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode);
+ method public java.util.List<androidx.ui.graphics.Color> getColors();
+ method public androidx.ui.core.Px getEndX();
+ method public androidx.ui.core.Px getEndY();
+ method public androidx.ui.core.Px getStartX();
+ method public androidx.ui.core.Px getStartY();
+ method public java.util.List<java.lang.Float>? getStops();
+ method public androidx.ui.painting.TileMode getTileMode();
+ }
+
+ public final class PathBuilder {
+ ctor public PathBuilder();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder arcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder arcToRelative(float a, float b, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder close();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder curveTo(float x1, float y1, float x2, float y2, float x3, float y3);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder curveToRelative(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getNodes();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder horizontalLineTo(float x);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder horizontalLineToRelative(float x);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder lineTo(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder lineToRelative(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder moveTo(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder moveToRelative(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder quadTo(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder quadToRelative(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveCurveTo(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveCurveToRelative(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveQuadTo(float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveQuadToRelative(float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder verticalLineTo(float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder verticalLineToRelative(float y);
+ }
+
+ public enum PathCommand {
+ method public final char toKey();
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ArcTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand Close;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand CurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand HorizontalLineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand LineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand MoveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand QuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ReflectiveCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ReflectiveQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeArcTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeClose;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeHorizontalTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeLineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeMoveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeReflectiveCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeReflectiveQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeVerticalTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand VerticalLineTo;
+ }
+
+ public final class PathCommandKt {
+ ctor public PathCommandKt();
+ method public static androidx.ui.graphics.vectorgraphics.PathCommand toPathCommand(char);
+ }
+
+ public final class PathComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public PathComponent(String name);
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.graphics.vectorgraphics.Brush getFill();
+ method public float getFillAlpha();
+ method public String getName();
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getPathNodes();
+ method public androidx.ui.graphics.vectorgraphics.Brush getStroke();
+ method public float getStrokeAlpha();
+ method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+ method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+ method public float getStrokeLineMiter();
+ method public float getStrokeLineWidth();
+ method public void setFill(androidx.ui.graphics.vectorgraphics.Brush value);
+ method public void setFillAlpha(float value);
+ method public void setPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] value);
+ method public void setStroke(androidx.ui.graphics.vectorgraphics.Brush value);
+ method public void setStrokeAlpha(float value);
+ method public void setStrokeLineCap(androidx.ui.painting.StrokeCap value);
+ method public void setStrokeLineJoin(androidx.ui.painting.StrokeJoin value);
+ method public void setStrokeLineMiter(float value);
+ method public void setStrokeLineWidth(float value);
+ property public final androidx.ui.graphics.vectorgraphics.Brush fill;
+ property public final float fillAlpha;
+ property public final androidx.ui.graphics.vectorgraphics.PathNode![] pathNodes;
+ property public final androidx.ui.graphics.vectorgraphics.Brush stroke;
+ property public final float strokeAlpha;
+ property public final androidx.ui.painting.StrokeCap strokeLineCap;
+ property public final androidx.ui.painting.StrokeJoin strokeLineJoin;
+ property public final float strokeLineMiter;
+ property public final float strokeLineWidth;
+ }
+
+ public final class PathDelegate {
+ ctor public PathDelegate(kotlin.jvm.functions.Function1<? super androidx.ui.graphics.vectorgraphics.PathBuilder,kotlin.Unit> delegate);
+ method public kotlin.jvm.functions.Function1<androidx.ui.graphics.vectorgraphics.PathBuilder,kotlin.Unit> getDelegate();
+ }
+
+ public final class PathNode {
+ ctor public PathNode(androidx.ui.graphics.vectorgraphics.PathCommand command, float[] args);
+ method public androidx.ui.graphics.vectorgraphics.PathCommand component1();
+ method public float[] component2();
+ method public androidx.ui.graphics.vectorgraphics.PathNode copy(androidx.ui.graphics.vectorgraphics.PathCommand command, float[] args);
+ method public float[] getArgs();
+ method public androidx.ui.graphics.vectorgraphics.PathCommand getCommand();
+ }
+
+ public final class PathNodeKt {
+ ctor public PathNodeKt();
+ method public static operator StringBuilder plus(StringBuilder, androidx.ui.graphics.vectorgraphics.PathNode node);
+ }
+
+ public final class PathParser {
+ ctor public PathParser();
+ method public androidx.ui.graphics.vectorgraphics.PathParser addPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] nodes);
+ method public void clear();
+ method public androidx.ui.graphics.vectorgraphics.PathParser parsePathString(String pathData);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] toNodes();
+ method public androidx.ui.painting.Path toPath(androidx.ui.painting.Path target = androidx.ui.painting.Path());
+ }
+
+ public final class PathParserKt {
+ ctor public PathParserKt();
+ }
+
+ public final class RadialGradient implements androidx.ui.graphics.vectorgraphics.Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public androidx.ui.graphics.vectorgraphics.RadialGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode);
+ }
+
+ public final class SolidColor implements androidx.ui.graphics.vectorgraphics.Brush {
+ ctor public SolidColor(androidx.ui.graphics.Color value);
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public androidx.ui.graphics.vectorgraphics.SolidColor copy(androidx.ui.graphics.Color value);
+ }
+
+ public abstract sealed class VNode {
+ method public abstract void draw(androidx.ui.painting.Canvas canvas);
+ }
+
+ public final class VectorComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public VectorComponent(String name, float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight);
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public androidx.ui.graphics.vectorgraphics.GroupComponent getRoot();
+ method public int getSize();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
+ method public void setDefaultHeight(androidx.ui.core.Px p);
+ method public void setDefaultWidth(androidx.ui.core.Px p);
+ method public void setViewportHeight(float p);
+ method public void setViewportWidth(float p);
+ property public final androidx.ui.graphics.vectorgraphics.GroupComponent root;
+ property public final int size;
+ }
+
+ public final class VectorKt {
+ ctor public VectorKt();
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] createPath(Object? pathData);
+ method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
+ method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] getEmptyPath();
+ field public static final float DefaultAlpha = 1.0f;
+ field public static final String DefaultGroupName = "";
+ field public static final String DefaultPathName = "";
+ field public static final float DefaultPivotX = 0.0f;
+ field public static final float DefaultPivotY = 0.0f;
+ field public static final float DefaultRotation = 0.0f;
+ field public static final float DefaultScaleX = 1.0f;
+ field public static final float DefaultScaleY = 1.0f;
+ field public static final float DefaultStrokeLineMiter = 4.0f;
+ field public static final float DefaultStrokeLineWidth = 0.0f;
+ field public static final float DefaultTranslationX = 0.0f;
+ field public static final float DefaultTranslationY = 0.0f;
+ }
+
+}
+
package androidx.ui.painting {
public final class AndroidCanvasKt {
@@ -1441,7 +1692,7 @@
public static final class Gradient.Companion {
method public androidx.ui.painting.Gradient linear(androidx.ui.engine.geometry.Offset from, androidx.ui.engine.geometry.Offset to, java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? colorStops = null, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
- method public androidx.ui.painting.Gradient radial(androidx.ui.engine.geometry.Offset center, float radius, java.util.List<androidx.ui.graphics.Color> color, java.util.List<java.lang.Float>? colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, androidx.ui.vectormath64.Matrix4 matrix4, androidx.ui.engine.geometry.Offset? focal, float focalRadius);
+ method public androidx.ui.painting.Gradient radial(androidx.ui.engine.geometry.Offset center, float radius, java.util.List<androidx.ui.graphics.Color> color, java.util.List<java.lang.Float>? colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, androidx.ui.vectormath64.Matrix4 matrix4);
method public androidx.ui.painting.Gradient sweep(androidx.ui.engine.geometry.Offset center, java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float> colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, float startAngle, float endAngle = 6.2831855f, androidx.ui.vectormath64.Matrix4 matrix4);
}
@@ -1685,6 +1936,7 @@
method public static androidx.ui.core.PointerInputChange consume(androidx.ui.core.PointerInputChange, float dx = 0f, float dy = 0f, boolean downChange = false);
method public static androidx.ui.core.PointerInputChange down(int id = 0, androidx.ui.core.Timestamp timestamp = 0.millisecondsToTimestamp(), float x = 0f, float y = 0f);
method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverAllPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, java.util.List<androidx.ui.core.PointerInputChange> pointerInputChanges);
+ method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverAllPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, androidx.ui.core.PointerInputChange... pointerInputChanges);
method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, java.util.List<androidx.ui.core.PointerInputChange> pointerInputChanges, androidx.ui.core.PointerEventPass... pointerEventPasses);
method public static androidx.ui.core.PointerInputChange moveBy(androidx.ui.core.PointerInputChange, androidx.ui.core.Duration duration, float dx = 0f, float dy = 0f);
method public static androidx.ui.core.PointerInputChange moveTo(androidx.ui.core.PointerInputChange, androidx.ui.core.Timestamp timestamp, float x = 0f, float y = 0f);
diff --git a/ui/ui-core/api/restricted_current.txt b/ui/ui-core/api/restricted_current.txt
index d0525d8..195ecdf 100644
--- a/ui/ui-core/api/restricted_current.txt
+++ b/ui/ui-core/api/restricted_current.txt
@@ -479,7 +479,9 @@
public static final class Px.Companion {
method public androidx.ui.core.Px getInfinity();
+ method public androidx.ui.core.Px getZero();
property public final androidx.ui.core.Px Infinity;
+ property public final androidx.ui.core.Px Zero;
}
public final class PxBounds {
@@ -1320,6 +1322,255 @@
}
+package androidx.ui.graphics.vectorgraphics {
+
+ public interface Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ }
+
+ public final class BrushKt {
+ ctor public BrushKt();
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient HorizontalGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startX, androidx.ui.core.Px endX, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.Brush HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startX, androidx.ui.core.Px endX, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient LinearGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.RadialGradient RadialGradient(androidx.ui.graphics.Color![] colors, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient VerticalGradient(androidx.ui.graphics.Color![] colors, androidx.ui.core.Px startY, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.ui.graphics.Color>![] colorStops, androidx.ui.core.Px startY, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
+ method public static androidx.ui.graphics.vectorgraphics.Brush getEmptyBrush();
+ method public static androidx.ui.graphics.vectorgraphics.Brush obtainBrush(Object? brush);
+ }
+
+ public final class GroupComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public GroupComponent(String name);
+ ctor public GroupComponent();
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getClipPathNodes();
+ method public String getName();
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSize();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public void insertAt(int index, androidx.ui.graphics.vectorgraphics.VNode instance);
+ method public void move(int from, int to, int count);
+ method public void remove(int index, int count);
+ method public void setClipPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] value);
+ method public void setPivotX(float value);
+ method public void setPivotY(float value);
+ method public void setRotation(float value);
+ method public void setScaleX(float value);
+ method public void setScaleY(float value);
+ method public void setTranslationX(float value);
+ method public void setTranslationY(float value);
+ property public final androidx.ui.graphics.vectorgraphics.PathNode![] clipPathNodes;
+ property public final float pivotX;
+ property public final float pivotY;
+ property public final float rotation;
+ property public final float scaleX;
+ property public final float scaleY;
+ property public final int size;
+ property public final float translationX;
+ property public final float translationY;
+ }
+
+ public final class LinearGradient implements androidx.ui.graphics.vectorgraphics.Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public java.util.List<androidx.ui.graphics.Color> component1();
+ method public java.util.List<java.lang.Float>? component2();
+ method public androidx.ui.core.Px component3();
+ method public androidx.ui.core.Px component4();
+ method public androidx.ui.core.Px component5();
+ method public androidx.ui.core.Px component6();
+ method public androidx.ui.painting.TileMode component7();
+ method public androidx.ui.graphics.vectorgraphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, androidx.ui.core.Px startX, androidx.ui.core.Px startY, androidx.ui.core.Px endX, androidx.ui.core.Px endY, androidx.ui.painting.TileMode tileMode);
+ method public java.util.List<androidx.ui.graphics.Color> getColors();
+ method public androidx.ui.core.Px getEndX();
+ method public androidx.ui.core.Px getEndY();
+ method public androidx.ui.core.Px getStartX();
+ method public androidx.ui.core.Px getStartY();
+ method public java.util.List<java.lang.Float>? getStops();
+ method public androidx.ui.painting.TileMode getTileMode();
+ }
+
+ public final class PathBuilder {
+ ctor public PathBuilder();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder arcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder arcToRelative(float a, float b, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder close();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder curveTo(float x1, float y1, float x2, float y2, float x3, float y3);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder curveToRelative(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getNodes();
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder horizontalLineTo(float x);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder horizontalLineToRelative(float x);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder lineTo(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder lineToRelative(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder moveTo(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder moveToRelative(float x, float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder quadTo(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder quadToRelative(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveCurveTo(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveCurveToRelative(float x1, float y1, float x2, float y2);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveQuadTo(float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder reflectiveQuadToRelative(float x1, float y1);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder verticalLineTo(float y);
+ method public androidx.ui.graphics.vectorgraphics.PathBuilder verticalLineToRelative(float y);
+ }
+
+ public enum PathCommand {
+ method public final char toKey();
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ArcTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand Close;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand CurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand HorizontalLineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand LineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand MoveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand QuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ReflectiveCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand ReflectiveQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeArcTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeClose;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeHorizontalTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeLineTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeMoveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeReflectiveCurveTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeReflectiveQuadTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand RelativeVerticalTo;
+ enum_constant public static final androidx.ui.graphics.vectorgraphics.PathCommand VerticalLineTo;
+ }
+
+ public final class PathCommandKt {
+ ctor public PathCommandKt();
+ method public static androidx.ui.graphics.vectorgraphics.PathCommand toPathCommand(char);
+ }
+
+ public final class PathComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public PathComponent(String name);
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.graphics.vectorgraphics.Brush getFill();
+ method public float getFillAlpha();
+ method public String getName();
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] getPathNodes();
+ method public androidx.ui.graphics.vectorgraphics.Brush getStroke();
+ method public float getStrokeAlpha();
+ method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+ method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+ method public float getStrokeLineMiter();
+ method public float getStrokeLineWidth();
+ method public void setFill(androidx.ui.graphics.vectorgraphics.Brush value);
+ method public void setFillAlpha(float value);
+ method public void setPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] value);
+ method public void setStroke(androidx.ui.graphics.vectorgraphics.Brush value);
+ method public void setStrokeAlpha(float value);
+ method public void setStrokeLineCap(androidx.ui.painting.StrokeCap value);
+ method public void setStrokeLineJoin(androidx.ui.painting.StrokeJoin value);
+ method public void setStrokeLineMiter(float value);
+ method public void setStrokeLineWidth(float value);
+ property public final androidx.ui.graphics.vectorgraphics.Brush fill;
+ property public final float fillAlpha;
+ property public final androidx.ui.graphics.vectorgraphics.PathNode![] pathNodes;
+ property public final androidx.ui.graphics.vectorgraphics.Brush stroke;
+ property public final float strokeAlpha;
+ property public final androidx.ui.painting.StrokeCap strokeLineCap;
+ property public final androidx.ui.painting.StrokeJoin strokeLineJoin;
+ property public final float strokeLineMiter;
+ property public final float strokeLineWidth;
+ }
+
+ public final class PathDelegate {
+ ctor public PathDelegate(kotlin.jvm.functions.Function1<? super androidx.ui.graphics.vectorgraphics.PathBuilder,kotlin.Unit> delegate);
+ method public kotlin.jvm.functions.Function1<androidx.ui.graphics.vectorgraphics.PathBuilder,kotlin.Unit> getDelegate();
+ }
+
+ public final class PathNode {
+ ctor public PathNode(androidx.ui.graphics.vectorgraphics.PathCommand command, float[] args);
+ method public androidx.ui.graphics.vectorgraphics.PathCommand component1();
+ method public float[] component2();
+ method public androidx.ui.graphics.vectorgraphics.PathNode copy(androidx.ui.graphics.vectorgraphics.PathCommand command, float[] args);
+ method public float[] getArgs();
+ method public androidx.ui.graphics.vectorgraphics.PathCommand getCommand();
+ }
+
+ public final class PathNodeKt {
+ ctor public PathNodeKt();
+ method public static operator StringBuilder plus(StringBuilder, androidx.ui.graphics.vectorgraphics.PathNode node);
+ }
+
+ public final class PathParser {
+ ctor public PathParser();
+ method public androidx.ui.graphics.vectorgraphics.PathParser addPathNodes(androidx.ui.graphics.vectorgraphics.PathNode![] nodes);
+ method public void clear();
+ method public androidx.ui.graphics.vectorgraphics.PathParser parsePathString(String pathData);
+ method public androidx.ui.graphics.vectorgraphics.PathNode![] toNodes();
+ method public androidx.ui.painting.Path toPath(androidx.ui.painting.Path target = androidx.ui.painting.Path());
+ }
+
+ public final class PathParserKt {
+ ctor public PathParserKt();
+ }
+
+ public final class RadialGradient implements androidx.ui.graphics.vectorgraphics.Brush {
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public androidx.ui.graphics.vectorgraphics.RadialGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode);
+ }
+
+ public final class SolidColor implements androidx.ui.graphics.vectorgraphics.Brush {
+ ctor public SolidColor(androidx.ui.graphics.Color value);
+ method public void applyBrush(androidx.ui.painting.Paint p);
+ method public androidx.ui.graphics.vectorgraphics.SolidColor copy(androidx.ui.graphics.Color value);
+ }
+
+ public abstract sealed class VNode {
+ method public abstract void draw(androidx.ui.painting.Canvas canvas);
+ }
+
+ public final class VectorComponent extends androidx.ui.graphics.vectorgraphics.VNode {
+ ctor public VectorComponent(String name, float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight);
+ method public void draw(androidx.ui.painting.Canvas canvas);
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public androidx.ui.graphics.vectorgraphics.GroupComponent getRoot();
+ method public int getSize();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
+ method public void setDefaultHeight(androidx.ui.core.Px p);
+ method public void setDefaultWidth(androidx.ui.core.Px p);
+ method public void setViewportHeight(float p);
+ method public void setViewportWidth(float p);
+ property public final androidx.ui.graphics.vectorgraphics.GroupComponent root;
+ property public final int size;
+ }
+
+ public final class VectorKt {
+ ctor public VectorKt();
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] createPath(Object? pathData);
+ method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
+ method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
+ method public static androidx.ui.graphics.vectorgraphics.PathNode![] getEmptyPath();
+ field public static final float DefaultAlpha = 1.0f;
+ field public static final String DefaultGroupName = "";
+ field public static final String DefaultPathName = "";
+ field public static final float DefaultPivotX = 0.0f;
+ field public static final float DefaultPivotY = 0.0f;
+ field public static final float DefaultRotation = 0.0f;
+ field public static final float DefaultScaleX = 1.0f;
+ field public static final float DefaultScaleY = 1.0f;
+ field public static final float DefaultStrokeLineMiter = 4.0f;
+ field public static final float DefaultStrokeLineWidth = 0.0f;
+ field public static final float DefaultTranslationX = 0.0f;
+ field public static final float DefaultTranslationY = 0.0f;
+ }
+
+}
+
package androidx.ui.painting {
public final class AndroidCanvasKt {
@@ -1441,7 +1692,7 @@
public static final class Gradient.Companion {
method public androidx.ui.painting.Gradient linear(androidx.ui.engine.geometry.Offset from, androidx.ui.engine.geometry.Offset to, java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? colorStops = null, androidx.ui.painting.TileMode tileMode = TileMode.clamp);
- method public androidx.ui.painting.Gradient radial(androidx.ui.engine.geometry.Offset center, float radius, java.util.List<androidx.ui.graphics.Color> color, java.util.List<java.lang.Float>? colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, androidx.ui.vectormath64.Matrix4 matrix4, androidx.ui.engine.geometry.Offset? focal, float focalRadius);
+ method public androidx.ui.painting.Gradient radial(androidx.ui.engine.geometry.Offset center, float radius, java.util.List<androidx.ui.graphics.Color> color, java.util.List<java.lang.Float>? colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, androidx.ui.vectormath64.Matrix4 matrix4);
method public androidx.ui.painting.Gradient sweep(androidx.ui.engine.geometry.Offset center, java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float> colorStops, androidx.ui.painting.TileMode tileMode = TileMode.clamp, float startAngle, float endAngle = 6.2831855f, androidx.ui.vectormath64.Matrix4 matrix4);
}
@@ -1685,6 +1936,7 @@
method public static androidx.ui.core.PointerInputChange consume(androidx.ui.core.PointerInputChange, float dx = 0f, float dy = 0f, boolean downChange = false);
method public static androidx.ui.core.PointerInputChange down(int id = 0, androidx.ui.core.Timestamp timestamp = 0.millisecondsToTimestamp(), float x = 0f, float y = 0f);
method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverAllPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, java.util.List<androidx.ui.core.PointerInputChange> pointerInputChanges);
+ method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverAllPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, androidx.ui.core.PointerInputChange... pointerInputChanges);
method public static java.util.List<androidx.ui.core.PointerInputChange> invokeOverPasses(kotlin.jvm.functions.Function2<? super java.util.List<androidx.ui.core.PointerInputChange>,? super androidx.ui.core.PointerEventPass,? extends java.util.List<androidx.ui.core.PointerInputChange>>, java.util.List<androidx.ui.core.PointerInputChange> pointerInputChanges, androidx.ui.core.PointerEventPass... pointerEventPasses);
method public static androidx.ui.core.PointerInputChange moveBy(androidx.ui.core.PointerInputChange, androidx.ui.core.Duration duration, float dx = 0f, float dy = 0f);
method public static androidx.ui.core.PointerInputChange moveTo(androidx.ui.core.PointerInputChange, androidx.ui.core.Timestamp timestamp, float x = 0f, float y = 0f);
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Px.kt b/ui/ui-core/src/main/java/androidx/ui/core/Px.kt
index 9985605..5295cf3 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Px.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Px.kt
@@ -117,6 +117,11 @@
* Infinite px dimension.
*/
val Infinity = Px(value = Float.POSITIVE_INFINITY)
+
+ /**
+ * Zero px dimension
+ */
+ val Zero = Px(0.0f)
}
}
diff --git a/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/Brush.kt b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/Brush.kt
new file mode 100644
index 0000000..3243120
--- /dev/null
+++ b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/Brush.kt
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2019 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.ui.graphics.vectorgraphics
+
+import androidx.ui.core.Px
+import androidx.ui.engine.geometry.Offset
+import androidx.ui.graphics.Color
+import androidx.ui.painting.Gradient
+import androidx.ui.painting.Paint
+import androidx.ui.painting.TileMode
+import androidx.ui.vectormath64.Matrix4
+
+val EmptyBrush = object : Brush {
+ override fun applyBrush(p: Paint) {
+ // NO-OP
+ }
+}
+
+interface Brush {
+ fun applyBrush(p: Paint)
+}
+
+data class SolidColor(private val value: Color) : Brush {
+ override fun applyBrush(p: Paint) {
+ p.color = value
+ }
+}
+
+typealias ColorStop = Pair<Float, Color>
+
+/**
+ * Obtains actual Brush instance from Union type, throws an IllegalArgumentException
+ * if the type is other than Int, Color, Brush or null
+ */
+fun obtainBrush(brush: Any?): Brush {
+ return when (brush) {
+ is Int -> SolidColor(Color(brush))
+ is Color -> SolidColor(brush)
+ is Brush -> brush
+ null -> EmptyBrush
+ else -> throw IllegalArgumentException(brush.javaClass.simpleName +
+ "Brush must be either a Color long, LinearGradient or RadialGradient")
+ }
+}
+
+/**
+ * Creates a linear gradient with the provided colors along the given start and end coordinates.
+ * The colors are
+ *
+ * ```
+ * LinearGradient(
+ * 0.0f to Color.Aqua,
+ * 0.3f to Color.Lime,
+ * 1.0f to Color.Fuchsia,
+ * startX = Px.Zero,
+ * startY = Px(50.0f),
+ * endY = Px.Zero,
+ * endY = Px(100.0f)
+ * )
+ * ```
+ */
+fun LinearGradient(
+ vararg colors: Color,
+ startX: Px,
+ startY: Px,
+ endX: Px,
+ endY: Px,
+ tileMode: TileMode = TileMode.clamp
+): LinearGradient {
+ return LinearGradient(
+ colors.asList(),
+ null,
+ startX,
+ startY,
+ endX,
+ endY,
+ tileMode)
+}
+
+/**
+ * Creates a linear gradient with the provided colors along the given start and end coordinates.
+ * The colors are dispersed at the provided offset defined in the [ColorStop]
+ *
+ * ```
+ * LinearGradient(
+ * 0.0f to Color.Aqua,
+ * 0.3f to Color.Lime,
+ * 1.0f to Color.Fuchsia,
+ * startX = Px.Zero,
+ * startY = Px(50.0f),
+ * endY = Px.Zero,
+ * endY = Px(100.0f)
+ * )
+ * ```
+ */
+fun LinearGradient(
+ vararg colorStops: ColorStop,
+ startX: Px,
+ startY: Px,
+ endX: Px,
+ endY: Px,
+ tileMode: TileMode = TileMode.clamp
+): LinearGradient {
+ return LinearGradient(
+ List<Color>(colorStops.size) { i -> colorStops[i].second },
+ List<Float>(colorStops.size) { i -> colorStops[i].first },
+ startX,
+ startY,
+ endX,
+ endY,
+ tileMode)
+}
+
+/**
+ * Creates a radial gradient with the given colors at the provided offset defined in the [ColorStop]
+ * ```
+ * RadialGradient(
+ * 0.0f to Color.Navy,
+ * 0.3f to Color.Olive,
+ * 1.0f to Color.Teal,
+ * centerX = side1 / 2.0f,
+ * centerY = side2 / 2.0f,
+ * radius = side1 / 2.0f,
+ * tileMode = TileMode.repeated
+ * )
+ * ```
+ */
+fun RadialGradient(
+ vararg colorStops: ColorStop,
+ centerX: Float,
+ centerY: Float,
+ radius: Float,
+ tileMode: TileMode = TileMode.clamp
+): RadialGradient {
+ return RadialGradient(
+ List<Color>(colorStops.size) { i -> colorStops[i].second },
+ List<Float>(colorStops.size) { i -> colorStops[i].first },
+ centerX,
+ centerY,
+ radius,
+ tileMode
+ )
+}
+
+/**
+ * Creates a radial gradient with the given colors evenly dispersed within the gradient
+ * ```
+ * RadialGradient(
+ * Color.Navy,
+ * Color.Olive,
+ * Color.Teal,
+ * centerX = side1 / 2.0f,
+ * centerY = side2 / 2.0f,
+ * radius = side1 / 2.0f,
+ * tileMode = TileMode.repeated
+ * )
+ * ```
+ */
+fun RadialGradient(
+ vararg colors: Color,
+ centerX: Float,
+ centerY: Float,
+ radius: Float,
+ tileMode: TileMode = TileMode.clamp
+): RadialGradient {
+ return RadialGradient(colors.asList(), null, centerX, centerY, radius, tileMode)
+}
+
+/**
+ * Creates a vertical gradient with the given colors at the provided offset defined in the [ColorStop]
+ * Ex:
+ * ```
+ * VerticalGradient(
+ * Color.Aqua,
+ * Color.Lime,
+ * Color.Fuchsia,
+ * startY = Px.Zero,
+ * endY = Px(100.0f)
+ * )
+ *
+ * ```
+ */
+fun VerticalGradient(
+ vararg colors: Color,
+ startY: Px,
+ endY: Px,
+ tileMode: TileMode = TileMode.clamp
+): LinearGradient {
+ return LinearGradient(
+ colors.asList(),
+ null,
+ startX = Px.Zero,
+ startY = startY,
+ endX = Px.Zero,
+ endY = endY,
+ tileMode = tileMode)
+}
+
+/**
+ * Creates a vertical gradient with the given colors evenly dispersed within the gradient
+ * Ex:
+ * ```
+ * VerticalGradient(
+ * Color.Aqua,
+ * Color.Lime,
+ * Color.Fuchsia,
+ * startY = Px.Zero,
+ * endY = Px(100.0f)
+ * )
+ * ```
+ */
+fun VerticalGradient(
+ vararg colorStops: ColorStop,
+ startY: Px,
+ endY: Px,
+ tileMode: TileMode = TileMode.clamp
+): LinearGradient {
+ return LinearGradient(
+ List<Color>(colorStops.size) { i -> colorStops[i].second },
+ List<Float>(colorStops.size) { i -> colorStops[i].first },
+ startX = Px.Zero,
+ startY = startY,
+ endX = Px.Zero,
+ endY = endY,
+ tileMode = tileMode
+ )
+}
+
+/**
+ * Creates a horizontal gradient with the given colors evenly dispersed within the gradient
+ *
+ * Ex:
+ * ```
+ * HorizontalGradient(
+ * Color.Aqua,
+ * Color.Lime,
+ * Color.Fuchsia,
+ * startX = Px(10.0f),
+ * endX = Px(20.0f)
+ * )
+ * ```
+ */
+fun HorizontalGradient(
+ vararg colors: Color,
+ startX: Px,
+ endX: Px,
+ tileMode: TileMode = TileMode.clamp
+): LinearGradient {
+ return LinearGradient(
+ colors.asList(),
+ null,
+ startX = startX,
+ startY = Px.Zero,
+ endX = endX,
+ endY = Px.Zero,
+ tileMode = tileMode)
+}
+
+/**
+ * Creates a horizontal gradient with the given colors dispersed at the provided offset defined in the [ColorStop]
+ *
+ * Ex:
+ * ```
+ * HorizontalGradient(
+ * 0.0f to Color.Aqua,
+ * 0.3f to Color.Lime,
+ * 1.0f to Color.Fuchsia,
+ * startX = Px.Zero,
+ * endX = Px(100.0f)
+ * )
+ * ```
+ */
+fun HorizontalGradient(
+ vararg colorStops: ColorStop,
+ startX: Px,
+ endX: Px,
+ tileMode: TileMode = TileMode.clamp
+): Brush {
+ return LinearGradient(
+ List<Color>(colorStops.size) { i -> colorStops[i].second },
+ List<Float>(colorStops.size) { i -> colorStops[i].first },
+ startX = startX,
+ startY = Px.Zero,
+ endX = endX,
+ endY = Px.Zero,
+ tileMode = tileMode
+ )
+}
+
+/**
+ * Brush implementation used to apply a linear gradient on a given [Paint]
+ */
+data class LinearGradient internal constructor(
+ val colors: List<Color>,
+ val stops: List<Float>? = null,
+ val startX: Px,
+ val startY: Px,
+ val endX: Px,
+ val endY: Px,
+ val tileMode: TileMode = TileMode.clamp
+) : Brush {
+
+ override fun applyBrush(p: Paint) {
+ p.shader = Gradient.linear(
+ Offset(startX.value, startY.value),
+ Offset(endX.value, endY.value),
+ colors,
+ stops,
+ tileMode)
+ }
+}
+
+/**
+ * Brush implementation used to apply a radial gradient on a given [Paint]
+ */
+data class RadialGradient internal constructor(
+ private val colors: List<Color>,
+ private val stops: List<Float>? = null,
+ private val centerX: Float,
+ private val centerY: Float,
+ private val radius: Float,
+ private val tileMode: TileMode = TileMode.clamp
+) : Brush {
+
+ override fun applyBrush(p: Paint) {
+ p.shader = Gradient.radial(
+ Offset(centerX, centerY),
+ radius, colors, stops, tileMode, Matrix4())
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathBuilder.kt b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathBuilder.kt
similarity index 95%
rename from ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathBuilder.kt
rename to ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathBuilder.kt
index 52cb119..6c634eb 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathBuilder.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathBuilder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.ui.core.vectorgraphics
+package androidx.ui.graphics.vectorgraphics
class PathBuilder {
@@ -93,7 +93,8 @@
sweepFlag: Float,
x1: Float,
y1: Float
- ) = addNode(PathCommand.ArcTo, horizontalEllipseRadius, verticalEllipseRadius, theta,
+ ) = addNode(
+ PathCommand.ArcTo, horizontalEllipseRadius, verticalEllipseRadius, theta,
largeArcFlag, sweepFlag, x1, y1)
fun arcToRelative(
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathCommand.kt b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathCommand.kt
similarity index 96%
rename from ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathCommand.kt
rename to ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathCommand.kt
index 29e3bde..b8e9f1d 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathCommand.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathCommand.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.ui.core.vectorgraphics
+package androidx.ui.graphics.vectorgraphics
enum class PathCommand(private val mKey: Char) {
@@ -49,9 +49,7 @@
* Return the corresponding PathCommand for the given character key if it exists.
* If the key is unknown then IllegalArgumentException is thrown
* @return PathCommand that matches the key
- * @throws IllegalArgumentException if the key is invalid
*/
-@Throws(IllegalArgumentException::class)
fun Char.toPathCommand(): PathCommand = when (this) {
RelativeCloseKey -> PathCommand.RelativeClose
CloseKey -> PathCommand.Close
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathNode.kt b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathNode.kt
similarity index 97%
rename from ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathNode.kt
rename to ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathNode.kt
index 4d0accc..57833ca 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathNode.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathNode.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.ui.core.vectorgraphics
+package androidx.ui.graphics.vectorgraphics
import kotlin.text.StringBuilder
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathParser.kt b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathParser.kt
similarity index 97%
rename from ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathParser.kt
rename to ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathParser.kt
index 7724ccf..553eb32 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/PathParser.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/PathParser.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.ui.core.vectorgraphics
+package androidx.ui.graphics.vectorgraphics
import android.util.Log
import androidx.ui.painting.Path
@@ -51,7 +51,11 @@
private val segmentPoint = PathPoint()
private val reflectiveCtrlPoint = PathPoint()
- @Throws(java.lang.IllegalArgumentException::class, NumberFormatException::class)
+ /**
+ * Parses the path string to create a collection of PathNode instances with their corresponding
+ * arguments
+ * throws an IllegalArgumentException or NumberFormatException if the parameters are invalid
+ */
fun parsePathString(pathData: String): PathParser {
nodes.clear()
@@ -348,7 +352,9 @@
}
private fun reflectiveQuadTo(prevCmd: PathCommand, target: Path, args: FloatArray) {
- forEachPathArg(args, NUM_REFLECTIVE_QUAD_TO_ARGS) { index ->
+ forEachPathArg(args,
+ NUM_REFLECTIVE_QUAD_TO_ARGS
+ ) { index ->
val x1 = args[index]
val y1 = args[index + 1]
if (prevCmd.isQuad()) {
@@ -368,7 +374,9 @@
}
private fun relativeReflectiveQuadTo(prevCmd: PathCommand, target: Path, args: FloatArray) {
- forEachPathArg(args, NUM_REFLECTIVE_QUAD_TO_ARGS) { index ->
+ forEachPathArg(args,
+ NUM_REFLECTIVE_QUAD_TO_ARGS
+ ) { index ->
val x1 = args[index]
val y1 = args[index + 1]
if (prevCmd.isQuad()) {
@@ -448,7 +456,9 @@
}
private fun reflectiveCurveTo(prevCmd: PathCommand, target: Path, args: FloatArray) {
- forEachPathArg(args, NUM_REFLECTIVE_CURVE_TO_ARGS) { index ->
+ forEachPathArg(args,
+ NUM_REFLECTIVE_CURVE_TO_ARGS
+ ) { index ->
val x1 = args[index]
val y1 = args[index + 1]
val x2 = args[index + 2]
@@ -472,7 +482,9 @@
}
private fun relativeReflectiveCurveTo(prevCmd: PathCommand, target: Path, args: FloatArray) {
- forEachPathArg(args, NUM_REFLECTIVE_CURVE_TO_ARGS) { index ->
+ forEachPathArg(args,
+ NUM_REFLECTIVE_CURVE_TO_ARGS
+ ) { index ->
val x1 = args[index]
val y1 = args[index + 1]
val x2 = args[index + 2]
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Vector.kt b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/Vector.kt
similarity index 67%
rename from ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Vector.kt
rename to ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/Vector.kt
index 9fd6bb1..920b59e 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Vector.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/graphics/vectorgraphics/Vector.kt
@@ -14,37 +14,33 @@
* limitations under the License.
*/
-package androidx.ui.core.vectorgraphics
+package androidx.ui.graphics.vectorgraphics
-import android.graphics.ColorFilter
import android.graphics.Matrix
-import android.graphics.PixelFormat
-import android.graphics.drawable.Drawable
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
import androidx.ui.engine.geometry.Offset
import androidx.ui.painting.Canvas
import androidx.ui.painting.Image
import androidx.ui.painting.Paint
import androidx.ui.painting.PaintingStyle
+import androidx.ui.painting.Path
import androidx.ui.painting.StrokeCap
import androidx.ui.painting.StrokeJoin
import androidx.compose.Children
import androidx.compose.Composable
import androidx.compose.Emittable
import androidx.compose.composer
+import androidx.ui.core.Px
import androidx.ui.painting.withSave
-import androidx.ui.painting.Path as PaintingPath
+import kotlin.math.ceil
const val DefaultGroupName = ""
-const val DefaultRotate = 0.0f
+const val DefaultRotation = 0.0f
const val DefaultPivotX = 0.0f
const val DefaultPivotY = 0.0f
const val DefaultScaleX = 1.0f
const val DefaultScaleY = 1.0f
-const val DefaultTranslateX = 0.0f
-const val DefaultTranslateY = 0.0f
+const val DefaultTranslationX = 0.0f
+const val DefaultTranslationY = 0.0f
val EmptyPath = emptyArray<PathNode>()
@@ -79,96 +75,23 @@
PathParser().parsePathString(pathStr).toNodes()
}
-@Composable
-fun vector(
- name: String = "",
- viewportWidth: Float,
- viewportHeight: Float,
- defaultWidth: Float = viewportWidth,
- defaultHeight: Float = viewportHeight,
- @Children children: @Composable() () -> Unit
-) {
- <Vector name defaultWidth defaultHeight viewportWidth viewportHeight>
- children()
- </Vector>
-}
-
-@Composable
-fun group(
- name: String = DefaultGroupName,
- rotate: Float = DefaultRotate,
- pivotX: Float = DefaultPivotX,
- pivotY: Float = DefaultPivotY,
- scaleX: Float = DefaultScaleX,
- scaleY: Float = DefaultScaleY,
- translateX: Float = DefaultTranslateX,
- translateY: Float = DefaultTranslateY,
- clipPathData: PathData = EmptyPath,
- @Children childNodes: @Composable() () -> Unit
-) {
-
- val clipPathNodes = createPath(clipPathData)
- <Group
- name
- rotate
- pivotX
- pivotY
- scaleX
- scaleY
- translateX
- translateY
- clipPathNodes>
- childNodes()
- </Group>
-}
-
-@Composable
-fun path(
- pathData: PathData,
- name: String = DefaultPathName,
- fill: BrushType = EmptyBrush,
- fillAlpha: Float = DefaultAlpha,
- stroke: BrushType = EmptyBrush,
- strokeAlpha: Float = DefaultAlpha,
- strokeLineWidth: Float = DefaultStrokeLineWidth,
- strokeLineCap: StrokeCap = DefaultStrokeLineCap,
- strokeLineJoin: StrokeJoin = DefaultStrokeLineJoin,
- strokeLineMiter: Float = DefaultStrokeLineMiter
-) {
- val pathNodes = createPath(pathData)
- val fillBrush: Brush = obtainBrush(fill)
- val strokeBrush: Brush = obtainBrush(stroke)
-
- <Path
- name
- pathNodes
- fill=fillBrush
- fillAlpha
- stroke=strokeBrush
- strokeAlpha
- strokeLineWidth
- strokeLineCap
- strokeLineJoin
- strokeLineMiter />
-}
-
-private sealed class VNode {
+sealed class VNode {
abstract fun draw(canvas: Canvas)
}
-private class Vector(
+class VectorComponent(
val name: String = "",
- val viewportWidth: Float,
- val viewportHeight: Float,
- val defaultWidth: Float = viewportWidth,
- val defaultHeight: Float = viewportHeight
-) : VNode(), Emittable {
+ var viewportWidth: Float,
+ var viewportHeight: Float,
+ var defaultWidth: Px,
+ var defaultHeight: Px
+) : VNode() {
- private val root = Group(this@Vector.name).apply {
+ val root = GroupComponent(this@VectorComponent.name).apply {
pivotX = 0.0f
pivotY = 0.0f
- scaleX = defaultWidth / viewportWidth
- scaleY = defaultHeight / viewportHeight
+ scaleX = defaultWidth.value / viewportWidth
+ scaleY = defaultHeight.value / viewportHeight
}
/**
@@ -185,8 +108,8 @@
var targetImage = cachedImage
if (targetImage == null) {
targetImage = Image(
- kotlin.math.ceil(defaultWidth).toInt(),
- kotlin.math.ceil(defaultHeight).toInt()
+ ceil(defaultWidth.value).toInt(),
+ ceil(defaultHeight.value).toInt()
)
cachedImage = targetImage
root.draw(Canvas(targetImage))
@@ -204,21 +127,9 @@
append("\tviewportHeight: ").append(viewportHeight).append("\n")
}
}
-
- override fun emitInsertAt(index: Int, instance: Emittable) {
- root.emitInsertAt(index, instance)
- }
-
- override fun emitMove(from: Int, to: Int, count: Int) {
- root.emitMove(from, to, count)
- }
-
- override fun emitRemoveAt(index: Int, count: Int) {
- root.emitRemoveAt(index, count)
- }
}
-private class Path(val name: String) : VNode(), Emittable {
+class PathComponent(val name: String) : VNode() {
var fill: Brush = EmptyBrush
set(value) {
@@ -292,7 +203,7 @@
private var isPathDirty = true
- private val path = PaintingPath()
+ private val path = Path()
private var fillPaint: Paint? = null
private var strokePaint: Paint? = null
@@ -366,24 +277,12 @@
}
}
- override fun emitInsertAt(index: Int, instance: Emittable) {
- throw IllegalArgumentException("Unable to insert Emittable into Path")
- }
-
- override fun emitMove(from: Int, to: Int, count: Int) {
- throw IllegalArgumentException("Unable to move Emittable within Path")
- }
-
- override fun emitRemoveAt(index: Int, count: Int) {
- throw IllegalArgumentException("Unable to remove Emittable from Path")
- }
-
override fun toString(): String {
return path.toString()
}
}
-private class Group(val name: String = DefaultGroupName) : VNode(), Emittable {
+class GroupComponent(val name: String = DefaultGroupName) : VNode() {
private var groupMatrix: Matrix? = null
@@ -400,7 +299,7 @@
private var isClipPathDirty = true
- private var clipPath: PaintingPath? = null
+ private var clipPath: Path? = null
private var parser: PathParser? = null
private fun updateClipPath() {
@@ -415,7 +314,7 @@
var targetClip = clipPath
if (targetClip == null) {
- targetClip = PaintingPath()
+ targetClip = Path()
clipPath = targetClip
} else {
targetClip.reset()
@@ -425,7 +324,7 @@
}
}
- var rotate: Float = DefaultRotate
+ var rotation: Float = DefaultRotation
set(value) {
field = value
isMatrixDirty = true
@@ -455,13 +354,13 @@
isMatrixDirty = true
}
- var translateX: Float = DefaultTranslateX
+ var translationX: Float = DefaultTranslationX
set(value) {
field = value
isMatrixDirty = true
}
- var translateY: Float = DefaultTranslateY
+ var translationY: Float = DefaultTranslationY
set(value) {
field = value
isMatrixDirty = true
@@ -482,23 +381,21 @@
reset()
postTranslate(-pivotX, -pivotY)
postScale(scaleX, scaleY)
- postRotate(rotate, 0f, 0f)
- postTranslate(translateX + pivotX,
- translateY + pivotY)
+ postRotate(rotation, 0f, 0f)
+ postTranslate(translationX + pivotX,
+ translationY + pivotY)
}
}
- override fun emitInsertAt(index: Int, instance: Emittable) {
- if (instance is VNode) {
- if (index < size) {
- children[index] = instance
- } else {
- children.add(instance)
- }
+ fun insertAt(index: Int, instance: VNode) {
+ if (index < size) {
+ children[index] = instance
+ } else {
+ children.add(instance)
}
}
- override fun emitMove(from: Int, to: Int, count: Int) {
+ fun move(from: Int, to: Int, count: Int) {
if (from > to) {
var current = to
repeat(count) {
@@ -516,7 +413,7 @@
}
}
- override fun emitRemoveAt(index: Int, count: Int) {
+ fun remove(index: Int, count: Int) {
repeat(count) {
children.removeAt(index)
}
@@ -563,7 +460,7 @@
}
}
-private fun createPath(pathData: PathData): Array<PathNode> {
+fun createPath(pathData: PathData): Array<PathNode> {
@Suppress("UNCHECKED_CAST")
return when (pathData) {
is Array<*> -> pathData as Array<PathNode>
@@ -575,39 +472,4 @@
}
else -> throw IllegalArgumentException("Must be array of PathNodes or PathDelegate")
}
-}
-
-// Temporary glue logic to wrap a Vector asset into an ImageView
-fun adoptVectorGraphic(parent: Any?, child: Any?): View? {
- return if (parent is ViewGroup && child is Vector) {
- val imageView = ImageView(parent.context)
- imageView.scaleType = ImageView.ScaleType.FIT_CENTER
- imageView.setImageDrawable(VectorGraphicDrawable(child))
- imageView
- } else {
- null
- }
-}
-
-private class VectorGraphicDrawable(private val vector: Vector) : Drawable() {
-
- override fun getIntrinsicWidth(): Int = Math.round(vector.defaultWidth)
-
- override fun getIntrinsicHeight(): Int = Math.round(vector.defaultHeight)
-
- override fun draw(canvas: android.graphics.Canvas) {
- vector.draw(androidx.ui.painting.Canvas(canvas))
- }
-
- override fun setAlpha(alpha: Int) {
- // TODO support modifying alpha for root of tree down to each node
- TODO("not implemented")
- }
-
- override fun getOpacity(): Int = PixelFormat.UNKNOWN
-
- override fun setColorFilter(colorFilter: ColorFilter?) {
- // TODO support color tinting
- TODO("not implemented")
- }
-}
+}
\ No newline at end of file
diff --git a/ui/ui-core/src/main/java/androidx/ui/painting/Gradient.kt b/ui/ui-core/src/main/java/androidx/ui/painting/Gradient.kt
index 5094d42..03266c7 100644
--- a/ui/ui-core/src/main/java/androidx/ui/painting/Gradient.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/painting/Gradient.kt
@@ -106,25 +106,19 @@
color: List<Color>,
colorStops: List<Float>?,
tileMode: TileMode = TileMode.clamp,
- @Suppress("UNUSED_PARAMETER") matrix4: Matrix4,
- focal: Offset?,
- focalRadius: Float
+ @Suppress("UNUSED_PARAMETER") matrix4: Matrix4
): Gradient {
_validateColorStops(color, colorStops)
- if (focal == null || (focal == center && focalRadius == 0.0f)) {
- TODO("Migration/njawad: add focal support to RadialGradient in framework")
- } else {
- // TODO(Migration/njawad use matrix parameter in creation of RadialGradient)
- val radial = android.graphics.RadialGradient(
- center.dx,
- center.dy,
- radius,
- toIntArray(color),
- toFloatArray(colorStops),
- toFrameworkTileMode(tileMode)
- )
- return Gradient(radial)
- }
+ // TODO(Migration/njawad use matrix parameter in creation of RadialGradient)
+ val radial = android.graphics.RadialGradient(
+ center.dx,
+ center.dy,
+ radius,
+ toIntArray(color),
+ toFloatArray(colorStops),
+ toFrameworkTileMode(tileMode)
+ )
+ return Gradient(radial)
}
/**
@@ -202,9 +196,9 @@
private fun _validateColorStops(colors: List<Color>, colorStops: List<Float>?) {
if (colorStops == null) {
- if (colors.size != 2) {
+ if (colors.size < 2) {
throw IllegalArgumentException(
- "colors must have length 2 if colorStops " +
+ "colors must have length of at least 2 if colorStops " +
"is omitted."
)
}
diff --git a/ui/ui-core/src/main/java/androidx/ui/testutils/PointerInputTestUtil.kt b/ui/ui-core/src/main/java/androidx/ui/testutils/PointerInputTestUtil.kt
index 335e2c9..e00bfbf 100644
--- a/ui/ui-core/src/main/java/androidx/ui/testutils/PointerInputTestUtil.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/testutils/PointerInputTestUtil.kt
@@ -96,3 +96,14 @@
PointerEventPass.PostUp,
PointerEventPass.PostDown
)
+
+fun PointerInputHandler.invokeOverAllPasses(
+ vararg pointerInputChanges: PointerInputChange
+) = invokeOverPasses(
+ pointerInputChanges.toList(),
+ PointerEventPass.InitialDown,
+ PointerEventPass.PreUp,
+ PointerEventPass.PreDown,
+ PointerEventPass.PostUp,
+ PointerEventPass.PostDown
+)
\ No newline at end of file
diff --git a/ui/ui-foundation/api/1.0.0-alpha01.txt b/ui/ui-foundation/api/1.0.0-alpha01.txt
index ec7e422..f0ceb44 100644
--- a/ui/ui-foundation/api/1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/1.0.0-alpha01.txt
@@ -8,7 +8,7 @@
public final class ColoredRectKt {
ctor public ColoredRectKt();
- method public static void ColoredRect(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
+ method public static void ColoredRect(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
method public static void ColoredRect(androidx.ui.graphics.Color color, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
}
@@ -132,7 +132,7 @@
public final class DrawShapeKt {
ctor public DrawShapeKt();
method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.graphics.Color color);
- method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.core.vectorgraphics.Brush brush);
+ method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.graphics.vectorgraphics.Brush brush);
}
public final class GenericShape implements androidx.ui.engine.geometry.Shape {
@@ -151,11 +151,11 @@
package androidx.ui.foundation.shape.border {
public final class Border {
- ctor public Border(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
- method public androidx.ui.core.vectorgraphics.Brush component1();
+ ctor public Border(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
+ method public androidx.ui.graphics.vectorgraphics.Brush component1();
method public androidx.ui.core.Dp component2();
- method public androidx.ui.foundation.shape.border.Border copy(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
- method public androidx.ui.core.vectorgraphics.Brush getBrush();
+ method public androidx.ui.foundation.shape.border.Border copy(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
+ method public androidx.ui.graphics.vectorgraphics.Brush getBrush();
method public androidx.ui.core.Dp getWidth();
}
diff --git a/ui/ui-foundation/api/current.txt b/ui/ui-foundation/api/current.txt
index ec7e422..f0ceb44 100644
--- a/ui/ui-foundation/api/current.txt
+++ b/ui/ui-foundation/api/current.txt
@@ -8,7 +8,7 @@
public final class ColoredRectKt {
ctor public ColoredRectKt();
- method public static void ColoredRect(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
+ method public static void ColoredRect(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
method public static void ColoredRect(androidx.ui.graphics.Color color, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
}
@@ -132,7 +132,7 @@
public final class DrawShapeKt {
ctor public DrawShapeKt();
method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.graphics.Color color);
- method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.core.vectorgraphics.Brush brush);
+ method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.graphics.vectorgraphics.Brush brush);
}
public final class GenericShape implements androidx.ui.engine.geometry.Shape {
@@ -151,11 +151,11 @@
package androidx.ui.foundation.shape.border {
public final class Border {
- ctor public Border(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
- method public androidx.ui.core.vectorgraphics.Brush component1();
+ ctor public Border(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
+ method public androidx.ui.graphics.vectorgraphics.Brush component1();
method public androidx.ui.core.Dp component2();
- method public androidx.ui.foundation.shape.border.Border copy(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
- method public androidx.ui.core.vectorgraphics.Brush getBrush();
+ method public androidx.ui.foundation.shape.border.Border copy(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
+ method public androidx.ui.graphics.vectorgraphics.Brush getBrush();
method public androidx.ui.core.Dp getWidth();
}
diff --git a/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt b/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
index ec7e422..f0ceb44 100644
--- a/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
@@ -8,7 +8,7 @@
public final class ColoredRectKt {
ctor public ColoredRectKt();
- method public static void ColoredRect(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
+ method public static void ColoredRect(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
method public static void ColoredRect(androidx.ui.graphics.Color color, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
}
@@ -132,7 +132,7 @@
public final class DrawShapeKt {
ctor public DrawShapeKt();
method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.graphics.Color color);
- method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.core.vectorgraphics.Brush brush);
+ method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.graphics.vectorgraphics.Brush brush);
}
public final class GenericShape implements androidx.ui.engine.geometry.Shape {
@@ -151,11 +151,11 @@
package androidx.ui.foundation.shape.border {
public final class Border {
- ctor public Border(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
- method public androidx.ui.core.vectorgraphics.Brush component1();
+ ctor public Border(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
+ method public androidx.ui.graphics.vectorgraphics.Brush component1();
method public androidx.ui.core.Dp component2();
- method public androidx.ui.foundation.shape.border.Border copy(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
- method public androidx.ui.core.vectorgraphics.Brush getBrush();
+ method public androidx.ui.foundation.shape.border.Border copy(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
+ method public androidx.ui.graphics.vectorgraphics.Brush getBrush();
method public androidx.ui.core.Dp getWidth();
}
diff --git a/ui/ui-foundation/api/restricted_current.txt b/ui/ui-foundation/api/restricted_current.txt
index ec7e422..f0ceb44 100644
--- a/ui/ui-foundation/api/restricted_current.txt
+++ b/ui/ui-foundation/api/restricted_current.txt
@@ -8,7 +8,7 @@
public final class ColoredRectKt {
ctor public ColoredRectKt();
- method public static void ColoredRect(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
+ method public static void ColoredRect(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
method public static void ColoredRect(androidx.ui.graphics.Color color, androidx.ui.core.Dp? width = null, androidx.ui.core.Dp? height = null);
}
@@ -132,7 +132,7 @@
public final class DrawShapeKt {
ctor public DrawShapeKt();
method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.graphics.Color color);
- method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.core.vectorgraphics.Brush brush);
+ method public static void DrawShape(androidx.ui.engine.geometry.Shape shape, androidx.ui.graphics.vectorgraphics.Brush brush);
}
public final class GenericShape implements androidx.ui.engine.geometry.Shape {
@@ -151,11 +151,11 @@
package androidx.ui.foundation.shape.border {
public final class Border {
- ctor public Border(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
- method public androidx.ui.core.vectorgraphics.Brush component1();
+ ctor public Border(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
+ method public androidx.ui.graphics.vectorgraphics.Brush component1();
method public androidx.ui.core.Dp component2();
- method public androidx.ui.foundation.shape.border.Border copy(androidx.ui.core.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
- method public androidx.ui.core.vectorgraphics.Brush getBrush();
+ method public androidx.ui.foundation.shape.border.Border copy(androidx.ui.graphics.vectorgraphics.Brush brush, androidx.ui.core.Dp width);
+ method public androidx.ui.graphics.vectorgraphics.Brush getBrush();
method public androidx.ui.core.Dp getWidth();
}
diff --git a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ColoredRectSamples.kt b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ColoredRectSamples.kt
index d90e8ac3..5a688a7 100644
--- a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ColoredRectSamples.kt
+++ b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ColoredRectSamples.kt
@@ -20,9 +20,9 @@
import androidx.compose.Composable
import androidx.compose.composer
import androidx.ui.core.dp
-import androidx.ui.core.vectorgraphics.SolidColor
import androidx.ui.foundation.ColoredRect
import androidx.ui.graphics.Color
+import androidx.ui.graphics.vectorgraphics.SolidColor
@Sampled
@Composable
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
index 1730ce1..bee68c7 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
@@ -40,7 +40,7 @@
fun Clickable(
onClick: (() -> Unit)? = null,
consumeDownOnStart: Boolean = false,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Semantics(
button = true,
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/ColoredRect.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/ColoredRect.kt
index 757de7f..ab71fd2 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/ColoredRect.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/ColoredRect.kt
@@ -20,15 +20,12 @@
import androidx.compose.composer
import androidx.compose.trace
import androidx.ui.core.Dp
-import androidx.ui.core.Draw
-import androidx.ui.core.toRect
-import androidx.ui.core.vectorgraphics.Brush
-import androidx.ui.core.vectorgraphics.SolidColor
import androidx.ui.foundation.shape.DrawShape
import androidx.ui.foundation.shape.RectangleShape
import androidx.ui.graphics.Color
+import androidx.ui.graphics.vectorgraphics.Brush
+import androidx.ui.graphics.vectorgraphics.SolidColor
import androidx.ui.layout.Container
-import androidx.ui.painting.Paint
/**
* Component that represents a rectangle painted with the specified [Brush].
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/DeterminateProgressIndicator.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/DeterminateProgressIndicator.kt
index 1182bfe..a40b834 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/DeterminateProgressIndicator.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/DeterminateProgressIndicator.kt
@@ -36,7 +36,7 @@
@Composable
fun DeterminateProgressIndicator(
@FloatRange(from = 0.0, to = 1.0) progress: Float,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
if (progress !in 0f..1f) {
throw IllegalArgumentException("Progress must be between 0.0 and 1.0")
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
index 8ccc5b6..b6ad191 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
@@ -47,7 +47,7 @@
* @param children The content to be displayed inside the dialog.
*/
@Composable
-fun Dialog(onCloseRequest: () -> Unit, @Children children: @Composable() () -> Unit) {
+fun Dialog(onCloseRequest: () -> Unit, children: @Composable() () -> Unit) {
val context = +ambient(ContextAmbient)
val dialog = +memo { DialogWrapper(context, onCloseRequest) }
@@ -72,7 +72,7 @@
setContentView(frameLayout)
}
- fun setContent(@Children children: @Composable() () -> Unit) {
+ fun setContent(children: @Composable() () -> Unit) {
frameLayout.setContent(children)
}
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/MutuallyExclusiveSetItem.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/MutuallyExclusiveSetItem.kt
index af44074..a199158 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/MutuallyExclusiveSetItem.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/MutuallyExclusiveSetItem.kt
@@ -37,7 +37,7 @@
fun MutuallyExclusiveSetItem(
selected: Boolean,
onClick: () -> Unit,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
// TODO: when semantics can be merged, we should make this use Clickable internally rather
// than duplicating logic
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
index 6531828..a9f6f7b 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
@@ -28,7 +28,7 @@
fun Toggleable(
value: ToggleableState = ToggleableState.Checked,
onToggle: (() -> Unit)? = null,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val actions = if (onToggle != null) {
listOf(SemanticsAction(SemanticsActionType.Tap, onToggle))
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/DrawShape.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/DrawShape.kt
index 6a093bb..1cd22be 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/DrawShape.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/DrawShape.kt
@@ -22,12 +22,12 @@
import androidx.compose.unaryPlus
import androidx.ui.core.Draw
import androidx.ui.core.PxSize
-import androidx.ui.core.vectorgraphics.Brush
-import androidx.ui.core.vectorgraphics.SolidColor
import androidx.ui.engine.geometry.Outline
import androidx.ui.engine.geometry.Shape
import androidx.ui.engine.geometry.drawOutline
import androidx.ui.graphics.Color
+import androidx.ui.graphics.vectorgraphics.Brush
+import androidx.ui.graphics.vectorgraphics.SolidColor
import androidx.ui.painting.Paint
/**
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/border/Border.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/border/Border.kt
index 3a66465..62f871a 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/border/Border.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/border/Border.kt
@@ -17,9 +17,9 @@
package androidx.ui.foundation.shape.border
import androidx.ui.core.Dp
-import androidx.ui.core.vectorgraphics.Brush
-import androidx.ui.core.vectorgraphics.SolidColor
import androidx.ui.graphics.Color
+import androidx.ui.graphics.vectorgraphics.Brush
+import androidx.ui.graphics.vectorgraphics.SolidColor
/**
* A border of a shape.
diff --git a/ui/ui-framework/api/1.0.0-alpha01.txt b/ui/ui-framework/api/1.0.0-alpha01.txt
index 224764c..01e28d9 100644
--- a/ui/ui-framework/api/1.0.0-alpha01.txt
+++ b/ui/ui-framework/api/1.0.0-alpha01.txt
@@ -7,7 +7,7 @@
}
public final class ComplexLayoutReceiver {
- method public void layout(kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutBlockReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
+ method public void layout(kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutBlockReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
method public void maxIntrinsicHeight(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> maxIntrinsicHeightBlock);
method public void maxIntrinsicWidth(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> maxIntrinsicWidthBlock);
method public void minIntrinsicHeight(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> minIntrinsicHeightBlock);
@@ -43,7 +43,7 @@
public final class LayoutBlockReceiver implements androidx.ui.core.DensityReceiver {
method public androidx.ui.core.Density getDensity();
- method public void layoutResult(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
+ method public androidx.ui.core.LayoutResult layoutResult(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
method public androidx.ui.core.IntPx maxIntrinsicHeight(androidx.ui.core.Measurable, androidx.ui.core.IntPx w);
method public androidx.ui.core.IntPx maxIntrinsicWidth(androidx.ui.core.Measurable, androidx.ui.core.IntPx h);
method public androidx.ui.core.Placeable measure(androidx.ui.core.Measurable, androidx.ui.core.Constraints constraints);
@@ -67,8 +67,8 @@
public final class LayoutKt {
ctor public LayoutKt();
method public static void ComplexLayout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function1<? super androidx.ui.core.ComplexLayoutReceiver,kotlin.Unit> block);
- method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
- method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit>![] childrenArray, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
+ method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
+ method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit>![] childrenArray, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
method public static void OnChildPositioned(kotlin.jvm.functions.Function1<? super androidx.ui.core.LayoutCoordinates,kotlin.Unit> onPositioned, kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void OnPositioned(kotlin.jvm.functions.Function1<? super androidx.ui.core.LayoutCoordinates,kotlin.Unit> onPositioned);
method public static void WithConstraints(kotlin.jvm.functions.Function1<? super androidx.ui.core.Constraints,kotlin.Unit> children);
@@ -77,11 +77,18 @@
public final class LayoutReceiver implements androidx.ui.core.DensityReceiver {
method public operator java.util.List<androidx.ui.core.Measurable> get(java.util.List<? extends androidx.ui.core.Measurable>, kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public androidx.ui.core.Density getDensity();
- method public void layout(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
+ method public androidx.ui.core.LayoutResult layout(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
method public androidx.ui.core.Placeable measure(androidx.ui.core.Measurable, androidx.ui.core.Constraints constraints);
property public androidx.ui.core.Density density;
}
+ public final class LayoutResult {
+ field public static final androidx.ui.core.LayoutResult.Companion! Companion;
+ }
+
+ public static final class LayoutResult.Companion {
+ }
+
public interface Measurable {
method public Object? getParentData();
property public abstract Object? parentData;
@@ -148,7 +155,7 @@
public final class TextFieldKt {
ctor public TextFieldKt();
- method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
+ method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function0<kotlin.Unit> onFocus = {}, kotlin.jvm.functions.Function0<kotlin.Unit> onBlur = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
}
public final class TextKt {
@@ -241,6 +248,11 @@
method public static androidx.ui.core.Duration getZoomControlsTimeout();
}
+ public final class DoubleTapGestureDetectorKt {
+ ctor public DoubleTapGestureDetectorKt();
+ method public static void DoubleTapGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit> onDoubleTap, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ }
+
public final class DragGestureDetectorKt {
ctor public DragGestureDetectorKt();
method public static void DragGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null, androidx.ui.core.gesture.DragObserver? dragObserver = null, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -334,143 +346,74 @@
package androidx.ui.core.vectorgraphics {
- public interface Brush {
- method public void applyBrush(androidx.ui.painting.Paint p);
+ public final class VectorAsset {
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public androidx.ui.core.vectorgraphics.VectorGroup getRoot();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
}
- public final class BrushKt {
- ctor public BrushKt();
- method public static androidx.ui.core.vectorgraphics.Brush getEmptyBrush();
- method public static androidx.ui.core.vectorgraphics.Brush obtainBrush(Object? brush);
+ public final class VectorAssetBuilder {
+ ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder addPath(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+ method public androidx.ui.core.vectorgraphics.VectorAsset build();
+ method public void ensureNotConsumed();
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder popGroup();
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder pushGroup(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath);
}
- public final class LinearGradient implements androidx.ui.core.vectorgraphics.Brush {
- ctor public LinearGradient(kotlin.Pair<androidx.ui.graphics.Color,java.lang.Float>![] colorStops, float startX, float startY, float endX, float endY, androidx.ui.painting.TileMode tileMode);
- method public void applyBrush(androidx.ui.painting.Paint p);
- method public float getEndX();
- method public float getEndY();
- method public float getStartX();
- method public float getStartY();
- method public androidx.ui.painting.TileMode getTileMode();
+ public final class VectorAssetKt {
+ ctor public VectorAssetKt();
+ method public static void DrawVector(androidx.ui.core.vectorgraphics.VectorAsset vectorImage);
}
- public final class PathBuilder {
- ctor public PathBuilder();
- method public androidx.ui.core.vectorgraphics.PathBuilder arcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder arcToRelative(float a, float b, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder close();
- method public androidx.ui.core.vectorgraphics.PathBuilder curveTo(float x1, float y1, float x2, float y2, float x3, float y3);
- method public androidx.ui.core.vectorgraphics.PathBuilder curveToRelative(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
- method public androidx.ui.core.vectorgraphics.PathNode![] getNodes();
- method public androidx.ui.core.vectorgraphics.PathBuilder horizontalLineTo(float x);
- method public androidx.ui.core.vectorgraphics.PathBuilder horizontalLineToRelative(float x);
- method public androidx.ui.core.vectorgraphics.PathBuilder lineTo(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder lineToRelative(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder moveTo(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder moveToRelative(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder quadTo(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder quadToRelative(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveCurveTo(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveCurveToRelative(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveQuadTo(float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveQuadToRelative(float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder verticalLineTo(float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder verticalLineToRelative(float y);
+ public final class VectorComposeKt {
+ ctor public VectorComposeKt();
+ method public static void DrawVector(float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth = Px(viewportWidth), androidx.ui.core.Px defaultHeight = Px(viewportHeight), String name = "", kotlin.jvm.functions.Function1<? super androidx.ui.vector.VectorScope,kotlin.Unit> children);
+ method public static void Group(androidx.ui.vector.VectorScope, String name = "", float rotation = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function1<? super androidx.ui.vector.VectorScope,kotlin.Unit> children);
+ method public static void Path(androidx.ui.vector.VectorScope, Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ArcTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand Close;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand CurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand LineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand MoveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand QuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand VerticalLineTo;
+ public final class VectorGroup extends androidx.ui.core.vectorgraphics.VectorNode implements java.lang.Iterable<androidx.ui.core.vectorgraphics.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, Object? clipPathData);
+ ctor public VectorGroup();
+ method public operator androidx.ui.core.vectorgraphics.VectorNode get(int index);
+ method public Object? getClipPathData();
+ method public String getName();
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSize();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public java.util.Iterator<androidx.ui.core.vectorgraphics.VectorNode> iterator();
+ property public final int size;
}
- public final class PathCommandKt {
- ctor public PathCommandKt();
- method public static androidx.ui.core.vectorgraphics.PathCommand toPathCommand(char) throws java.lang.IllegalArgumentException;
+ public abstract sealed class VectorNode {
}
- public final class PathDelegate {
- ctor public PathDelegate(kotlin.jvm.functions.Function1<? super androidx.ui.core.vectorgraphics.PathBuilder,kotlin.Unit> delegate);
- method public kotlin.jvm.functions.Function1<androidx.ui.core.vectorgraphics.PathBuilder,kotlin.Unit> getDelegate();
- }
-
- public final class PathNode {
- ctor public PathNode(androidx.ui.core.vectorgraphics.PathCommand command, float[] args);
- method public androidx.ui.core.vectorgraphics.PathCommand component1();
- method public float[] component2();
- method public androidx.ui.core.vectorgraphics.PathNode copy(androidx.ui.core.vectorgraphics.PathCommand command, float[] args);
- method public float[] getArgs();
- method public androidx.ui.core.vectorgraphics.PathCommand getCommand();
- }
-
- public final class PathNodeKt {
- ctor public PathNodeKt();
- method public static operator StringBuilder plus(StringBuilder, androidx.ui.core.vectorgraphics.PathNode node);
- }
-
- public final class PathParser {
- ctor public PathParser();
- method public androidx.ui.core.vectorgraphics.PathParser addPathNodes(androidx.ui.core.vectorgraphics.PathNode![] nodes);
- method public void clear();
- method public androidx.ui.core.vectorgraphics.PathParser parsePathString(String pathData) throws java.lang.IllegalArgumentException, java.lang.NumberFormatException;
- method public androidx.ui.core.vectorgraphics.PathNode![] toNodes();
- method public androidx.ui.painting.Path toPath(androidx.ui.painting.Path target = Path());
- }
-
- public final class PathParserKt {
- ctor public PathParserKt();
- }
-
- public final class RadialGradient implements androidx.ui.core.vectorgraphics.Brush {
- ctor public RadialGradient(kotlin.Pair<androidx.ui.graphics.Color,java.lang.Float>![] colorStops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode);
- method public void applyBrush(androidx.ui.painting.Paint p);
- }
-
- public final class SolidColor implements androidx.ui.core.vectorgraphics.Brush {
- ctor public SolidColor(androidx.ui.graphics.Color value);
- method public void applyBrush(androidx.ui.painting.Paint p);
- }
-
- public final class VectorKt {
- ctor public VectorKt();
- method public static androidx.ui.core.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
- method public static android.view.View? adoptVectorGraphic(Object? parent, Object? child);
- method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
- method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
- method public static androidx.ui.core.vectorgraphics.PathNode![] getEmptyPath();
- method public static void group(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translateX = 0.0f, float translateY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
- method public static void path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
- method public static void vector(String name = "", float viewportWidth, float viewportHeight, float defaultWidth = viewportWidth, float defaultHeight = viewportHeight, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- field public static final float DefaultAlpha = 1.0f;
- field public static final String DefaultGroupName = "";
- field public static final String DefaultPathName = "";
- field public static final float DefaultPivotX = 0.0f;
- field public static final float DefaultPivotY = 0.0f;
- field public static final float DefaultRotate = 0.0f;
- field public static final float DefaultScaleX = 1.0f;
- field public static final float DefaultScaleY = 1.0f;
- field public static final float DefaultStrokeLineMiter = 4.0f;
- field public static final float DefaultStrokeLineWidth = 0.0f;
- field public static final float DefaultTranslateX = 0.0f;
- field public static final float DefaultTranslateY = 0.0f;
+ public final class VectorPath extends androidx.ui.core.vectorgraphics.VectorNode {
+ ctor public VectorPath(String name, Object? pathData, Object fill, float fillAlpha, Object stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.painting.StrokeCap strokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin, float strokeLineMiter);
+ method public Object getFill();
+ method public float getFillAlpha();
+ method public String getName();
+ method public Object? getPathData();
+ method public Object getStroke();
+ method public float getStrokeAlpha();
+ method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+ method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+ method public float getStrokeLineMiter();
+ method public float getStrokeLineWidth();
}
}
@@ -479,7 +422,12 @@
public final class VectorResourceKt {
ctor public VectorResourceKt();
- method public static void vectorResource(android.content.res.Resources res, int resId);
+ method public static void VectorResource(int resId);
+ method public static androidx.ui.core.vectorgraphics.VectorAsset loadVectorResource(android.content.res.Resources.Theme? theme = null, android.content.res.Resources res, int resId) throws org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public final class XmlVectorParserKt {
+ ctor public XmlVectorParserKt();
}
}
diff --git a/ui/ui-framework/api/current.txt b/ui/ui-framework/api/current.txt
index 224764c..01e28d9 100644
--- a/ui/ui-framework/api/current.txt
+++ b/ui/ui-framework/api/current.txt
@@ -7,7 +7,7 @@
}
public final class ComplexLayoutReceiver {
- method public void layout(kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutBlockReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
+ method public void layout(kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutBlockReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
method public void maxIntrinsicHeight(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> maxIntrinsicHeightBlock);
method public void maxIntrinsicWidth(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> maxIntrinsicWidthBlock);
method public void minIntrinsicHeight(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> minIntrinsicHeightBlock);
@@ -43,7 +43,7 @@
public final class LayoutBlockReceiver implements androidx.ui.core.DensityReceiver {
method public androidx.ui.core.Density getDensity();
- method public void layoutResult(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
+ method public androidx.ui.core.LayoutResult layoutResult(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
method public androidx.ui.core.IntPx maxIntrinsicHeight(androidx.ui.core.Measurable, androidx.ui.core.IntPx w);
method public androidx.ui.core.IntPx maxIntrinsicWidth(androidx.ui.core.Measurable, androidx.ui.core.IntPx h);
method public androidx.ui.core.Placeable measure(androidx.ui.core.Measurable, androidx.ui.core.Constraints constraints);
@@ -67,8 +67,8 @@
public final class LayoutKt {
ctor public LayoutKt();
method public static void ComplexLayout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function1<? super androidx.ui.core.ComplexLayoutReceiver,kotlin.Unit> block);
- method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
- method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit>![] childrenArray, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
+ method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
+ method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit>![] childrenArray, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
method public static void OnChildPositioned(kotlin.jvm.functions.Function1<? super androidx.ui.core.LayoutCoordinates,kotlin.Unit> onPositioned, kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void OnPositioned(kotlin.jvm.functions.Function1<? super androidx.ui.core.LayoutCoordinates,kotlin.Unit> onPositioned);
method public static void WithConstraints(kotlin.jvm.functions.Function1<? super androidx.ui.core.Constraints,kotlin.Unit> children);
@@ -77,11 +77,18 @@
public final class LayoutReceiver implements androidx.ui.core.DensityReceiver {
method public operator java.util.List<androidx.ui.core.Measurable> get(java.util.List<? extends androidx.ui.core.Measurable>, kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public androidx.ui.core.Density getDensity();
- method public void layout(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
+ method public androidx.ui.core.LayoutResult layout(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
method public androidx.ui.core.Placeable measure(androidx.ui.core.Measurable, androidx.ui.core.Constraints constraints);
property public androidx.ui.core.Density density;
}
+ public final class LayoutResult {
+ field public static final androidx.ui.core.LayoutResult.Companion! Companion;
+ }
+
+ public static final class LayoutResult.Companion {
+ }
+
public interface Measurable {
method public Object? getParentData();
property public abstract Object? parentData;
@@ -148,7 +155,7 @@
public final class TextFieldKt {
ctor public TextFieldKt();
- method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
+ method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function0<kotlin.Unit> onFocus = {}, kotlin.jvm.functions.Function0<kotlin.Unit> onBlur = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
}
public final class TextKt {
@@ -241,6 +248,11 @@
method public static androidx.ui.core.Duration getZoomControlsTimeout();
}
+ public final class DoubleTapGestureDetectorKt {
+ ctor public DoubleTapGestureDetectorKt();
+ method public static void DoubleTapGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit> onDoubleTap, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ }
+
public final class DragGestureDetectorKt {
ctor public DragGestureDetectorKt();
method public static void DragGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null, androidx.ui.core.gesture.DragObserver? dragObserver = null, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -334,143 +346,74 @@
package androidx.ui.core.vectorgraphics {
- public interface Brush {
- method public void applyBrush(androidx.ui.painting.Paint p);
+ public final class VectorAsset {
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public androidx.ui.core.vectorgraphics.VectorGroup getRoot();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
}
- public final class BrushKt {
- ctor public BrushKt();
- method public static androidx.ui.core.vectorgraphics.Brush getEmptyBrush();
- method public static androidx.ui.core.vectorgraphics.Brush obtainBrush(Object? brush);
+ public final class VectorAssetBuilder {
+ ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder addPath(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+ method public androidx.ui.core.vectorgraphics.VectorAsset build();
+ method public void ensureNotConsumed();
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder popGroup();
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder pushGroup(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath);
}
- public final class LinearGradient implements androidx.ui.core.vectorgraphics.Brush {
- ctor public LinearGradient(kotlin.Pair<androidx.ui.graphics.Color,java.lang.Float>![] colorStops, float startX, float startY, float endX, float endY, androidx.ui.painting.TileMode tileMode);
- method public void applyBrush(androidx.ui.painting.Paint p);
- method public float getEndX();
- method public float getEndY();
- method public float getStartX();
- method public float getStartY();
- method public androidx.ui.painting.TileMode getTileMode();
+ public final class VectorAssetKt {
+ ctor public VectorAssetKt();
+ method public static void DrawVector(androidx.ui.core.vectorgraphics.VectorAsset vectorImage);
}
- public final class PathBuilder {
- ctor public PathBuilder();
- method public androidx.ui.core.vectorgraphics.PathBuilder arcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder arcToRelative(float a, float b, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder close();
- method public androidx.ui.core.vectorgraphics.PathBuilder curveTo(float x1, float y1, float x2, float y2, float x3, float y3);
- method public androidx.ui.core.vectorgraphics.PathBuilder curveToRelative(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
- method public androidx.ui.core.vectorgraphics.PathNode![] getNodes();
- method public androidx.ui.core.vectorgraphics.PathBuilder horizontalLineTo(float x);
- method public androidx.ui.core.vectorgraphics.PathBuilder horizontalLineToRelative(float x);
- method public androidx.ui.core.vectorgraphics.PathBuilder lineTo(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder lineToRelative(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder moveTo(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder moveToRelative(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder quadTo(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder quadToRelative(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveCurveTo(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveCurveToRelative(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveQuadTo(float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveQuadToRelative(float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder verticalLineTo(float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder verticalLineToRelative(float y);
+ public final class VectorComposeKt {
+ ctor public VectorComposeKt();
+ method public static void DrawVector(float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth = Px(viewportWidth), androidx.ui.core.Px defaultHeight = Px(viewportHeight), String name = "", kotlin.jvm.functions.Function1<? super androidx.ui.vector.VectorScope,kotlin.Unit> children);
+ method public static void Group(androidx.ui.vector.VectorScope, String name = "", float rotation = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function1<? super androidx.ui.vector.VectorScope,kotlin.Unit> children);
+ method public static void Path(androidx.ui.vector.VectorScope, Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ArcTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand Close;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand CurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand LineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand MoveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand QuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand VerticalLineTo;
+ public final class VectorGroup extends androidx.ui.core.vectorgraphics.VectorNode implements java.lang.Iterable<androidx.ui.core.vectorgraphics.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, Object? clipPathData);
+ ctor public VectorGroup();
+ method public operator androidx.ui.core.vectorgraphics.VectorNode get(int index);
+ method public Object? getClipPathData();
+ method public String getName();
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSize();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public java.util.Iterator<androidx.ui.core.vectorgraphics.VectorNode> iterator();
+ property public final int size;
}
- public final class PathCommandKt {
- ctor public PathCommandKt();
- method public static androidx.ui.core.vectorgraphics.PathCommand toPathCommand(char) throws java.lang.IllegalArgumentException;
+ public abstract sealed class VectorNode {
}
- public final class PathDelegate {
- ctor public PathDelegate(kotlin.jvm.functions.Function1<? super androidx.ui.core.vectorgraphics.PathBuilder,kotlin.Unit> delegate);
- method public kotlin.jvm.functions.Function1<androidx.ui.core.vectorgraphics.PathBuilder,kotlin.Unit> getDelegate();
- }
-
- public final class PathNode {
- ctor public PathNode(androidx.ui.core.vectorgraphics.PathCommand command, float[] args);
- method public androidx.ui.core.vectorgraphics.PathCommand component1();
- method public float[] component2();
- method public androidx.ui.core.vectorgraphics.PathNode copy(androidx.ui.core.vectorgraphics.PathCommand command, float[] args);
- method public float[] getArgs();
- method public androidx.ui.core.vectorgraphics.PathCommand getCommand();
- }
-
- public final class PathNodeKt {
- ctor public PathNodeKt();
- method public static operator StringBuilder plus(StringBuilder, androidx.ui.core.vectorgraphics.PathNode node);
- }
-
- public final class PathParser {
- ctor public PathParser();
- method public androidx.ui.core.vectorgraphics.PathParser addPathNodes(androidx.ui.core.vectorgraphics.PathNode![] nodes);
- method public void clear();
- method public androidx.ui.core.vectorgraphics.PathParser parsePathString(String pathData) throws java.lang.IllegalArgumentException, java.lang.NumberFormatException;
- method public androidx.ui.core.vectorgraphics.PathNode![] toNodes();
- method public androidx.ui.painting.Path toPath(androidx.ui.painting.Path target = Path());
- }
-
- public final class PathParserKt {
- ctor public PathParserKt();
- }
-
- public final class RadialGradient implements androidx.ui.core.vectorgraphics.Brush {
- ctor public RadialGradient(kotlin.Pair<androidx.ui.graphics.Color,java.lang.Float>![] colorStops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode);
- method public void applyBrush(androidx.ui.painting.Paint p);
- }
-
- public final class SolidColor implements androidx.ui.core.vectorgraphics.Brush {
- ctor public SolidColor(androidx.ui.graphics.Color value);
- method public void applyBrush(androidx.ui.painting.Paint p);
- }
-
- public final class VectorKt {
- ctor public VectorKt();
- method public static androidx.ui.core.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
- method public static android.view.View? adoptVectorGraphic(Object? parent, Object? child);
- method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
- method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
- method public static androidx.ui.core.vectorgraphics.PathNode![] getEmptyPath();
- method public static void group(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translateX = 0.0f, float translateY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
- method public static void path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
- method public static void vector(String name = "", float viewportWidth, float viewportHeight, float defaultWidth = viewportWidth, float defaultHeight = viewportHeight, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- field public static final float DefaultAlpha = 1.0f;
- field public static final String DefaultGroupName = "";
- field public static final String DefaultPathName = "";
- field public static final float DefaultPivotX = 0.0f;
- field public static final float DefaultPivotY = 0.0f;
- field public static final float DefaultRotate = 0.0f;
- field public static final float DefaultScaleX = 1.0f;
- field public static final float DefaultScaleY = 1.0f;
- field public static final float DefaultStrokeLineMiter = 4.0f;
- field public static final float DefaultStrokeLineWidth = 0.0f;
- field public static final float DefaultTranslateX = 0.0f;
- field public static final float DefaultTranslateY = 0.0f;
+ public final class VectorPath extends androidx.ui.core.vectorgraphics.VectorNode {
+ ctor public VectorPath(String name, Object? pathData, Object fill, float fillAlpha, Object stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.painting.StrokeCap strokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin, float strokeLineMiter);
+ method public Object getFill();
+ method public float getFillAlpha();
+ method public String getName();
+ method public Object? getPathData();
+ method public Object getStroke();
+ method public float getStrokeAlpha();
+ method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+ method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+ method public float getStrokeLineMiter();
+ method public float getStrokeLineWidth();
}
}
@@ -479,7 +422,12 @@
public final class VectorResourceKt {
ctor public VectorResourceKt();
- method public static void vectorResource(android.content.res.Resources res, int resId);
+ method public static void VectorResource(int resId);
+ method public static androidx.ui.core.vectorgraphics.VectorAsset loadVectorResource(android.content.res.Resources.Theme? theme = null, android.content.res.Resources res, int resId) throws org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public final class XmlVectorParserKt {
+ ctor public XmlVectorParserKt();
}
}
diff --git a/ui/ui-framework/api/restricted_1.0.0-alpha01.txt b/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
index 224764c..01e28d9 100644
--- a/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
@@ -7,7 +7,7 @@
}
public final class ComplexLayoutReceiver {
- method public void layout(kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutBlockReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
+ method public void layout(kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutBlockReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
method public void maxIntrinsicHeight(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> maxIntrinsicHeightBlock);
method public void maxIntrinsicWidth(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> maxIntrinsicWidthBlock);
method public void minIntrinsicHeight(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> minIntrinsicHeightBlock);
@@ -43,7 +43,7 @@
public final class LayoutBlockReceiver implements androidx.ui.core.DensityReceiver {
method public androidx.ui.core.Density getDensity();
- method public void layoutResult(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
+ method public androidx.ui.core.LayoutResult layoutResult(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
method public androidx.ui.core.IntPx maxIntrinsicHeight(androidx.ui.core.Measurable, androidx.ui.core.IntPx w);
method public androidx.ui.core.IntPx maxIntrinsicWidth(androidx.ui.core.Measurable, androidx.ui.core.IntPx h);
method public androidx.ui.core.Placeable measure(androidx.ui.core.Measurable, androidx.ui.core.Constraints constraints);
@@ -67,8 +67,8 @@
public final class LayoutKt {
ctor public LayoutKt();
method public static void ComplexLayout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function1<? super androidx.ui.core.ComplexLayoutReceiver,kotlin.Unit> block);
- method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
- method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit>![] childrenArray, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
+ method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
+ method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit>![] childrenArray, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
method public static void OnChildPositioned(kotlin.jvm.functions.Function1<? super androidx.ui.core.LayoutCoordinates,kotlin.Unit> onPositioned, kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void OnPositioned(kotlin.jvm.functions.Function1<? super androidx.ui.core.LayoutCoordinates,kotlin.Unit> onPositioned);
method public static void WithConstraints(kotlin.jvm.functions.Function1<? super androidx.ui.core.Constraints,kotlin.Unit> children);
@@ -77,11 +77,18 @@
public final class LayoutReceiver implements androidx.ui.core.DensityReceiver {
method public operator java.util.List<androidx.ui.core.Measurable> get(java.util.List<? extends androidx.ui.core.Measurable>, kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public androidx.ui.core.Density getDensity();
- method public void layout(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
+ method public androidx.ui.core.LayoutResult layout(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
method public androidx.ui.core.Placeable measure(androidx.ui.core.Measurable, androidx.ui.core.Constraints constraints);
property public androidx.ui.core.Density density;
}
+ public final class LayoutResult {
+ field public static final androidx.ui.core.LayoutResult.Companion! Companion;
+ }
+
+ public static final class LayoutResult.Companion {
+ }
+
public interface Measurable {
method public Object? getParentData();
property public abstract Object? parentData;
@@ -148,7 +155,7 @@
public final class TextFieldKt {
ctor public TextFieldKt();
- method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
+ method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function0<kotlin.Unit> onFocus = {}, kotlin.jvm.functions.Function0<kotlin.Unit> onBlur = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
}
public final class TextKt {
@@ -241,6 +248,11 @@
method public static androidx.ui.core.Duration getZoomControlsTimeout();
}
+ public final class DoubleTapGestureDetectorKt {
+ ctor public DoubleTapGestureDetectorKt();
+ method public static void DoubleTapGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit> onDoubleTap, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ }
+
public final class DragGestureDetectorKt {
ctor public DragGestureDetectorKt();
method public static void DragGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null, androidx.ui.core.gesture.DragObserver? dragObserver = null, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -334,143 +346,74 @@
package androidx.ui.core.vectorgraphics {
- public interface Brush {
- method public void applyBrush(androidx.ui.painting.Paint p);
+ public final class VectorAsset {
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public androidx.ui.core.vectorgraphics.VectorGroup getRoot();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
}
- public final class BrushKt {
- ctor public BrushKt();
- method public static androidx.ui.core.vectorgraphics.Brush getEmptyBrush();
- method public static androidx.ui.core.vectorgraphics.Brush obtainBrush(Object? brush);
+ public final class VectorAssetBuilder {
+ ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder addPath(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+ method public androidx.ui.core.vectorgraphics.VectorAsset build();
+ method public void ensureNotConsumed();
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder popGroup();
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder pushGroup(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath);
}
- public final class LinearGradient implements androidx.ui.core.vectorgraphics.Brush {
- ctor public LinearGradient(kotlin.Pair<androidx.ui.graphics.Color,java.lang.Float>![] colorStops, float startX, float startY, float endX, float endY, androidx.ui.painting.TileMode tileMode);
- method public void applyBrush(androidx.ui.painting.Paint p);
- method public float getEndX();
- method public float getEndY();
- method public float getStartX();
- method public float getStartY();
- method public androidx.ui.painting.TileMode getTileMode();
+ public final class VectorAssetKt {
+ ctor public VectorAssetKt();
+ method public static void DrawVector(androidx.ui.core.vectorgraphics.VectorAsset vectorImage);
}
- public final class PathBuilder {
- ctor public PathBuilder();
- method public androidx.ui.core.vectorgraphics.PathBuilder arcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder arcToRelative(float a, float b, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder close();
- method public androidx.ui.core.vectorgraphics.PathBuilder curveTo(float x1, float y1, float x2, float y2, float x3, float y3);
- method public androidx.ui.core.vectorgraphics.PathBuilder curveToRelative(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
- method public androidx.ui.core.vectorgraphics.PathNode![] getNodes();
- method public androidx.ui.core.vectorgraphics.PathBuilder horizontalLineTo(float x);
- method public androidx.ui.core.vectorgraphics.PathBuilder horizontalLineToRelative(float x);
- method public androidx.ui.core.vectorgraphics.PathBuilder lineTo(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder lineToRelative(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder moveTo(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder moveToRelative(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder quadTo(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder quadToRelative(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveCurveTo(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveCurveToRelative(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveQuadTo(float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveQuadToRelative(float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder verticalLineTo(float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder verticalLineToRelative(float y);
+ public final class VectorComposeKt {
+ ctor public VectorComposeKt();
+ method public static void DrawVector(float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth = Px(viewportWidth), androidx.ui.core.Px defaultHeight = Px(viewportHeight), String name = "", kotlin.jvm.functions.Function1<? super androidx.ui.vector.VectorScope,kotlin.Unit> children);
+ method public static void Group(androidx.ui.vector.VectorScope, String name = "", float rotation = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function1<? super androidx.ui.vector.VectorScope,kotlin.Unit> children);
+ method public static void Path(androidx.ui.vector.VectorScope, Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ArcTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand Close;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand CurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand LineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand MoveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand QuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand VerticalLineTo;
+ public final class VectorGroup extends androidx.ui.core.vectorgraphics.VectorNode implements java.lang.Iterable<androidx.ui.core.vectorgraphics.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, Object? clipPathData);
+ ctor public VectorGroup();
+ method public operator androidx.ui.core.vectorgraphics.VectorNode get(int index);
+ method public Object? getClipPathData();
+ method public String getName();
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSize();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public java.util.Iterator<androidx.ui.core.vectorgraphics.VectorNode> iterator();
+ property public final int size;
}
- public final class PathCommandKt {
- ctor public PathCommandKt();
- method public static androidx.ui.core.vectorgraphics.PathCommand toPathCommand(char) throws java.lang.IllegalArgumentException;
+ public abstract sealed class VectorNode {
}
- public final class PathDelegate {
- ctor public PathDelegate(kotlin.jvm.functions.Function1<? super androidx.ui.core.vectorgraphics.PathBuilder,kotlin.Unit> delegate);
- method public kotlin.jvm.functions.Function1<androidx.ui.core.vectorgraphics.PathBuilder,kotlin.Unit> getDelegate();
- }
-
- public final class PathNode {
- ctor public PathNode(androidx.ui.core.vectorgraphics.PathCommand command, float[] args);
- method public androidx.ui.core.vectorgraphics.PathCommand component1();
- method public float[] component2();
- method public androidx.ui.core.vectorgraphics.PathNode copy(androidx.ui.core.vectorgraphics.PathCommand command, float[] args);
- method public float[] getArgs();
- method public androidx.ui.core.vectorgraphics.PathCommand getCommand();
- }
-
- public final class PathNodeKt {
- ctor public PathNodeKt();
- method public static operator StringBuilder plus(StringBuilder, androidx.ui.core.vectorgraphics.PathNode node);
- }
-
- public final class PathParser {
- ctor public PathParser();
- method public androidx.ui.core.vectorgraphics.PathParser addPathNodes(androidx.ui.core.vectorgraphics.PathNode![] nodes);
- method public void clear();
- method public androidx.ui.core.vectorgraphics.PathParser parsePathString(String pathData) throws java.lang.IllegalArgumentException, java.lang.NumberFormatException;
- method public androidx.ui.core.vectorgraphics.PathNode![] toNodes();
- method public androidx.ui.painting.Path toPath(androidx.ui.painting.Path target = Path());
- }
-
- public final class PathParserKt {
- ctor public PathParserKt();
- }
-
- public final class RadialGradient implements androidx.ui.core.vectorgraphics.Brush {
- ctor public RadialGradient(kotlin.Pair<androidx.ui.graphics.Color,java.lang.Float>![] colorStops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode);
- method public void applyBrush(androidx.ui.painting.Paint p);
- }
-
- public final class SolidColor implements androidx.ui.core.vectorgraphics.Brush {
- ctor public SolidColor(androidx.ui.graphics.Color value);
- method public void applyBrush(androidx.ui.painting.Paint p);
- }
-
- public final class VectorKt {
- ctor public VectorKt();
- method public static androidx.ui.core.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
- method public static android.view.View? adoptVectorGraphic(Object? parent, Object? child);
- method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
- method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
- method public static androidx.ui.core.vectorgraphics.PathNode![] getEmptyPath();
- method public static void group(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translateX = 0.0f, float translateY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
- method public static void path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
- method public static void vector(String name = "", float viewportWidth, float viewportHeight, float defaultWidth = viewportWidth, float defaultHeight = viewportHeight, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- field public static final float DefaultAlpha = 1.0f;
- field public static final String DefaultGroupName = "";
- field public static final String DefaultPathName = "";
- field public static final float DefaultPivotX = 0.0f;
- field public static final float DefaultPivotY = 0.0f;
- field public static final float DefaultRotate = 0.0f;
- field public static final float DefaultScaleX = 1.0f;
- field public static final float DefaultScaleY = 1.0f;
- field public static final float DefaultStrokeLineMiter = 4.0f;
- field public static final float DefaultStrokeLineWidth = 0.0f;
- field public static final float DefaultTranslateX = 0.0f;
- field public static final float DefaultTranslateY = 0.0f;
+ public final class VectorPath extends androidx.ui.core.vectorgraphics.VectorNode {
+ ctor public VectorPath(String name, Object? pathData, Object fill, float fillAlpha, Object stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.painting.StrokeCap strokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin, float strokeLineMiter);
+ method public Object getFill();
+ method public float getFillAlpha();
+ method public String getName();
+ method public Object? getPathData();
+ method public Object getStroke();
+ method public float getStrokeAlpha();
+ method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+ method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+ method public float getStrokeLineMiter();
+ method public float getStrokeLineWidth();
}
}
@@ -479,7 +422,12 @@
public final class VectorResourceKt {
ctor public VectorResourceKt();
- method public static void vectorResource(android.content.res.Resources res, int resId);
+ method public static void VectorResource(int resId);
+ method public static androidx.ui.core.vectorgraphics.VectorAsset loadVectorResource(android.content.res.Resources.Theme? theme = null, android.content.res.Resources res, int resId) throws org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public final class XmlVectorParserKt {
+ ctor public XmlVectorParserKt();
}
}
diff --git a/ui/ui-framework/api/restricted_current.txt b/ui/ui-framework/api/restricted_current.txt
index 224764c..01e28d9 100644
--- a/ui/ui-framework/api/restricted_current.txt
+++ b/ui/ui-framework/api/restricted_current.txt
@@ -7,7 +7,7 @@
}
public final class ComplexLayoutReceiver {
- method public void layout(kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutBlockReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
+ method public void layout(kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutBlockReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
method public void maxIntrinsicHeight(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> maxIntrinsicHeightBlock);
method public void maxIntrinsicWidth(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> maxIntrinsicWidthBlock);
method public void minIntrinsicHeight(kotlin.jvm.functions.Function3<? super androidx.ui.core.IntrinsicMeasurementsReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> minIntrinsicHeightBlock);
@@ -43,7 +43,7 @@
public final class LayoutBlockReceiver implements androidx.ui.core.DensityReceiver {
method public androidx.ui.core.Density getDensity();
- method public void layoutResult(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
+ method public androidx.ui.core.LayoutResult layoutResult(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
method public androidx.ui.core.IntPx maxIntrinsicHeight(androidx.ui.core.Measurable, androidx.ui.core.IntPx w);
method public androidx.ui.core.IntPx maxIntrinsicWidth(androidx.ui.core.Measurable, androidx.ui.core.IntPx h);
method public androidx.ui.core.Placeable measure(androidx.ui.core.Measurable, androidx.ui.core.Constraints constraints);
@@ -67,8 +67,8 @@
public final class LayoutKt {
ctor public LayoutKt();
method public static void ComplexLayout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function1<? super androidx.ui.core.ComplexLayoutReceiver,kotlin.Unit> block);
- method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
- method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit>![] childrenArray, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,kotlin.Unit> layoutBlock);
+ method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> children, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
+ method public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit>![] childrenArray, kotlin.jvm.functions.Function3<? super androidx.ui.core.LayoutReceiver,? super java.util.List<? extends androidx.ui.core.Measurable>,? super androidx.ui.core.Constraints,androidx.ui.core.LayoutResult> layoutBlock);
method public static void OnChildPositioned(kotlin.jvm.functions.Function1<? super androidx.ui.core.LayoutCoordinates,kotlin.Unit> onPositioned, kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void OnPositioned(kotlin.jvm.functions.Function1<? super androidx.ui.core.LayoutCoordinates,kotlin.Unit> onPositioned);
method public static void WithConstraints(kotlin.jvm.functions.Function1<? super androidx.ui.core.Constraints,kotlin.Unit> children);
@@ -77,11 +77,18 @@
public final class LayoutReceiver implements androidx.ui.core.DensityReceiver {
method public operator java.util.List<androidx.ui.core.Measurable> get(java.util.List<? extends androidx.ui.core.Measurable>, kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public androidx.ui.core.Density getDensity();
- method public void layout(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
+ method public androidx.ui.core.LayoutResult layout(androidx.ui.core.IntPx width, androidx.ui.core.IntPx height, kotlin.jvm.functions.Function1<? super androidx.ui.core.PositioningBlockReceiver,kotlin.Unit> block);
method public androidx.ui.core.Placeable measure(androidx.ui.core.Measurable, androidx.ui.core.Constraints constraints);
property public androidx.ui.core.Density density;
}
+ public final class LayoutResult {
+ field public static final androidx.ui.core.LayoutResult.Companion! Companion;
+ }
+
+ public static final class LayoutResult.Companion {
+ }
+
public interface Measurable {
method public Object? getParentData();
property public abstract Object? parentData;
@@ -148,7 +155,7 @@
public final class TextFieldKt {
ctor public TextFieldKt();
- method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
+ method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function0<kotlin.Unit> onFocus = {}, kotlin.jvm.functions.Function0<kotlin.Unit> onBlur = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
}
public final class TextKt {
@@ -241,6 +248,11 @@
method public static androidx.ui.core.Duration getZoomControlsTimeout();
}
+ public final class DoubleTapGestureDetectorKt {
+ ctor public DoubleTapGestureDetectorKt();
+ method public static void DoubleTapGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit> onDoubleTap, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ }
+
public final class DragGestureDetectorKt {
ctor public DragGestureDetectorKt();
method public static void DragGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null, androidx.ui.core.gesture.DragObserver? dragObserver = null, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -334,143 +346,74 @@
package androidx.ui.core.vectorgraphics {
- public interface Brush {
- method public void applyBrush(androidx.ui.painting.Paint p);
+ public final class VectorAsset {
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public androidx.ui.core.vectorgraphics.VectorGroup getRoot();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
}
- public final class BrushKt {
- ctor public BrushKt();
- method public static androidx.ui.core.vectorgraphics.Brush getEmptyBrush();
- method public static androidx.ui.core.vectorgraphics.Brush obtainBrush(Object? brush);
+ public final class VectorAssetBuilder {
+ ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder addPath(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+ method public androidx.ui.core.vectorgraphics.VectorAsset build();
+ method public void ensureNotConsumed();
+ method public androidx.ui.core.Px getDefaultHeight();
+ method public androidx.ui.core.Px getDefaultWidth();
+ method public String getName();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder popGroup();
+ method public androidx.ui.core.vectorgraphics.VectorAssetBuilder pushGroup(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath);
}
- public final class LinearGradient implements androidx.ui.core.vectorgraphics.Brush {
- ctor public LinearGradient(kotlin.Pair<androidx.ui.graphics.Color,java.lang.Float>![] colorStops, float startX, float startY, float endX, float endY, androidx.ui.painting.TileMode tileMode);
- method public void applyBrush(androidx.ui.painting.Paint p);
- method public float getEndX();
- method public float getEndY();
- method public float getStartX();
- method public float getStartY();
- method public androidx.ui.painting.TileMode getTileMode();
+ public final class VectorAssetKt {
+ ctor public VectorAssetKt();
+ method public static void DrawVector(androidx.ui.core.vectorgraphics.VectorAsset vectorImage);
}
- public final class PathBuilder {
- ctor public PathBuilder();
- method public androidx.ui.core.vectorgraphics.PathBuilder arcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder arcToRelative(float a, float b, float theta, float largeArcFlag, float sweepFlag, float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder close();
- method public androidx.ui.core.vectorgraphics.PathBuilder curveTo(float x1, float y1, float x2, float y2, float x3, float y3);
- method public androidx.ui.core.vectorgraphics.PathBuilder curveToRelative(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
- method public androidx.ui.core.vectorgraphics.PathNode![] getNodes();
- method public androidx.ui.core.vectorgraphics.PathBuilder horizontalLineTo(float x);
- method public androidx.ui.core.vectorgraphics.PathBuilder horizontalLineToRelative(float x);
- method public androidx.ui.core.vectorgraphics.PathBuilder lineTo(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder lineToRelative(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder moveTo(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder moveToRelative(float x, float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder quadTo(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder quadToRelative(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveCurveTo(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveCurveToRelative(float x1, float y1, float x2, float y2);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveQuadTo(float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder reflectiveQuadToRelative(float x1, float y1);
- method public androidx.ui.core.vectorgraphics.PathBuilder verticalLineTo(float y);
- method public androidx.ui.core.vectorgraphics.PathBuilder verticalLineToRelative(float y);
+ public final class VectorComposeKt {
+ ctor public VectorComposeKt();
+ method public static void DrawVector(float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth = Px(viewportWidth), androidx.ui.core.Px defaultHeight = Px(viewportHeight), String name = "", kotlin.jvm.functions.Function1<? super androidx.ui.vector.VectorScope,kotlin.Unit> children);
+ method public static void Group(androidx.ui.vector.VectorScope, String name = "", float rotation = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function1<? super androidx.ui.vector.VectorScope,kotlin.Unit> children);
+ method public static void Path(androidx.ui.vector.VectorScope, Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ArcTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand Close;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand CurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand LineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand MoveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand QuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.core.vectorgraphics.PathCommand VerticalLineTo;
+ public final class VectorGroup extends androidx.ui.core.vectorgraphics.VectorNode implements java.lang.Iterable<androidx.ui.core.vectorgraphics.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, Object? clipPathData);
+ ctor public VectorGroup();
+ method public operator androidx.ui.core.vectorgraphics.VectorNode get(int index);
+ method public Object? getClipPathData();
+ method public String getName();
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSize();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public java.util.Iterator<androidx.ui.core.vectorgraphics.VectorNode> iterator();
+ property public final int size;
}
- public final class PathCommandKt {
- ctor public PathCommandKt();
- method public static androidx.ui.core.vectorgraphics.PathCommand toPathCommand(char) throws java.lang.IllegalArgumentException;
+ public abstract sealed class VectorNode {
}
- public final class PathDelegate {
- ctor public PathDelegate(kotlin.jvm.functions.Function1<? super androidx.ui.core.vectorgraphics.PathBuilder,kotlin.Unit> delegate);
- method public kotlin.jvm.functions.Function1<androidx.ui.core.vectorgraphics.PathBuilder,kotlin.Unit> getDelegate();
- }
-
- public final class PathNode {
- ctor public PathNode(androidx.ui.core.vectorgraphics.PathCommand command, float[] args);
- method public androidx.ui.core.vectorgraphics.PathCommand component1();
- method public float[] component2();
- method public androidx.ui.core.vectorgraphics.PathNode copy(androidx.ui.core.vectorgraphics.PathCommand command, float[] args);
- method public float[] getArgs();
- method public androidx.ui.core.vectorgraphics.PathCommand getCommand();
- }
-
- public final class PathNodeKt {
- ctor public PathNodeKt();
- method public static operator StringBuilder plus(StringBuilder, androidx.ui.core.vectorgraphics.PathNode node);
- }
-
- public final class PathParser {
- ctor public PathParser();
- method public androidx.ui.core.vectorgraphics.PathParser addPathNodes(androidx.ui.core.vectorgraphics.PathNode![] nodes);
- method public void clear();
- method public androidx.ui.core.vectorgraphics.PathParser parsePathString(String pathData) throws java.lang.IllegalArgumentException, java.lang.NumberFormatException;
- method public androidx.ui.core.vectorgraphics.PathNode![] toNodes();
- method public androidx.ui.painting.Path toPath(androidx.ui.painting.Path target = Path());
- }
-
- public final class PathParserKt {
- ctor public PathParserKt();
- }
-
- public final class RadialGradient implements androidx.ui.core.vectorgraphics.Brush {
- ctor public RadialGradient(kotlin.Pair<androidx.ui.graphics.Color,java.lang.Float>![] colorStops, float centerX, float centerY, float radius, androidx.ui.painting.TileMode tileMode);
- method public void applyBrush(androidx.ui.painting.Paint p);
- }
-
- public final class SolidColor implements androidx.ui.core.vectorgraphics.Brush {
- ctor public SolidColor(androidx.ui.graphics.Color value);
- method public void applyBrush(androidx.ui.painting.Paint p);
- }
-
- public final class VectorKt {
- ctor public VectorKt();
- method public static androidx.ui.core.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
- method public static android.view.View? adoptVectorGraphic(Object? parent, Object? child);
- method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
- method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
- method public static androidx.ui.core.vectorgraphics.PathNode![] getEmptyPath();
- method public static void group(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translateX = 0.0f, float translateY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
- method public static void path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
- method public static void vector(String name = "", float viewportWidth, float viewportHeight, float defaultWidth = viewportWidth, float defaultHeight = viewportHeight, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- field public static final float DefaultAlpha = 1.0f;
- field public static final String DefaultGroupName = "";
- field public static final String DefaultPathName = "";
- field public static final float DefaultPivotX = 0.0f;
- field public static final float DefaultPivotY = 0.0f;
- field public static final float DefaultRotate = 0.0f;
- field public static final float DefaultScaleX = 1.0f;
- field public static final float DefaultScaleY = 1.0f;
- field public static final float DefaultStrokeLineMiter = 4.0f;
- field public static final float DefaultStrokeLineWidth = 0.0f;
- field public static final float DefaultTranslateX = 0.0f;
- field public static final float DefaultTranslateY = 0.0f;
+ public final class VectorPath extends androidx.ui.core.vectorgraphics.VectorNode {
+ ctor public VectorPath(String name, Object? pathData, Object fill, float fillAlpha, Object stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.painting.StrokeCap strokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin, float strokeLineMiter);
+ method public Object getFill();
+ method public float getFillAlpha();
+ method public String getName();
+ method public Object? getPathData();
+ method public Object getStroke();
+ method public float getStrokeAlpha();
+ method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+ method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+ method public float getStrokeLineMiter();
+ method public float getStrokeLineWidth();
}
}
@@ -479,7 +422,12 @@
public final class VectorResourceKt {
ctor public VectorResourceKt();
- method public static void vectorResource(android.content.res.Resources res, int resId);
+ method public static void VectorResource(int resId);
+ method public static androidx.ui.core.vectorgraphics.VectorAsset loadVectorResource(android.content.res.Resources.Theme? theme = null, android.content.res.Resources res, int resId) throws org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public final class XmlVectorParserKt {
+ ctor public XmlVectorParserKt();
}
}
diff --git a/ui/ui-framework/build.gradle b/ui/ui-framework/build.gradle
index 2bf6a35..5d162f6 100644
--- a/ui/ui-framework/build.gradle
+++ b/ui/ui-framework/build.gradle
@@ -40,6 +40,7 @@
implementation project(":ui:ui-core")
implementation project(":ui:ui-platform")
implementation project(":ui:ui-text")
+ implementation project(":ui:ui-vector")
testImplementation(ANDROIDX_TEST_RULES)
testImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/ui/ui-framework/integration-tests/framework-demos/build.gradle b/ui/ui-framework/integration-tests/framework-demos/build.gradle
index efa06f8..5e42071 100644
--- a/ui/ui-framework/integration-tests/framework-demos/build.gradle
+++ b/ui/ui-framework/integration-tests/framework-demos/build.gradle
@@ -26,6 +26,7 @@
implementation project(':ui:ui-animation')
implementation project(':ui:ui-framework')
implementation project(':ui:ui-material')
+ implementation project(path: ':ui:ui-vector')
}
android {
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml b/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml
index 933fee0..77971ae 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml
@@ -53,6 +53,14 @@
<category android:name="androidx.ui.demos.SAMPLE_CODE" />
</intent-filter>
</activity>
+ <activity android:name=".gestures.DoubleTapGestureDetectorDemo"
+ android:configChanges="orientation|screenSize"
+ android:label="Gestures/Single GestureDetectors/DoubleTapGestureDetectorDemo">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="androidx.ui.demos.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
<activity android:name=".gestures.NestedScrollingDemo"
android:configChanges="orientation|screenSize"
android:label="Gestures/Complex Demos/Nested Scrolling">
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
index 443fcd6..2d01e25 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
@@ -25,13 +25,13 @@
import androidx.ui.core.coerceIn
import androidx.ui.core.ipx
import androidx.ui.core.toRect
-import androidx.ui.core.vectorgraphics.Brush
-import androidx.ui.core.vectorgraphics.SolidColor
import androidx.ui.graphics.Color
import androidx.ui.painting.Paint
import androidx.compose.Children
import androidx.compose.Composable
import androidx.compose.composer
+import androidx.ui.graphics.vectorgraphics.Brush
+import androidx.ui.graphics.vectorgraphics.SolidColor
@Composable
fun ColoredRect(brush: Brush, width: Dp? = null, height: Dp? = null) {
@@ -63,7 +63,7 @@
fun HeaderFooterLayout(
header: @Composable() () -> Unit,
footer: @Composable() () -> Unit,
- @Children content: @Composable() () -> Unit
+ content: @Composable() () -> Unit
) {
@Suppress("USELESS_CAST")
Layout(
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
index 74904e6..a24fa7b 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
@@ -144,7 +144,7 @@
onPress: SemanticAction<PxPosition> = SemanticAction(defaultParam = PxPosition.Origin) { },
onRelease: SemanticAction<Unit> = SemanticAction(defaultParam = Unit) { },
onCancel: SemanticAction<Unit> = SemanticAction(defaultParam = Unit) { },
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
PressGestureDetector(
onPress = { onPress.action(ActionParam(ActionCaller.PointerInput, it)) },
@@ -165,7 +165,7 @@
fun Semantics(
@Suppress("UNUSED_PARAMETER") properties: Set<SemanticProperty<out Any>> = setOf(),
actions: Set<SemanticAction<out Any?>> = setOf(),
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Column {
MaterialTheme {
@@ -275,7 +275,7 @@
* children or make them visible.
*/
@Composable
-private fun Collapsable(@Children children: @Composable() () -> Unit) {
+private fun Collapsable(children: @Composable() () -> Unit) {
val collapsedState = +state { CollapseMode.Collapsed }
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL2.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL2.kt
index a1aa21f..031632f 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL2.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL2.kt
@@ -28,7 +28,7 @@
label: String = "",
visibility: Visibility = Visibility.Undefined,
actions: Set<SemanticAction<out Any?>> = setOf(),
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val propertySet = mutableSetOf<SemanticProperty<out Any>>()
@@ -62,7 +62,7 @@
defaultParam: T,
types: Set<ActionType> = setOf(),
action: (ActionParam<T>) -> Unit,
- @Children block: @Composable() (SemanticAction<T>) -> Unit
+ block: @Composable() (SemanticAction<T>) -> Unit
) {
val semanticAction = SemanticAction<T>(phrase, defaultParam, types, action)
block.invoke(semanticAction)
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL3.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL3.kt
index 9b5eed4..565ab99 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL3.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL3.kt
@@ -29,7 +29,7 @@
@Composable
fun ClickInteraction(
click: SemanticActionBuilder<Unit>.() -> Unit,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val clickAction = SemanticActionBuilder(phrase = "Click", defaultParam = Unit)
.apply(click)
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/VectorGraphicsActivity.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/VectorGraphicsActivity.kt
index e6288ff..37f6735 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/VectorGraphicsActivity.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/VectorGraphicsActivity.kt
@@ -17,68 +17,90 @@
package androidx.ui.framework.demos
import android.app.Activity
-import android.graphics.Color
+import android.graphics.Shader
import android.os.Bundle
-import android.widget.LinearLayout
-import androidx.ui.core.vectorgraphics.adoptVectorGraphic
-import androidx.ui.core.vectorgraphics.compat.vectorResource
-import androidx.ui.core.vectorgraphics.group
-import androidx.ui.core.vectorgraphics.path
-import androidx.ui.core.vectorgraphics.vector
-import androidx.ui.core.vectorgraphics.PathBuilder
-import androidx.ui.core.vectorgraphics.PathDelegate
+import androidx.compose.Children
import androidx.compose.Composable
import androidx.compose.composer
-import androidx.compose.registerAdapter
-import androidx.compose.setViewContent
+import androidx.ui.core.IntPx
+import androidx.ui.core.Layout
+import androidx.ui.core.Px
+import androidx.ui.core.dp
+import androidx.ui.core.px
+import androidx.ui.core.round
+import androidx.ui.core.vectorgraphics.DrawVector
+import androidx.ui.core.vectorgraphics.Group
+import androidx.ui.core.vectorgraphics.Path
+import androidx.ui.graphics.Color
+import androidx.ui.graphics.vectorgraphics.HorizontalGradient
+import androidx.ui.graphics.vectorgraphics.PathBuilder
+import androidx.ui.graphics.vectorgraphics.PathDelegate
+import androidx.ui.graphics.vectorgraphics.RadialGradient
+import androidx.ui.graphics.vectorgraphics.VerticalGradient
+import androidx.ui.layout.Center
+import androidx.ui.layout.Column
+import androidx.ui.layout.Container
+import androidx.ui.painting.TileMode
+import androidx.ui.core.setContent
+import androidx.ui.core.vectorgraphics.compat.VectorResource
+import androidx.ui.vector.VectorScope
class VectorGraphicsActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val res = getResources()
- setViewContent {
- composer.registerAdapter { parent, child ->
- adoptVectorGraphic(parent, child)
- }
+ setContent {
+ Column {
+ Container(width = 480.dp, height = 480.dp) {
+ VectorResource(resId = R.drawable.ic_crane)
+ }
- LinearLayout(orientation = LinearLayout.VERTICAL) {
- vectorResource(
- res = res,
- resId = androidx.ui.framework.demos.R.drawable.ic_crane
- )
- vectorShape()
+ Center {
+ val width = 300.px
+ val height = 300.px
+ FixedLayout(width.round(), height.round()) {
+ vectorShape(width, height)
+ }
+ }
}
}
}
@Composable
- fun vectorShape() {
- val viewportWidth = 300.0f
- val viewportHeight = 300.0f
- vector(
+ fun FixedLayout(width: IntPx, height: IntPx, @Children child: @Composable() () -> Unit) {
+ Layout(children = { child() },
+ layoutBlock = { _, _ ->
+ layout(width, height) {}
+ })
+ }
+
+ @Composable
+ fun vectorShape(width: Px, height: Px) {
+ val viewportWidth = width.value
+ val viewportHeight = height.value
+ DrawVector(
name = "vectorShape",
- defaultWidth = 300.0f,
- defaultHeight = 300.0f,
+ defaultWidth = width,
+ defaultHeight = height,
viewportWidth = viewportWidth,
viewportHeight = viewportHeight
) {
- group(
+ Group(
scaleX = 0.75f,
scaleY = 0.75f,
- rotate = 45.0f,
+ rotation = 45.0f,
pivotX = (viewportWidth / 2),
pivotY = (viewportHeight / 2)
) {
backgroundPath(vectorWidth = viewportWidth, vectorHeight = viewportHeight)
stripePath(vectorWidth = viewportWidth, vectorHeight = viewportHeight)
- group(
- translateX = 50.0f,
- translateY = 50.0f,
+ Group(
+ translationX = 50.0f,
+ translationY = 50.0f,
pivotX = (viewportWidth / 2),
pivotY = (viewportHeight / 2),
- rotate = 25.0f
+ rotation = 25.0f
) {
val pathData = PathDelegate {
moveTo(viewportWidth / 2 - 100, viewportHeight / 2 - 100)
@@ -87,14 +109,24 @@
horizontalLineToRelative(-200.0f)
close()
}
- path(fill = Color.MAGENTA, pathData = pathData)
+ Path(
+ fill = HorizontalGradient(
+ Color.Red,
+ Color.Blue,
+ startX = Px.Zero,
+ endX = Px(viewportWidth / 2 + 100)
+ ),
+ pathData = pathData
+ )
}
+ triangle()
+ triangleWithOffsets()
}
}
}
@Composable
- fun backgroundPath(vectorWidth: Float, vectorHeight: Float) {
+ fun VectorScope.backgroundPath(vectorWidth: Float, vectorHeight: Float) {
val background = PathDelegate {
horizontalLineTo(vectorWidth)
verticalLineTo(vectorHeight)
@@ -102,16 +134,68 @@
close()
}
- path(fill = Color.CYAN, pathData = background)
+ Path(
+ fill = VerticalGradient(
+ 0.0f to Color.Aqua,
+ 0.3f to Color.Lime,
+ 1.0f to Color.Fuchsia,
+ startY = Px.Zero,
+ endY = Px(vectorHeight)
+ ),
+ pathData = background
+ )
}
@Composable
- fun stripePath(vectorWidth: Float, vectorHeight: Float) {
+ fun VectorScope.triangle() {
+ val length = 150.0f
+ Path(
+ fill = RadialGradient(
+ Color.Navy,
+ Color.Olive,
+ Color.Teal,
+ centerX = length / 2.0f,
+ centerY = length / 2.0f,
+ radius = length / 2.0f,
+ tileMode = TileMode.repeated
+ ),
+ pathData = PathDelegate {
+ verticalLineTo(length)
+ horizontalLineTo(length)
+ close()
+ }
+ )
+ }
+
+ @Composable
+ fun VectorScope.triangleWithOffsets() {
+
+ val side1 = 150.0f
+ val side2 = 150.0f
+ Path(
+ fill = RadialGradient(
+ 0.0f to Color.Maroon,
+ 0.3f to Color.Cyan,
+ 0.8f to Color.Yellow,
+ centerX = side1 / 2.0f,
+ centerY = side2 / 2.0f,
+ radius = side1 / 2.0f
+ ),
+ pathData = PathDelegate {
+ horizontalLineToRelative(side1)
+ verticalLineToRelative(side2)
+ close()
+ }
+ )
+ }
+
+ @Composable
+ fun VectorScope.stripePath(vectorWidth: Float, vectorHeight: Float) {
val stripeDelegate = PathDelegate {
stripe(vectorWidth, vectorHeight, 10)
}
- path(stroke = Color.BLUE, pathData = stripeDelegate)
+ Path(stroke = Color.Blue, pathData = stripeDelegate)
}
private fun PathBuilder.stripe(vectorWidth: Float, vectorHeight: Float, numLines: Int) {
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/DoubleTapGestureDetectorDemo.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/DoubleTapGestureDetectorDemo.kt
new file mode 100644
index 0000000..7a972c5
--- /dev/null
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/DoubleTapGestureDetectorDemo.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 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.ui.framework.demos.gestures
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.px
+import androidx.ui.core.setContent
+import androidx.compose.composer
+import androidx.ui.core.PxPosition
+import androidx.ui.core.dp
+import androidx.ui.core.gesture.DoubleTapGestureDetector
+
+/**
+ * Simple demo that shows off DragGestureDetector.
+ */
+class DoubleTapGestureDetectorDemo : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ val color = +state { Colors.random() }
+
+ val onDoubleTap: (PxPosition) -> Unit = {
+ color.value = color.value.anotherRandomColor()
+ }
+
+ DoubleTapGestureDetector(onDoubleTap = onDoubleTap) {
+ MatchParent {
+ DrawBox(
+ 0.px,
+ 0.px,
+ 96.dp,
+ 96.dp,
+ color.value
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/NestedScrollingDemo.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/NestedScrollingDemo.kt
index dc3feee..ffadb06 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/NestedScrollingDemo.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/NestedScrollingDemo.kt
@@ -41,6 +41,7 @@
import androidx.compose.composer
import androidx.ui.core.Draw
import androidx.ui.core.dp
+import androidx.ui.core.gesture.DoubleTapGestureDetector
import androidx.ui.core.gesture.PressReleasedGestureDetector
import androidx.ui.core.round
import androidx.ui.core.toRect
@@ -80,7 +81,7 @@
* A very simple ScrollView like implementation that allows for vertical scrolling.
*/
@Composable
-private fun Draggable(@Children children: @Composable() () -> Unit) {
+private fun Draggable(children: @Composable() () -> Unit) {
val offset = +state { 0.px }
val maxOffset = +state { 0.px }
@@ -141,27 +142,30 @@
) {
val pressedColor = Color(0x1f000000)
- val itemColor = Color(0xFFFFFFFF.toInt())
+ val defaultColor = Color(0xFFFFFFFF.toInt())
- val color = +state { itemColor }
- val pressed = +state { false }
+ val color = +state { defaultColor }
+ val showPressed = +state { false }
- val onStart: (PxPosition) -> Unit = {
- pressed.value = true
- }
-
- val onStop = {
- pressed.value = false
+ val onPress: (PxPosition) -> Unit = {
+ showPressed.value = true
}
val onRelease = {
+ showPressed.value = false
+ }
+
+ val onTap = {
color.value = color.value.next()
- pressed.value = false
+ }
+
+ val onDoubleTap: (PxPosition) -> Unit = {
+ color.value = color.value.prev().prev()
}
val onLongPress = { _: PxPosition ->
- color.value = color.value.prev()
- pressed.value = false
+ color.value = defaultColor
+ showPressed.value = false
}
val children = @Composable {
@@ -171,7 +175,7 @@
Rect(0f, 0f, parentSize.width.value, parentSize.height.value),
backgroundPaint
)
- if (pressed.value) {
+ if (showPressed.value) {
backgroundPaint.color = pressedColor
canvas.drawRect(
Rect(0f, 0f, parentSize.width.value, parentSize.height.value),
@@ -181,14 +185,16 @@
}
}
- PressIndicatorGestureDetector(onStart, onStop, onStop) {
- PressReleasedGestureDetector(onRelease, false) {
- LongPressGestureDetector(onLongPress) {
- Layout(children) { _, constraints ->
- layout(
- constraints.maxWidth,
- height.toIntPx().coerceIn(constraints.minHeight, constraints.maxHeight)
- ) {}
+ PressIndicatorGestureDetector(onPress, onRelease, onRelease) {
+ PressReleasedGestureDetector(onTap, false) {
+ DoubleTapGestureDetector(onDoubleTap) {
+ LongPressGestureDetector(onLongPress) {
+ Layout(children) { _, constraints ->
+ layout(
+ constraints.maxWidth,
+ height.toIntPx().coerceIn(constraints.minHeight, constraints.maxHeight)
+ ) {}
+ }
}
}
}
@@ -215,7 +221,7 @@
* A simple composable that arranges it's children as vertical list of items.
*/
@Composable
-private fun Column(@Children children: @Composable() () -> Unit) {
+private fun Column(children: @Composable() () -> Unit) {
Layout(children) { measurables, constraints ->
var height = 0.ipx
val placeables = measurables.map {
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/SimpleComposables.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/SimpleComposables.kt
index e49a48e..dc07bc9 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/SimpleComposables.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/SimpleComposables.kt
@@ -35,7 +35,7 @@
* A simple layout composable that matches the size of it's parent layout.
*/
@Composable
-internal fun MatchParent(@Children children: @Composable() () -> Unit) {
+internal fun MatchParent(children: @Composable() () -> Unit) {
Layout({
children()
}, { _, constraints ->
@@ -44,7 +44,7 @@
}
@Composable
-internal fun Center(@Children children: @Composable() () -> Unit) {
+internal fun Center(children: @Composable() () -> Unit) {
Layout(children) { measurables, constraints ->
val placeable = measurables.first().measure(constraints)
layout(constraints.maxWidth, constraints.maxHeight) {
@@ -59,7 +59,7 @@
* A simple composable that pads items by [padding].
*/
@Composable
-private fun Padding(padding: Dp?, @Children children: @Composable() () -> Unit) {
+private fun Padding(padding: Dp?, children: @Composable() () -> Unit) {
Layout(children) { measurables, constraints ->
val paddingPx = padding?.toIntPx() ?: 0.ipx
val doublePadding = paddingPx * 2
@@ -90,7 +90,7 @@
* A simple composable that draws a border around it's children.
*/
@Composable
-private fun Border(color: Color, width: Dp, @Children children: @Composable() () -> Unit) {
+private fun Border(color: Color, width: Dp, children: @Composable() () -> Unit) {
Layout(
children = {
children()
@@ -140,7 +140,7 @@
width: Dp,
height: Dp,
padding: Dp,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val borderWidth: Dp = 2.dp
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
index 2318041..c8866dc 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
@@ -426,14 +426,17 @@
val header = @Composable {
Layout(layoutBlock = { _, constraints ->
assertEquals(childConstraints[0], constraints)
+ layout(0.ipx, 0.ipx) {}
}, children = {})
}
val footer = @Composable {
Layout(layoutBlock = { _, constraints ->
assertEquals(childConstraints[1], constraints)
+ layout(0.ipx, 0.ipx) {}
}, children = {})
Layout(layoutBlock = { _, constraints ->
assertEquals(childConstraints[2], constraints)
+ layout(0.ipx, 0.ipx) {}
}, children = {})
}
@Suppress("USELESS_CAST")
@@ -447,6 +450,7 @@
assertEquals(footerChildrenCount, measurables[footer].size)
assertSame(measurables[1], measurables[footer][0])
assertSame(measurables[2], measurables[footer][1])
+ layout(0.ipx, 0.ipx) {}
}
}
}
@@ -458,18 +462,19 @@
activity.setContent {
val header = @Composable {
ParentData(data = 0) {
- Layout(layoutBlock = { _, _ -> }, children = {})
+ Layout(layoutBlock = { _, _ -> layout(0.ipx, 0.ipx, {}) }, children = {})
}
}
val footer = @Composable {
ParentData(data = 1) {
- Layout(layoutBlock = { _, _ -> }, children = {})
+ Layout(layoutBlock = { _, _ -> layout(0.ipx, 0.ipx, {}) }, children = {})
}
}
Layout(childrenArray = arrayOf(header, footer)) { measurables, _ ->
assertEquals(0, measurables[0].parentData)
assertEquals(1, measurables[1].parentData)
+ layout(0.ipx, 0.ipx, {})
}
}
}
@@ -553,7 +558,7 @@
fun FixedSizeRow(
width: IntPx,
height: IntPx,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Layout(children = children, layoutBlock = { measurables, constraints ->
val resolvedWidth = width.coerceIn(constraints.minWidth, constraints.maxWidth)
@@ -936,7 +941,7 @@
}
@Composable
-fun AtLeastSize(size: IntPx, @Children children: @Composable() () -> Unit) {
+fun AtLeastSize(size: IntPx, children: @Composable() () -> Unit) {
Layout(
layoutBlock = { measurables, constraints ->
val newConstraints = Constraints(
@@ -964,7 +969,7 @@
}
@Composable
-fun Align(@Children children: @Composable() () -> Unit) {
+fun Align(children: @Composable() () -> Unit) {
Layout(
layoutBlock = { measurables, constraints ->
val newConstraints = Constraints(
@@ -992,7 +997,7 @@
}
@Composable
-fun Padding(size: IntPx, @Children children: @Composable() () -> Unit) {
+fun Padding(size: IntPx, children: @Composable() () -> Unit) {
Layout(
layoutBlock = { measurables, constraints ->
val totalDiff = size * 2
@@ -1024,7 +1029,7 @@
fun TwoMeasureLayout(
size: IntPx,
latch: CountDownLatch,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Layout(children = children) { measurables, _ ->
val testConstraints = Constraints()
@@ -1042,11 +1047,12 @@
// expected
latch.countDown()
}
+ layout(0.ipx, 0.ipx, {})
}
}
@Composable
-fun Position(size: IntPx, offset: OffsetModel, @Children children: @Composable() () -> Unit) {
+fun Position(size: IntPx, offset: OffsetModel, children: @Composable() () -> Unit) {
Layout(children) { measurables, constraints ->
val placeables = measurables.map { m ->
m.measure(constraints)
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/TextLayoutTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/TextLayoutTest.kt
index e31200b..ccfa929 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/TextLayoutTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/TextLayoutTest.kt
@@ -137,7 +137,7 @@
var threw = false
try {
textMeasurable.minIntrinsicWidth(0.ipx)
- } catch(e: UnsupportedOperationException) {
+ } catch (e: UnsupportedOperationException) {
threw = true
}
assertTrue(threw)
@@ -153,6 +153,8 @@
assertEquals(textHeight, textMeasurable.maxIntrinsicHeight(IntPx.Infinity))
intrinsicsLatch.countDown()
+
+ layoutResult(0.ipx, 0.ipx) {}
}
minIntrinsicWidth { _, _ -> 0.ipx }
minIntrinsicHeight { _, _ -> 0.ipx }
@@ -163,7 +165,7 @@
assertTrue(intrinsicsLatch.await(1, TimeUnit.SECONDS))
}
- private fun show(@Children composable: @Composable() () -> Unit) {
+ private fun show(composable: @Composable() () -> Unit) {
val runnable: Runnable = object : Runnable {
override fun run() {
activity.setContent {
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParserTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParserTest.kt
new file mode 100644
index 0000000..2fcd9a2
--- /dev/null
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParserTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 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.ui.core.vectorgraphics.compat
+
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.ui.core.Px
+
+import androidx.ui.core.vectorgraphics.VectorPath
+import androidx.ui.framework.test.R
+import androidx.ui.graphics.vectorgraphics.PathCommand
+import androidx.ui.graphics.vectorgraphics.PathNode
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class XmlVectorParserTest {
+
+ @Test
+ fun testParseXml() {
+ val res = InstrumentationRegistry.getInstrumentation().targetContext.resources
+ val asset = loadVectorResource(null, res, R.drawable.test_compose_vector)
+ val density = res.displayMetrics.density
+ val expectedSize = Px(density*24)
+ assertEquals(expectedSize, asset.defaultWidth)
+ assertEquals(expectedSize, asset.defaultHeight)
+ assertEquals(24.0f, asset.viewportWidth)
+ assertEquals(24.0f, asset.viewportHeight)
+ assertEquals(1, asset.root.size)
+
+ val node = asset.root.iterator().next() as VectorPath
+ assertEquals(0xFFFF0000.toInt(), node.fill as Int)
+
+ @Suppress("UNCHECKED_CAST")
+ val path = node.pathData as Array<PathNode>
+ assertEquals(3, path.size)
+ assertEquals(PathCommand.MoveTo, path.get(0).command)
+ assertEquals(20.0f, path.get(0).args[0])
+ assertEquals(10.0f, path.get(0).args[1])
+
+ assertEquals(PathCommand.RelativeLineTo, path.get(1).command)
+ assertEquals(6, path.get(1).args.size)
+ assertEquals(10.0f, path.get(1).args[0])
+ assertEquals(0.0f, path.get(1).args[1])
+ assertEquals(0.0f, path.get(1).args[2])
+ assertEquals(10.0f, path.get(1).args[3])
+ assertEquals(-10.0f, path.get(1).args[4])
+ assertEquals(0.0f, path.get(1).args[5])
+
+ assertEquals(PathCommand.RelativeClose, path.get(2).command)
+ assertEquals(0, path.get(2).args.size)
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/androidTest/res/drawable/test_compose_vector.xml b/ui/ui-framework/src/androidTest/res/drawable/test_compose_vector.xml
new file mode 100644
index 0000000..05beeb3
--- /dev/null
+++ b/ui/ui-framework/src/androidTest/res/drawable/test_compose_vector.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFF0000"
+ android:pathData="M20,10, l10,0 0,10 -10, 0z"/>
+</vector>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Clip.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Clip.kt
index be6e0ff..294325e 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Clip.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Clip.kt
@@ -27,7 +27,7 @@
* @param shape the [Shape] used for clipping.
*/
@Composable
-fun Clip(shape: Shape, @Children children: @Composable() () -> Unit) {
+fun Clip(shape: Shape, children: @Composable() () -> Unit) {
<RepaintBoundaryNode name=null shape=shape clipToShape=true>
children()
</RepaintBoundaryNode>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
index 4686cc3..1295a2c 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
@@ -27,12 +27,22 @@
import androidx.compose.trace
import androidx.compose.unaryPlus
-internal typealias LayoutBlock = LayoutBlockReceiver.(List<Measurable>, Constraints) -> Unit
+internal typealias LayoutBlock = LayoutBlockReceiver.(List<Measurable>, Constraints) -> LayoutResult
internal typealias IntrinsicMeasurementBlock = IntrinsicMeasurementsReceiver
.(List<Measurable>, IntPx) -> IntPx
-internal val LayoutBlockStub: LayoutBlock = { _, _ -> }
+internal val LayoutBlockStub: LayoutBlock = { _, _ -> LayoutResult.instance }
internal val IntrinsicMeasurementBlockStub: IntrinsicMeasurementBlock = { _, _ -> 0.ipx }
+/**
+ * Object returned from [LayoutReceiver.layout] and [LayoutBlockReceiver.layoutResult] to deliver
+ * proof the method was called in the respective layout lambdas.
+ */
+class LayoutResult private constructor() {
+ companion object {
+ internal val instance = LayoutResult()
+ }
+}
+
internal class ComplexLayoutState(
internal var layoutBlock: LayoutBlock = LayoutBlockStub,
internal var minIntrinsicWidthBlock: IntrinsicMeasurementBlock = IntrinsicMeasurementBlockStub,
@@ -315,9 +325,10 @@
width: IntPx,
height: IntPx,
block: PositioningBlockReceiver.() -> Unit
- ) {
+ ): LayoutResult {
layoutState.resize(width, height)
layoutState.positioningBlock = block
+ return LayoutResult.instance
}
fun Measurable.minIntrinsicWidth(h: IntPx) =
(this as ComplexLayoutState).minIntrinsicWidth(h)
@@ -345,7 +356,7 @@
fun Layout(
children: @Composable() () -> Unit,
@Children(composable = false) layoutBlock: LayoutReceiver
- .(measurables: List<Measurable>, constraints: Constraints) -> Unit
+ .(measurables: List<Measurable>, constraints: Constraints) -> LayoutResult
) {
trace("UI:Layout") {
ComplexLayout(children = children, block = {
@@ -356,7 +367,7 @@
)
layout { measurables, constraints ->
layoutReceiver.complexMeasure = LayoutMeasure
- layoutReceiver.complexLayoutResult = this::layoutResult
+ layoutReceiver.complexLayoutResult = { w, h, block -> layoutResult(w, h, block) }
layoutReceiver.layoutBlock(measurables, constraints)
}
@@ -438,7 +449,7 @@
fun Layout(
childrenArray: Array<@Composable() () -> Unit>,
@Children(composable = false) layoutBlock: LayoutReceiver
- .(measurables: List<Measurable>, constraints: Constraints) -> Unit
+ .(measurables: List<Measurable>, constraints: Constraints) -> LayoutResult
) {
val ChildrenEndMarker = @Composable { children: @Composable() () -> Unit ->
ParentData(data = ChildrenEndParentData(children)) {
@@ -487,8 +498,13 @@
* calls to [Placeable.place], defining the positions of the children relative to the current
* layout.
*/
- fun layout(width: IntPx, height: IntPx, block: PositioningBlockReceiver.() -> Unit) {
+ fun layout(
+ width: IntPx,
+ height: IntPx,
+ block: PositioningBlockReceiver.() -> Unit
+ ): LayoutResult {
complexLayoutResult(width, height, block)
+ return LayoutResult.instance
}
}
@@ -516,7 +532,7 @@
* Please note that using this widget might be a performance hit, so please use with care.
*/
@Composable
-fun WithConstraints(@Children children: @Composable() (Constraints) -> Unit) {
+fun WithConstraints(children: @Composable() (Constraints) -> Unit) {
val ref = +compositionReference()
val context = +ambient(ContextAmbient)
@@ -592,7 +608,7 @@
@Composable
fun OnChildPositioned(
onPositioned: (coordinates: LayoutCoordinates) -> Unit,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
<DataNode key=OnChildPositionedKey value=onPositioned>
<children/>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Opacity.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Opacity.kt
index 7399e1a..bc1881a 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Opacity.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Opacity.kt
@@ -33,7 +33,7 @@
@Composable
fun Opacity(
@FloatRange(from = 0.0, to = 1.0) opacity: Float,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
<RepaintBoundaryNode name=null opacity=opacity>
children()
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/ParentData.kt b/ui/ui-framework/src/main/java/androidx/ui/core/ParentData.kt
index 8ae7c49..081b3daf 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/ParentData.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/ParentData.kt
@@ -53,7 +53,7 @@
* expanded, which are inflexible and which are flexible.
*/
@Composable
-fun ParentData(data: Any, @Children children: @Composable() () -> Unit) {
+fun ParentData(data: Any, children: @Composable() () -> Unit) {
<DataNode key=ParentDataKey value=data>
<children/>
</DataNode>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/PointerInputWrapper.kt b/ui/ui-framework/src/main/java/androidx/ui/core/PointerInputWrapper.kt
index ec66af4..d6e0e77 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/PointerInputWrapper.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/PointerInputWrapper.kt
@@ -24,7 +24,7 @@
@Composable
fun PointerInputWrapper(
pointerInputHandler: PointerInputHandler = { event, _ -> event },
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
// Hide the internals of PointerInputNode
<PointerInputNode pointerInputHandler>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/RepaintBoundary.kt b/ui/ui-framework/src/main/java/androidx/ui/core/RepaintBoundary.kt
index 757e740..cbf1760 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/RepaintBoundary.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/RepaintBoundary.kt
@@ -31,7 +31,7 @@
* @param children The contained children.
*/
@Composable
-fun RepaintBoundary(name: String? = null, @Children children: @Composable() () -> Unit) {
+fun RepaintBoundary(name: String? = null, children: @Composable() () -> Unit) {
<RepaintBoundaryNode name=name>
<children/>
</RepaintBoundaryNode>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Semantics.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Semantics.kt
index baa4ff3..dad6816 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Semantics.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Semantics.kt
@@ -124,7 +124,7 @@
textDirection: TextDirection? = null,
testTag: String? = null,
actions: List<SemanticsAction<*>> = emptyList(),
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val providedTestTag = +ambient(TestTagAmbient)
<SemanticsComponentNode
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/TestTagProvider.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TestTagProvider.kt
index e69bf03..a75a022 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/TestTagProvider.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/TestTagProvider.kt
@@ -27,6 +27,6 @@
// Implementation with ambients now for only one semantics inside.
// replace with mergeable semantics later
@Composable
-fun TestTag(tag: String, @Children children: @Composable() () -> Unit) {
+fun TestTag(tag: String, children: @Composable() () -> Unit) {
TestTagAmbient.Provider(value = tag, children = children)
}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
index 7e1680cb..4d0fd2a 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
@@ -97,7 +97,7 @@
/**
* Composable TextSpan attached after [text].
*/
- @Children child: @Composable TextSpanScope.() -> Unit
+ child: @Composable TextSpanScope.() -> Unit
) {
val rootTextSpan = +memo(text) { TextSpan(text = text) }
val ref = +compositionReference()
@@ -321,7 +321,7 @@
* styled explicitly.
*/
@Composable
-fun CurrentTextStyleProvider(value: TextStyle, @Children children: @Composable() () -> Unit) {
+fun CurrentTextStyleProvider(value: TextStyle, children: @Composable() () -> Unit) {
val style = +ambient(CurrentTextStyleAmbient)
val mergedStyle = style.merge(value)
CurrentTextStyleAmbient.Provider(value = mergedStyle) {
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt
index 6964f89..656a8a4 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt
@@ -98,6 +98,12 @@
/** Called when the InputMethodService update the editor state */
onValueChange: (EditorModel) -> Unit = {},
+ /** Called when the input field gains focus. */
+ onFocus: () -> Unit = {},
+
+ /** Called when the input field loses focus. */
+ onBlur: () -> Unit = {},
+
/** Called when the InputMethod requested an IME action */
onImeActionPerformed: (ImeAction) -> Unit = {},
@@ -157,6 +163,7 @@
)
}
}
+ onFocus()
},
onBlur = {
hasFocus.value = false
@@ -164,6 +171,7 @@
textInputService,
processor,
onValueChange)
+ onBlur()
},
onDragAt = { TextFieldDelegate.onDragAt(it) },
onRelease = {
@@ -221,7 +229,7 @@
onRelease: (PxPosition) -> Unit,
onFocus: () -> Unit,
onBlur: () -> Unit,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val focused = +state { false }
val focusManager = +ambient(FocusManagerAmbient)
@@ -294,7 +302,7 @@
onPress: (PxPosition) -> Unit,
onDragAt: (PxPosition) -> Unit,
onRelease: (PxPosition) -> Unit,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val tracker = +state { DragEventTracker() }
PressGestureDetector(
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt
index 1af1687..8d8a56f 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt
@@ -256,7 +256,7 @@
fun TextSpanScope.Span(
text: String? = null,
style: TextStyle? = null,
- @Children child: @Composable TextSpanScope.() -> Unit
+ child: @Composable TextSpanScope.() -> Unit
) {
TextSpan(text = text, style = style) {
child()
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Wrapper.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Wrapper.kt
index 45f178a..ed51529 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Wrapper.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Wrapper.kt
@@ -47,7 +47,7 @@
* [Activity.setContent] or [ViewGroup.setContent] extensions.
*/
@Composable
-fun ComposeView(@Children children: @Composable() () -> Unit) {
+fun ComposeView(children: @Composable() () -> Unit) {
val rootRef = +memo { Ref<AndroidCraneView>() }
<AndroidCraneView ref=rootRef>
@@ -100,7 +100,7 @@
* @param content Composable that will be the content of the activity.
*/
fun Activity.setContent(
- @Children content: @Composable() () -> Unit
+ content: @Composable() () -> Unit
): CompositionContext? {
val craneView = window.decorView
.findViewById<ViewGroup>(android.R.id.content)
@@ -123,7 +123,7 @@
* @param content Composable that will be the content of the view.
*/
fun ViewGroup.setContent(
- @Children content: @Composable() () -> Unit
+ content: @Composable() () -> Unit
): CompositionContext? {
val craneView =
if (childCount > 0) { getChildAt(0) as? AndroidCraneView } else { removeAllViews(); null }
@@ -144,7 +144,7 @@
craneView: AndroidCraneView,
context: Context,
coroutineContext: CoroutineContext,
- @Children content: @Composable() () -> Unit
+ content: @Composable() () -> Unit
) {
// TODO(nona): Tie the focus manger lifecycle to Window, otherwise FocusManager won't work
// with nested AndroidCraneView case
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DoubleTapGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DoubleTapGestureDetector.kt
new file mode 100644
index 0000000..c887644
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DoubleTapGestureDetector.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019 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.ui.core.gesture
+
+import androidx.ui.core.PointerEventPass
+import androidx.ui.core.PointerInputChange
+import androidx.ui.core.changedToDown
+import androidx.compose.Children
+import androidx.compose.Composable
+import androidx.compose.ambient
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.core.changedToUp
+import androidx.compose.composer
+import androidx.ui.core.CoroutineContextAmbient
+import androidx.ui.core.PointerInputWrapper
+import androidx.ui.core.PxPosition
+import androidx.ui.core.anyPositionChangeConsumed
+import androidx.ui.core.changedToUpIgnoreConsumed
+import androidx.ui.core.consumeDownChange
+import androidx.ui.temputils.delay
+import kotlinx.coroutines.Job
+import kotlin.coroutines.CoroutineContext
+
+// TODO(b/138605697): This bug tracks the note below: DoubleTapGestureDetector should use the
+// eventual api that will allow it to temporary block tap.
+// TODO(b/138754591): The behavior of this gesture detector needs to be finalized.
+/**
+ * Responds to pointers going up, down within a small duration, and then up again.
+ *
+ * Note: This is a temporary implementation to unblock dependents. Once the underlying API that
+ * allows double tap to temporarily block tap from firing is complete, this gesture detector will
+ * not block tap when the first "up" occurs. It will however block the 2nd up from causing tap to
+ * fire.
+ *
+ * Also, given that this gesture detector is so temporary, opting to not write substantial tests.
+ */
+@Composable
+fun DoubleTapGestureDetector(
+ onDoubleTap: (PxPosition) -> Unit,
+ children: @Composable() () -> Unit
+) {
+ val recognizer =
+ +memo { DoubleTapGestureRecognizer(onDoubleTap, +ambient(CoroutineContextAmbient)) }
+ PointerInputWrapper(pointerInputHandler = recognizer.pointerInputHandler) {
+ children()
+ }
+}
+
+internal class DoubleTapGestureRecognizer(
+ val onDoubleTap: (PxPosition) -> Unit,
+ coroutineContext: CoroutineContext
+) {
+
+ private enum class State {
+ Idle, Down, Up, SecondDown, Cancelled
+ }
+
+ var doubleTapTimeout = DoubleTapTimeout
+ private var state = State.Idle
+ private var job: Job? = null
+
+ val pointerInputHandler =
+ { changes: List<PointerInputChange>, pass: PointerEventPass ->
+
+ var changesToReturn = changes
+
+ if (pass == PointerEventPass.PostUp) {
+ if (state == State.Idle && changesToReturn.all { it.changedToDown() }) {
+ state = State.Down
+ } else if (state == State.Down && changesToReturn.all { it.changedToUp() }) {
+ state = State.Up
+ job = delay(doubleTapTimeout, coroutineContext) {
+ state = State.Idle
+ }
+ } else if (state == State.Up && changesToReturn.all { it.changedToDown() }) {
+ job?.cancel()
+ state = State.SecondDown
+ } else if (state == State.SecondDown && changesToReturn.all { it.changedToUp() }) {
+ changesToReturn = changesToReturn.map { it.consumeDownChange() }
+ state = State.Idle
+ onDoubleTap.invoke(changes[0].previous.position!!)
+ } else if (state == State.Cancelled &&
+ changesToReturn.all { it.changedToUpIgnoreConsumed() }
+ ) {
+ state = State.Idle
+ }
+ }
+
+ if (pass == PointerEventPass.PostDown &&
+ changesToReturn.any { it.anyPositionChangeConsumed() }
+ ) {
+ state = State.Cancelled
+ }
+
+ changesToReturn
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DragGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DragGestureDetector.kt
index 95cc8eb..0692d57 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DragGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DragGestureDetector.kt
@@ -100,7 +100,7 @@
fun DragGestureDetector(
canDrag: ((Direction) -> Boolean)? = null,
dragObserver: DragObserver? = null,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val recognizer = +memo { DragGestureRecognizer() }
recognizer.canDrag = canDrag
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/LongPressGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/LongPressGestureDetector.kt
index a3f894c..d269572 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/LongPressGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/LongPressGestureDetector.kt
@@ -46,7 +46,7 @@
@Composable
fun LongPressGestureDetector(
onLongPress: (PxPosition) -> Unit,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val recognizer =
+memo { LongPressGestureRecognizer(onLongPress, +ambient(CoroutineContextAmbient)) }
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressGestureDetector.kt
index 6d8beb2..e91a1e34 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressGestureDetector.kt
@@ -26,7 +26,7 @@
onPress: ((PxPosition) -> Unit)? = null,
onRelease: (() -> Unit)? = null,
onCancel: (() -> Unit)? = null,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
PressIndicatorGestureDetector(onStart = onPress, onCancel = onCancel) {
PressReleasedGestureDetector(onRelease = onRelease, consumeDownOnStart = false) {
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressIndicatorGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressIndicatorGestureDetector.kt
index 7705035..423b917 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressIndicatorGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressIndicatorGestureDetector.kt
@@ -53,7 +53,7 @@
onStart: ((PxPosition) -> Unit)? = null,
onStop: (() -> Unit)? = null,
onCancel: (() -> Unit)? = null,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val recognizer = +memo { PressIndicatorGestureRecognizer() }
recognizer.onStart = onStart
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressReleasedGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressReleasedGestureDetector.kt
index a524b3f..99a0daa 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressReleasedGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressReleasedGestureDetector.kt
@@ -52,7 +52,7 @@
fun PressReleasedGestureDetector(
onRelease: (() -> Unit)? = null,
consumeDownOnStart: Boolean = true,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val recognizer = +memo { PressReleaseGestureRecognizer() }
recognizer.onRelease = onRelease
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
index a3bd5e5..6dfb42c 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
@@ -44,7 +44,7 @@
onSelectionChange: (Selection?) -> Unit,
/** Selection mode. The default mode is Vertical. */
mode: SelectionMode = SelectionMode.Vertical,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val manager = +memo { SelectionManager() }
// TODO (qqd): After selection widget is fully implemented, evaluate if the following 2 items
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Brush.kt b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Brush.kt
deleted file mode 100644
index b8fd184..0000000
--- a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Brush.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2019 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.ui.core.vectorgraphics
-
-import androidx.ui.engine.geometry.Offset
-import androidx.ui.graphics.Color
-import androidx.ui.painting.Gradient
-import androidx.ui.painting.Paint
-import androidx.ui.painting.TileMode
-import androidx.ui.vectormath64.Matrix4
-
-val EmptyBrush = object : Brush {
- override fun applyBrush(p: Paint) {
- // NO-OP
- }
-}
-
-interface Brush {
- fun applyBrush(p: Paint)
-}
-
-/* inline */ class SolidColor(private val value: Color) : Brush {
- override fun applyBrush(p: Paint) {
- p.color = value
- }
-}
-
-typealias ColorStop = Pair<Color, Float>
-
-fun obtainBrush(brush: Any?): Brush {
- return when (brush) {
- is Int -> SolidColor(Color(brush))
- is Color -> SolidColor(brush)
- is Brush -> brush
- null -> EmptyBrush
- else -> throw IllegalArgumentException(brush.javaClass.simpleName +
- "Brush must be either a Color long, LinearGradient or RadialGradient")
- }
-}
-
-// TODO (njawad) replace with inline color class
-class LinearGradient(
- vararg colorStops: ColorStop,
- val startX: Float,
- val startY: Float,
- val endX: Float,
- val endY: Float,
- val tileMode: TileMode = TileMode.clamp
-) : Brush {
-
- private val colors: List<Color>
- private val stops: List<Float>
-
- init {
- colors = List(colorStops.size) { i -> colorStops[i].first }
- stops = List(colorStops.size) { i -> colorStops[i].second }
- }
-
- override fun applyBrush(p: Paint) {
- p.shader = Gradient.linear(
- Offset(startX, startY),
- Offset(endX, endY),
- colors,
- stops,
- tileMode)
- }
-}
-
-class RadialGradient(
- vararg colorStops: ColorStop,
- private val centerX: Float,
- private val centerY: Float,
- private val radius: Float,
- private val tileMode: TileMode = TileMode.clamp
-) : Brush {
-
- private val colors: List<Color>
- private val stops: List<Float>
-
- init {
- colors = List(colorStops.size) { it -> colorStops[it].first }
- stops = List(colorStops.size) { it -> colorStops[it].second }
- }
-
- override fun applyBrush(p: Paint) {
- p.shader = Gradient.radial(
- Offset(centerX, centerY),
- radius, colors, stops, tileMode, Matrix4(),
- null, 0.0f)
- }
-}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/VectorAsset.kt b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/VectorAsset.kt
new file mode 100644
index 0000000..80071b1
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/VectorAsset.kt
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2019 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.ui.core.vectorgraphics
+
+import androidx.compose.Composable
+import androidx.compose.composer
+import androidx.ui.core.Px
+import androidx.ui.graphics.vectorgraphics.BrushType
+import androidx.ui.graphics.vectorgraphics.DefaultAlpha
+import androidx.ui.graphics.vectorgraphics.DefaultGroupName
+import androidx.ui.graphics.vectorgraphics.DefaultPathName
+import androidx.ui.graphics.vectorgraphics.DefaultPivotX
+import androidx.ui.graphics.vectorgraphics.DefaultPivotY
+import androidx.ui.graphics.vectorgraphics.DefaultRotation
+import androidx.ui.graphics.vectorgraphics.DefaultScaleX
+import androidx.ui.graphics.vectorgraphics.DefaultScaleY
+import androidx.ui.graphics.vectorgraphics.DefaultStrokeLineCap
+import androidx.ui.graphics.vectorgraphics.DefaultStrokeLineJoin
+import androidx.ui.graphics.vectorgraphics.DefaultStrokeLineMiter
+import androidx.ui.graphics.vectorgraphics.DefaultStrokeLineWidth
+import androidx.ui.graphics.vectorgraphics.DefaultTranslationX
+import androidx.ui.graphics.vectorgraphics.DefaultTranslationY
+import androidx.ui.graphics.vectorgraphics.EmptyBrush
+import androidx.ui.graphics.vectorgraphics.EmptyPath
+import androidx.ui.graphics.vectorgraphics.PathData
+import androidx.ui.painting.StrokeCap
+import androidx.ui.painting.StrokeJoin
+import androidx.ui.vector.VectorScope
+import java.util.Stack
+import java.util.function.Consumer
+
+/**
+ * Builder used to construct a Vector graphic tree.
+ * This is useful for caching the result of expensive operations used to construct
+ * a vector graphic for compose.
+ * For example, the vector graphic could be serialized and downloaded from a server and represented
+ * internally in a VectorAsset before it is composed through [DrawVector]
+ * The generated VectorAsset is recommended to be memoized across composition calls to avoid
+ * doing redundant work
+ **/
+class VectorAssetBuilder(
+
+ /**
+ * Name of the vector asset
+ */
+ val name: String = DefaultGroupName,
+
+ /**
+ * Intrinsic width of the Vector in pixels
+ */
+ val defaultWidth: Px,
+
+ /**
+ * Intrinsic height of the Vector in pixels
+ */
+ val defaultHeight: Px,
+
+ /**
+ * Used to define the width of the viewport space. Viewport is basically the virtual canvas
+ * where the paths are drawn on.
+ */
+ val viewportWidth: Float,
+
+ /**
+ * Used to define the height of the viewport space. Viewport is basically the virtual canvas
+ * where the paths are drawn on.
+ */
+ val viewportHeight: Float
+) {
+
+ private val nodes = Stack<VectorGroup>()
+
+ private var root = VectorGroup()
+ private var isConsumed = false
+
+ private var currentGroup: VectorGroup = root
+ private set
+ get() = nodes.peek()
+
+ init {
+ nodes.add(root)
+ }
+
+ /**
+ * Create a new group and push it to the front of the stack of VectorAsset nodes
+ * @return This VectorAssetBuilder instance as a convenience for chaining calls
+ */
+ fun pushGroup(
+ name: String = DefaultGroupName,
+ rotate: Float = DefaultRotation,
+ pivotX: Float = DefaultPivotX,
+ pivotY: Float = DefaultPivotY,
+ scaleX: Float = DefaultScaleX,
+ scaleY: Float = DefaultScaleY,
+ translationX: Float = DefaultTranslationX,
+ translationY: Float = DefaultTranslationY,
+ clipPathData: PathData = EmptyPath
+ ): VectorAssetBuilder {
+ ensureNotConsumed()
+ val group = VectorGroup(
+ name,
+ rotate,
+ pivotX,
+ pivotY,
+ scaleX,
+ scaleY,
+ translationX,
+ translationY,
+ clipPathData
+ )
+ nodes.add(group)
+ currentGroup.addNode(group)
+ return this
+ }
+
+ /**
+ * Pops the topmost VectorGroup from this VectorAssetBuilder. This is used to indicate
+ * that no additional VectorAsset nodes will be added to the current VectorGroup
+ * @return This VectorAssetBuilder instance as a convenience for chaining calls
+ */
+ fun popGroup(): VectorAssetBuilder {
+ ensureNotConsumed()
+ nodes.pop()
+ return this
+ }
+
+ /**
+ * Add a path to the VectorAsset graphic. This represents a leaf node in the VectorAsset graphics
+ * tree structure
+ * @return This VectorAssetBuilder instance as a convenience for chaining calls
+ */
+ fun addPath(
+ pathData: PathData,
+ name: String = DefaultPathName,
+ fill: BrushType = EmptyBrush,
+ fillAlpha: Float = DefaultAlpha,
+ stroke: BrushType = EmptyBrush,
+ strokeAlpha: Float = DefaultAlpha,
+ strokeLineWidth: Float = DefaultStrokeLineWidth,
+ strokeLineCap: StrokeCap = DefaultStrokeLineCap,
+ strokeLineJoin: StrokeJoin = DefaultStrokeLineJoin,
+ strokeLineMiter: Float = DefaultStrokeLineMiter
+ ): VectorAssetBuilder {
+ ensureNotConsumed()
+ currentGroup.addNode(
+ VectorPath(
+ name,
+ pathData,
+ fill,
+ fillAlpha,
+ stroke,
+ strokeAlpha,
+ strokeLineWidth,
+ strokeLineCap,
+ strokeLineJoin,
+ strokeLineMiter
+ )
+ )
+ return this
+ }
+
+ /**
+ * Construct a VectorAsset. This concludes the creation process of a VectorAsset graphic
+ * This builder cannot be re-used to create additional VectorAsset instances
+ * @return Thew newly created VectorAsset instance
+ */
+ fun build(): VectorAsset {
+ ensureNotConsumed()
+ val vectorImage = VectorAsset(
+ name,
+ defaultWidth,
+ defaultHeight,
+ viewportWidth,
+ viewportHeight,
+ root
+ )
+
+ // reset state in case this builder is used again
+ nodes.clear()
+ root = VectorGroup()
+ nodes.add(root)
+
+ isConsumed = true
+
+ return vectorImage
+ }
+
+ /**
+ * Throws IllegalStateException if the VectorAssetBuilder is already been consumed
+ */
+ fun ensureNotConsumed() {
+ if (isConsumed) {
+ throw IllegalStateException("VectorAssetBuilder is single use, create " +
+ "a new instance to create a new VectorAsset")
+ }
+ }
+}
+
+sealed class VectorNode
+
+/**
+ * Vector graphics object that is generated as a result of [VectorAssetBuilder]]
+ * It can be composed and rendered by passing it as an argument to [DrawVector]
+ */
+class VectorAsset internal constructor(
+
+ /**
+ * Name of the Vector asset
+ */
+ val name: String,
+
+ /**
+ * Intrinsic width of the vector asset in pixels
+ */
+ val defaultWidth: Px,
+
+ /**
+ * Intrinsic height of the vector asset in pixels
+ */
+ val defaultHeight: Px,
+
+ /**
+ * Used to define the width of the viewport space. Viewport is basically the virtual canvas
+ * where the paths are drawn on.
+ */
+ val viewportWidth: Float,
+
+ /**
+ * Used to define the height of the viewport space. Viewport is basically the virtual canvas
+ * where the paths are drawn on.
+ */
+ val viewportHeight: Float,
+
+ /**
+ * Root group of the vector asset that contains all the child groups and paths
+ */
+ val root: VectorGroup
+)
+
+/**
+ * Defines a group of paths or subgroups, plus transformation information.
+ * The transformations are defined in the same coordinates as the viewport.
+ * The transformations are applied in the order of scale, rotate then translate.
+ */
+class VectorGroup(
+ /**
+ * Name of the corresponding group
+ */
+ val name: String = DefaultGroupName,
+
+ /**
+ * Rotation of the group in degrees
+ */
+ val rotation: Float = DefaultRotation,
+
+ /**
+ * X coordinate of the pivot point to rotate or scale the group
+ */
+ val pivotX: Float = DefaultPivotX,
+
+ /**
+ * Y coordinate of the pivot point to rotate or scale the group
+ */
+ val pivotY: Float = DefaultPivotY,
+
+ /**
+ * Scale factor in the X-axis to apply to the group
+ */
+ val scaleX: Float = DefaultScaleX,
+
+ /**
+ * Scale factor in the Y-axis to apply to the group
+ */
+ val scaleY: Float = DefaultScaleY,
+
+ /**
+ * Translation in virtual pixels to apply along the x-axis
+ */
+ val translationX: Float = DefaultTranslationX,
+
+ /**
+ * Translation in virtual pixels to apply along the y-axis
+ */
+ val translationY: Float = DefaultTranslationY,
+
+ /**
+ * Path information used to clip the content within the group
+ */
+ val clipPathData: PathData = EmptyPath
+
+) : VectorNode(), Iterable<VectorNode> {
+
+ private val children = ArrayList<VectorNode>()
+
+ internal fun addNode(node: VectorNode) {
+ children.add(node)
+ }
+
+ val size: Int
+ get() = children.size
+
+ operator fun get(index: Int): VectorNode {
+ return children[index]
+ }
+
+ override fun iterator(): Iterator<VectorNode> {
+ return object: Iterator<VectorNode> {
+
+ val it = children.iterator()
+
+ override fun hasNext(): Boolean = it.hasNext()
+
+ override fun next(): VectorNode = it.next()
+ }
+ }
+}
+
+/**
+ * Leaf node of a Vector graphics tree. This specifies a path shape and parameters
+ * to color and style the the shape itself
+ */
+class VectorPath(
+ /**
+ * Name of the corresponding path
+ */
+ val name: String = DefaultPathName,
+
+ /**
+ * Path information to render the shape of the path
+ */
+ val pathData: PathData,
+
+ /**
+ * Specifies the color or gradient used to fill the path
+ */
+ val fill: BrushType = EmptyBrush,
+
+ /**
+ * Opacity to fill the path
+ */
+ val fillAlpha: Float = DefaultAlpha,
+
+ /**
+ * Specifies the color or gradient used to fill the stroke
+ */
+ val stroke: BrushType = EmptyBrush,
+
+ /**
+ * Opacity to stroke the path
+ */
+ val strokeAlpha: Float = DefaultAlpha,
+
+ /**
+ * Width of the line to stroke the path
+ */
+ val strokeLineWidth: Float = DefaultStrokeLineWidth,
+
+ /**
+ * Specifies the linecap for a stroked path, either butt, round, or square. The default is butt.
+ */
+ val strokeLineCap: StrokeCap = DefaultStrokeLineCap,
+
+ /**
+ * Specifies the linejoin for a stroked path, either miter, round or bevel. The default is miter
+ */
+ val strokeLineJoin: StrokeJoin = DefaultStrokeLineJoin,
+
+ /**
+ * Specifies the miter limit for a stroked path, the default is 4
+ */
+ val strokeLineMiter: Float = DefaultStrokeLineMiter
+) : VectorNode()
+
+/**
+ * Composes a vector graphic into the composition tree based on the specification
+ * provided by given [VectorAsset]
+ */
+@Composable
+fun DrawVector(vectorImage: VectorAsset) {
+ DrawVector(
+ name = vectorImage.name,
+ viewportWidth = vectorImage.viewportWidth,
+ viewportHeight = vectorImage.viewportHeight,
+ defaultWidth = vectorImage.defaultWidth,
+ defaultHeight = vectorImage.defaultHeight) {
+ RenderVectorGroup(group = vectorImage.root)
+ }
+}
+
+/**
+ * Recursive method for creating the vector graphic composition by traversing
+ * the tree structure
+ */
+@Composable
+private fun VectorScope.RenderVectorGroup(group: VectorGroup) {
+ for (vectorNode in group) {
+ if (vectorNode is VectorPath) {
+ Path(
+ pathData = vectorNode.pathData,
+ name = vectorNode.name,
+ fill = vectorNode.fill,
+ fillAlpha = vectorNode.fillAlpha,
+ stroke = vectorNode.stroke,
+ strokeAlpha = vectorNode.strokeAlpha,
+ strokeLineWidth = vectorNode.strokeLineWidth,
+ strokeLineCap = vectorNode.strokeLineCap,
+ strokeLineJoin = vectorNode.strokeLineJoin,
+ strokeLineMiter = vectorNode.strokeLineMiter
+ )
+ } else if (vectorNode is VectorGroup) {
+ Group(
+ name = vectorNode.name,
+ rotation = vectorNode.rotation,
+ scaleX = vectorNode.scaleX,
+ scaleY = vectorNode.scaleY,
+ translationX = vectorNode.translationX,
+ translationY = vectorNode.translationY,
+ pivotX = vectorNode.pivotX,
+ pivotY = vectorNode.pivotY,
+ clipPathData = vectorNode.clipPathData) {
+ RenderVectorGroup(group = vectorNode)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/VectorCompose.kt b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/VectorCompose.kt
new file mode 100644
index 0000000..4e36f04
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/VectorCompose.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2019 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.ui.core.vectorgraphics
+
+import androidx.compose.Children
+import androidx.compose.Composable
+import androidx.compose.composer
+import androidx.compose.compositionReference
+import androidx.compose.memo
+import androidx.compose.onDispose
+import androidx.compose.onPreCommit
+import androidx.compose.unaryPlus
+
+import androidx.ui.core.Draw
+import androidx.ui.core.Px
+import androidx.ui.graphics.vectorgraphics.Brush
+import androidx.ui.graphics.vectorgraphics.BrushType
+import androidx.ui.graphics.vectorgraphics.DefaultAlpha
+import androidx.ui.graphics.vectorgraphics.DefaultGroupName
+import androidx.ui.graphics.vectorgraphics.DefaultPathName
+import androidx.ui.graphics.vectorgraphics.DefaultPivotX
+import androidx.ui.graphics.vectorgraphics.DefaultPivotY
+import androidx.ui.graphics.vectorgraphics.DefaultRotation
+import androidx.ui.graphics.vectorgraphics.DefaultScaleX
+import androidx.ui.graphics.vectorgraphics.DefaultScaleY
+import androidx.ui.graphics.vectorgraphics.DefaultStrokeLineCap
+import androidx.ui.graphics.vectorgraphics.DefaultStrokeLineJoin
+import androidx.ui.graphics.vectorgraphics.DefaultStrokeLineMiter
+import androidx.ui.graphics.vectorgraphics.DefaultStrokeLineWidth
+import androidx.ui.graphics.vectorgraphics.DefaultTranslationX
+import androidx.ui.graphics.vectorgraphics.DefaultTranslationY
+import androidx.ui.graphics.vectorgraphics.EmptyBrush
+import androidx.ui.graphics.vectorgraphics.EmptyPath
+import androidx.ui.graphics.vectorgraphics.PathData
+import androidx.ui.graphics.vectorgraphics.VectorComponent
+import androidx.ui.graphics.vectorgraphics.GroupComponent
+import androidx.ui.graphics.vectorgraphics.PathComponent
+import androidx.ui.graphics.vectorgraphics.createPath
+import androidx.ui.graphics.vectorgraphics.obtainBrush
+import androidx.ui.painting.StrokeCap
+import androidx.ui.painting.StrokeJoin
+import androidx.ui.vector.VectorScope
+import androidx.ui.vector.composeVector
+import androidx.ui.vector.disposeVector
+import java.util.Vector
+
+
+@Composable
+fun DrawVector(
+ viewportWidth: Float,
+ viewportHeight: Float,
+ defaultWidth: Px = Px(viewportWidth),
+ defaultHeight: Px = Px(viewportHeight),
+ name: String = "",
+ @Children children: @Composable() VectorScope.() -> Unit
+) {
+ val vector = +memo(name, viewportWidth, viewportHeight) {
+ VectorComponent(
+ name,
+ viewportWidth,
+ viewportHeight,
+ defaultWidth,
+ defaultHeight
+ )
+ }
+
+ val ref = +compositionReference()
+ composeVector(vector, ref, children)
+ +onPreCommit(vector) {
+ onDispose { disposeVector(vector, ref) }
+ }
+
+ Draw { canvas, _ ->
+ vector.draw(canvas)
+ }
+}
+
+@Composable
+fun VectorScope.Group(
+ name: String = DefaultGroupName,
+ rotation: Float = DefaultRotation,
+ pivotX: Float = DefaultPivotX,
+ pivotY: Float = DefaultPivotY,
+ scaleX: Float = DefaultScaleX,
+ scaleY: Float = DefaultScaleY,
+ translationX: Float = DefaultTranslationX,
+ translationY: Float = DefaultTranslationY,
+ clipPathData: PathData = EmptyPath,
+ @Children children: @Composable() VectorScope.() -> Unit
+) {
+
+ val clipPathNodes = +memo(clipPathData) {
+ createPath(clipPathData)
+ }
+ <GroupComponent
+ name = name
+ rotation = rotation
+ pivotX = pivotX
+ pivotY = pivotY
+ scaleX = scaleX
+ scaleY = scaleY
+ translationX = translationX
+ translationY = translationY
+ clipPathNodes = clipPathNodes
+ >
+ children()
+ </GroupComponent>
+}
+
+@Composable
+fun VectorScope.Path(
+ pathData: PathData,
+ name: String = DefaultPathName,
+ fill: BrushType = EmptyBrush,
+ fillAlpha: Float = DefaultAlpha,
+ stroke: BrushType = EmptyBrush,
+ strokeAlpha: Float = DefaultAlpha,
+ strokeLineWidth: Float = DefaultStrokeLineWidth,
+ strokeLineCap: StrokeCap = DefaultStrokeLineCap,
+ strokeLineJoin: StrokeJoin = DefaultStrokeLineJoin,
+ strokeLineMiter: Float = DefaultStrokeLineMiter
+) {
+ val pathNodes = createPath(pathData)
+ val fillBrush: Brush = obtainBrush(fill)
+ val strokeBrush: Brush = obtainBrush(stroke)
+
+ <PathComponent
+ name
+ pathNodes
+ fill = fillBrush
+ fillAlpha
+ stroke = strokeBrush
+ strokeAlpha
+ strokeLineWidth
+ strokeLineJoin
+ strokeLineCap
+ strokeLineMiter
+ />
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/VectorResource.kt b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/VectorResource.kt
index c129e97..a886ad0 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/VectorResource.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/VectorResource.kt
@@ -16,395 +16,41 @@
package androidx.ui.core.vectorgraphics.compat
+import androidx.compose.composer
import android.annotation.SuppressLint
import android.content.res.Resources
-import android.content.res.TypedArray
-import android.util.AttributeSet
-import android.util.Log
import android.util.Xml
-import androidx.core.content.res.TypedArrayUtils
-import androidx.ui.core.vectorgraphics.addPathNodes
-import androidx.ui.core.vectorgraphics.group
-import androidx.ui.core.vectorgraphics.path
-import androidx.ui.core.vectorgraphics.vector
-import androidx.ui.painting.StrokeCap
-import androidx.ui.painting.StrokeJoin
-import androidx.ui.core.vectorgraphics.DefaultPivotX
-import androidx.ui.core.vectorgraphics.DefaultPivotY
-import androidx.ui.core.vectorgraphics.DefaultRotate
-import androidx.ui.core.vectorgraphics.DefaultScaleX
-import androidx.ui.core.vectorgraphics.DefaultScaleY
-import androidx.ui.core.vectorgraphics.DefaultTranslateX
-import androidx.ui.core.vectorgraphics.DefaultTranslateY
-import androidx.ui.core.vectorgraphics.EmptyBrush
-import androidx.ui.core.vectorgraphics.EmptyPath
-import androidx.ui.core.vectorgraphics.PathNode
-import androidx.compose.Children
import androidx.compose.Composable
-import androidx.compose.composer
-import org.xmlpull.v1.XmlPullParser
+import androidx.compose.ambient
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.core.ContextAmbient
+import androidx.ui.core.vectorgraphics.DrawVector
+import androidx.ui.core.vectorgraphics.VectorAsset
import org.xmlpull.v1.XmlPullParserException
-import java.io.IOException
-
-private val LINECAP_BUTT = 0
-private val LINECAP_ROUND = 1
-private val LINECAP_SQUARE = 2
-
-private val LINEJOIN_MITER = 0
-private val LINEJOIN_ROUND = 1
-private val LINEJOIN_BEVEL = 2
-
-private val FILL_TYPE_WINDING = 0
-
-private val SHAPE_CLIP_PATH = "clip-VPath"
-private val SHAPE_GROUP = "group"
-private val SHAPE_PATH = "path"
-
-private val LOGTAG = "VectorGraphicCreator"
-
-private fun getStrokeLineCap(id: Int, defValue: StrokeCap = StrokeCap.butt): StrokeCap =
- when (id) {
- LINECAP_BUTT -> StrokeCap.butt
- LINECAP_ROUND -> StrokeCap.round
- LINECAP_SQUARE -> StrokeCap.square
- else -> defValue
- }
-
-private fun getStrokeLineJoin(id: Int, defValue: StrokeJoin = StrokeJoin.miter): StrokeJoin =
- when (id) {
- LINEJOIN_MITER -> StrokeJoin.miter
- LINEJOIN_ROUND -> StrokeJoin.round
- LINEJOIN_BEVEL -> StrokeJoin.bevel
- else -> defValue
- }
@Composable
+fun VectorResource(resId: Int) {
+ val context = +ambient(ContextAmbient)
+ val res = context.resources
+ val theme = context.theme
+ val vectorImage = +memo(resId) {
+ loadVectorResource(theme, res, resId)
+ }
+ DrawVector(vectorImage = vectorImage)
+}
+
+@Throws(XmlPullParserException::class)
@SuppressWarnings("RestrictedApi")
-private fun inflateGroup(
- a: TypedArray,
- parser: XmlPullParser,
- @Suppress("UNUSED_PARAMETER") theme: Resources.Theme?,
- @Children childNodes: @Composable() () -> Unit
-) {
- // Account for any configuration changes.
- // mChangingConfigurations |= Utils.getChangingConfigurations(a);
+fun loadVectorResource(theme: Resources.Theme? = null, res: Resources, resId: Int): VectorAsset {
- // Extract the theme attributes, if any.
- // mThemeAttrs = null // TODO TINT THEME Not supported yet a.extractThemeAttrs();
-
- // This is added in API 11
- val rotate = TypedArrayUtils.getNamedFloat(
- a, parser, "rotation",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_ROTATION,
- DefaultRotate
- )
-
- val pivotX = a.getFloat(
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_X,
- DefaultPivotX
- )
- val pivotY = a.getFloat(
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_Y,
- DefaultPivotY
- )
-
- // This is added in API 11
- val scaleX = TypedArrayUtils.getNamedFloat(
- a, parser, "scaleX",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_X,
- DefaultScaleX
- )
-
- // This is added in API 11
- val scaleY = TypedArrayUtils.getNamedFloat(
- a, parser, "scaleY",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_Y,
- DefaultScaleY
- )
-
- val translateX = TypedArrayUtils.getNamedFloat(
- a, parser, "translateX",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_X,
- DefaultTranslateX
- )
- val translateY = TypedArrayUtils.getNamedFloat(
- a, parser, "translateY",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_Y,
- DefaultTranslateY
- )
-
- val name: String =
- a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_NAME) ?: ""
-
- parser.next()
-
- // TODO parse clip path
- val clipPathData = EmptyPath
- group(
- name = name,
- rotate = rotate,
- scaleX = scaleX,
- scaleY = scaleY,
- translateX = translateX,
- translateY = translateY,
- pivotX = pivotX,
- pivotY = pivotY,
- clipPathData = clipPathData
- ) {
- childNodes()
- }
-}
-
-@Suppress("UNUSED_PARAMETER")
-@Composable
-private fun inflateClip(
- a: TypedArray,
- parser: XmlPullParser,
- theme: Resources.Theme?,
- @Children childNodes: @Composable() () -> Unit
-) {
-// var pathName: String? =
-// a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_NAME)
-// if (pathName == null) {
-// pathName = ""
-// }
-// val pathData = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_PATH_DATA)
-
- // TODO (njawad) finish parsing clip paths from xml resources
-}
-
-@Composable
-@SuppressWarnings("RestrictedApi")
-private fun inflatePath(a: TypedArray, parser: XmlPullParser, theme: Resources.Theme?) {
- val hasPathData = TypedArrayUtils.hasAttribute(parser, "pathData")
- if (!hasPathData) {
- // If there is no pathData in the VPath tag, then this is an empty VPath,
- // nothing need to be drawn.
- Log.v("VectorPath", "no path data available skipping path")
- return
- }
-
- val name: String = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_NAME) ?: ""
-
- val pathStr = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_PATH_DATA)
-
- val pathData: Array<PathNode> = addPathNodes(pathStr)
-
- val fillColor = TypedArrayUtils.getNamedComplexColor(
- a, parser, theme, "fillColor",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_COLOR, 0
- )
- // TODO(njawad): restore these when they are used
-/* val fillAlpha = TypedArrayUtils.getNamedFloat(
- a, parser, "fillAlpha",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_ALPHA, 1.0f
- )
- val lineCap = TypedArrayUtils.getNamedInt(
- a, parser, "strokeLineCap",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_CAP, -1
- )
- val strokeLineCap =
- getStrokeLineCap(lineCap, StrokeCap.butt)
- val lineJoin = TypedArrayUtils.getNamedInt(
- a, parser, "strokeLineJoin",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_JOIN, -1
- )
- val strokeLineJoin =
- getStrokeLineJoin(lineJoin, StrokeJoin.bevel)
- val strokeMiterlimit = TypedArrayUtils.getNamedFloat(
- a, parser, "strokeMiterLimit",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_MITER_LIMIT,
- 1.0f
- )*/
- val strokeColor = TypedArrayUtils.getNamedComplexColor(
- a, parser, theme, "strokeColor",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_COLOR, 0
- )
- val strokeAlpha = TypedArrayUtils.getNamedFloat(
- a, parser, "strokeAlpha",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_ALPHA, 1.0f
- )
- val strokeLineWidth = TypedArrayUtils.getNamedFloat(
- a, parser, "strokeWidth",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_WIDTH, 1.0f
- )
- /*val trimPathEnd = TypedArrayUtils.getNamedFloat(
- a, parser, "trimPathEnd",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_END, 1.0f
- )
- val trimPathOffset = TypedArrayUtils.getNamedFloat(
- a, parser, "trimPathOffset",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_OFFSET,
- 0.0f
- )
- val trimPathStart = TypedArrayUtils.getNamedFloat(
- a, parser, "trimPathStart",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_START,
- 0.0f
- )
- val fillRule = TypedArrayUtils.getNamedInt(
- a, parser, "fillType",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_FILLTYPE,
- FILL_TYPE_WINDING
- )*/
-
- // TODO update path with additional params
- path(
- name = name,
- fill = if (fillColor.willDraw()) fillColor.color else EmptyBrush,
- strokeAlpha = strokeAlpha,
- strokeLineWidth = strokeLineWidth,
- stroke = if (strokeColor.willDraw()) strokeColor.color else EmptyBrush,
- pathData = pathData
- )
-}
-
-@Composable
-@SuppressWarnings("RestrictedApi")
-private fun inflateInner(
- res: Resources,
- attrs: AttributeSet,
- theme: Resources.Theme?,
- innerDepth: Int,
- parser: XmlPullParser
-) {
- var eventType = parser.getEventType()
-
- // Parse everything until the end of the VectorGraphic element.
- while (eventType != XmlPullParser.END_DOCUMENT &&
- (parser.getDepth() >= innerDepth || eventType != XmlPullParser.END_TAG)
- ) {
- if (eventType == XmlPullParser.START_TAG) {
- val tagName = parser.getName()
- if (SHAPE_PATH.equals(tagName)) {
- Log.v("VectorPath", "parsing path...")
- val a = TypedArrayUtils.obtainAttributes(
- res, theme, attrs,
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH
- )
- inflatePath(a = a, parser = parser, theme = theme)
- a.recycle()
- } else if (SHAPE_CLIP_PATH.equals(tagName)) {
- val a = TypedArrayUtils.obtainAttributes(
- res, theme, attrs,
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH
- )
- inflateClip(a = a, parser = parser, theme = theme) {
- inflateInner(
- res = res,
- parser = parser,
- attrs = attrs,
- theme = theme,
- innerDepth = innerDepth
- )
- }
- a.recycle()
- } else if (SHAPE_GROUP.equals(tagName)) {
- val a = TypedArrayUtils.obtainAttributes(
- res, theme, attrs,
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP
- )
- inflateGroup(a = a, parser = parser, theme = theme) {
- inflateInner(
- res = res,
- parser = parser,
- attrs = attrs,
- theme = theme,
- innerDepth = innerDepth
- )
- }
- a.recycle()
- }
- }
- eventType = parser.next()
- }
-}
-
-@Composable
-@SuppressWarnings("RestrictedApi")
-private fun inflate(
- res: Resources,
- parser: XmlPullParser,
- attrs: AttributeSet,
- theme: Resources.Theme?
-) {
- val innerDepth = parser.getDepth() + 1
-
- val vectorAttrs = TypedArrayUtils.obtainAttributes(
- res, theme, attrs,
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_TYPE_ARRAY
- )
-
- // TODO (njawad) handle mirroring here
-// state.mAutoMirrored = TypedArrayUtils.getNamedBoolean(a, parser, "autoMirrored",
-// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_AUTO_MIRRORED, state.mAutoMirrored)
-
- val viewportWidth = TypedArrayUtils.getNamedFloat(
- vectorAttrs, parser, "viewportWidth",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_WIDTH,
- 0.0f
- )
-
- val viewportHeight = TypedArrayUtils.getNamedFloat(
- vectorAttrs, parser, "viewportHeight",
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_HEIGHT,
- 0.0f
- )
-
- if (viewportWidth <= 0) {
- throw XmlPullParserException(
- vectorAttrs.getPositionDescription() +
- "VectorGraphic requires viewportWidth > 0"
- )
- } else if (viewportHeight <= 0) {
- throw XmlPullParserException(
- vectorAttrs.getPositionDescription() +
- "VectorGraphic requires viewportHeight > 0"
- )
- }
-
- val defaultWidth = vectorAttrs.getDimension(
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_WIDTH, 0.0f
- )
- val defaultHeight = vectorAttrs.getDimension(
- AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_HEIGHT, 0.0f
- )
-
- vector(
- name = "vector",
- defaultWidth = defaultWidth,
- defaultHeight = defaultHeight,
- viewportWidth = viewportWidth,
- viewportHeight = viewportHeight
- ) {
- inflateInner(
- res = res,
- attrs = attrs,
- theme = theme,
- innerDepth = innerDepth,
- parser = parser
- )
- }
-}
-
-@Composable
-fun vectorResource(res: Resources, resId: Int) {
@SuppressLint("ResourceType") val parser = res.getXml(resId)
val attrs = Xml.asAttributeSet(parser)
- var type: Int
- try {
- type = parser.next()
- while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
- // Empty loop
- type = parser.next()
- }
- if (type != XmlPullParser.START_TAG) {
- throw XmlPullParserException("No start tag found")
- }
- inflate(res = res, parser = parser, attrs = attrs, theme = null)
- } catch (e: XmlPullParserException) {
- Log.e(LOGTAG, "parser error", e)
- } catch (e: IOException) {
- Log.e(LOGTAG, "parser error", e)
+ val builder = parser.seekToStartTag().createVectorImageBuilder(res, theme, attrs)
+
+ while (!parser.isAtEnd()) {
+ parser.parseCurrentVectorNode(res, attrs, theme, builder)
+ parser.next()
}
-}
+ return builder.build()
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParser.kt b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParser.kt
new file mode 100644
index 0000000..88749ef
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParser.kt
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2019 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.ui.core.vectorgraphics.compat
+
+import android.content.res.Resources
+import android.util.AttributeSet
+import androidx.core.content.res.TypedArrayUtils
+import androidx.ui.core.Px
+import androidx.ui.core.vectorgraphics.VectorAssetBuilder
+import androidx.ui.graphics.vectorgraphics.DefaultPivotX
+import androidx.ui.graphics.vectorgraphics.DefaultPivotY
+import androidx.ui.graphics.vectorgraphics.DefaultRotation
+import androidx.ui.graphics.vectorgraphics.DefaultScaleX
+import androidx.ui.graphics.vectorgraphics.DefaultScaleY
+import androidx.ui.graphics.vectorgraphics.DefaultTranslationX
+import androidx.ui.graphics.vectorgraphics.DefaultTranslationY
+import androidx.ui.graphics.vectorgraphics.EmptyBrush
+import androidx.ui.graphics.vectorgraphics.EmptyPath
+import androidx.ui.graphics.vectorgraphics.PathNode
+import androidx.ui.graphics.vectorgraphics.addPathNodes
+import androidx.ui.painting.StrokeCap
+import androidx.ui.painting.StrokeJoin
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserException
+
+private val LINECAP_BUTT = 0
+private val LINECAP_ROUND = 1
+private val LINECAP_SQUARE = 2
+
+private val LINEJOIN_MITER = 0
+private val LINEJOIN_ROUND = 1
+private val LINEJOIN_BEVEL = 2
+
+private val FILL_TYPE_WINDING = 0
+
+private val SHAPE_CLIP_PATH = "clip-VPath"
+private val SHAPE_GROUP = "group"
+private val SHAPE_PATH = "path"
+
+private fun getStrokeLineCap(id: Int, defValue: StrokeCap = StrokeCap.butt): StrokeCap =
+ when (id) {
+ LINECAP_BUTT -> StrokeCap.butt
+ LINECAP_ROUND -> StrokeCap.round
+ LINECAP_SQUARE -> StrokeCap.square
+ else -> defValue
+ }
+
+private fun getStrokeLineJoin(id: Int, defValue: StrokeJoin = StrokeJoin.miter): StrokeJoin =
+ when (id) {
+ LINEJOIN_MITER -> StrokeJoin.miter
+ LINEJOIN_ROUND -> StrokeJoin.round
+ LINEJOIN_BEVEL -> StrokeJoin.bevel
+ else -> defValue
+ }
+
+internal fun XmlPullParser.isAtEnd(): Boolean =
+ eventType == XmlPullParser.END_DOCUMENT ||
+ (depth < 1 && eventType == XmlPullParser.END_TAG)
+
+internal fun XmlPullParser.parseCurrentVectorNode(
+ res: Resources,
+ attrs: AttributeSet,
+ theme: Resources.Theme? = null,
+ builder: VectorAssetBuilder
+) {
+ when (eventType) {
+ XmlPullParser.START_TAG -> {
+ when (name) {
+ SHAPE_PATH -> {
+ parsePath(res, theme, attrs, builder)
+ }
+ SHAPE_CLIP_PATH -> {
+ // TODO parse clipping paths
+ }
+ SHAPE_GROUP -> {
+ parseGroup(res, theme, attrs, builder)
+ }
+ }
+ }
+ XmlPullParser.END_TAG -> {
+ if (SHAPE_GROUP == name) {
+ builder.popGroup()
+ }
+ }
+ }
+}
+
+/**
+ * Helper method to seek to the first tag within the VectorDrawable xml asset
+ */
+@Throws(XmlPullParserException::class)
+internal fun XmlPullParser.seekToStartTag(): XmlPullParser {
+ var type = next()
+ while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
+ // Empty loop
+ type = next()
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw XmlPullParserException("No start tag found")
+ }
+ return this
+}
+
+@SuppressWarnings("RestrictedApi")
+internal fun XmlPullParser.createVectorImageBuilder(
+ res: Resources,
+ theme: Resources.Theme?,
+ attrs: AttributeSet
+): VectorAssetBuilder {
+ val vectorAttrs = TypedArrayUtils.obtainAttributes(
+ res,
+ theme,
+ attrs,
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_TYPE_ARRAY
+ )
+
+ // TODO (njawad) handle mirroring here
+// state.mAutoMirrored = TypedArrayUtils.getNamedBoolean(a, parser, "autoMirrored",
+// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_AUTO_MIRRORED, state.mAutoMirrored)
+
+ val viewportWidth = TypedArrayUtils.getNamedFloat(
+ vectorAttrs,
+ this,
+ "viewportWidth",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_WIDTH,
+ 0.0f
+ )
+
+ val viewportHeight = TypedArrayUtils.getNamedFloat(
+ vectorAttrs,
+ this,
+ "viewportHeight",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_HEIGHT,
+ 0.0f
+ )
+
+ if (viewportWidth <= 0) {
+ throw XmlPullParserException(
+ vectorAttrs.getPositionDescription() +
+ "<VectorGraphic> tag requires viewportWidth > 0"
+ )
+ } else if (viewportHeight <= 0) {
+ throw XmlPullParserException(
+ vectorAttrs.getPositionDescription() +
+ "<VectorGraphic> tag requires viewportHeight > 0"
+ )
+ }
+
+ val defaultWidth = vectorAttrs.getDimension(
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_WIDTH, 0.0f
+ )
+ val defaultHeight = vectorAttrs.getDimension(
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_HEIGHT, 0.0f
+ )
+
+ vectorAttrs.recycle()
+
+ return VectorAssetBuilder(
+ defaultWidth = Px(defaultWidth),
+ defaultHeight = Px(defaultHeight),
+ viewportWidth = viewportWidth,
+ viewportHeight = viewportHeight
+ )
+}
+
+@Throws(IllegalArgumentException::class)
+@SuppressWarnings("RestrictedApi")
+internal fun XmlPullParser.parsePath(
+ res: Resources,
+ theme: Resources.Theme?,
+ attrs: AttributeSet,
+ builder: VectorAssetBuilder
+) {
+ val a = TypedArrayUtils.obtainAttributes(
+ res,
+ theme,
+ attrs,
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH
+ )
+
+ val hasPathData = TypedArrayUtils.hasAttribute(this, "pathData")
+ if (!hasPathData) {
+ // If there is no pathData in the VPath tag, then this is an empty VPath,
+ // nothing need to be drawn.
+ throw IllegalArgumentException("No path data available")
+ }
+
+ val name: String = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_NAME) ?: ""
+
+ val pathStr = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_PATH_DATA)
+
+ val pathData: Array<PathNode> = addPathNodes(pathStr)
+
+ val fillColor = TypedArrayUtils.getNamedComplexColor(
+ a,
+ this,
+ theme,
+ "fillColor",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_COLOR, 0
+ )
+ val fillAlpha = TypedArrayUtils.getNamedFloat(
+ a,
+ this,
+ "fillAlpha",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_ALPHA, 1.0f
+ )
+ val lineCap = TypedArrayUtils.getNamedInt(
+ a,
+ this,
+ "strokeLineCap",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_CAP, -1
+ )
+ val strokeLineCap = getStrokeLineCap(lineCap, StrokeCap.butt)
+ val lineJoin = TypedArrayUtils.getNamedInt(
+ a,
+ this,
+ "strokeLineJoin",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_JOIN, -1
+ )
+ val strokeLineJoin = getStrokeLineJoin(lineJoin, StrokeJoin.bevel)
+ val strokeMiterLimit = TypedArrayUtils.getNamedFloat(
+ a,
+ this,
+ "strokeMiterLimit",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_MITER_LIMIT,
+ 1.0f
+ )
+ val strokeColor = TypedArrayUtils.getNamedComplexColor(
+ a,
+ this,
+ theme,
+ "strokeColor",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_COLOR, 0
+ )
+ val strokeAlpha = TypedArrayUtils.getNamedFloat(
+ a,
+ this,
+ "strokeAlpha",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_ALPHA, 1.0f
+ )
+ val strokeLineWidth = TypedArrayUtils.getNamedFloat(
+ a,
+ this,
+ "strokeWidth",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_WIDTH, 1.0f
+ )
+
+ // TODO (njawad) handle trim paths + fill rule
+// val trimPathEnd = TypedArrayUtils.getNamedFloat(
+// a, this, "trimPathEnd",
+// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_END, 1.0f
+// )
+// val trimPathOffset = TypedArrayUtils.getNamedFloat(
+// a, this, "trimPathOffset",
+// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_OFFSET,
+// 0.0f
+// )
+// val trimPathStart = TypedArrayUtils.getNamedFloat(
+// a, this, "trimPathStart",
+// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_START,
+// 0.0f
+// )
+// val fillRule = TypedArrayUtils.getNamedInt(
+// a, this, "fillType",
+// AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_FILLTYPE,
+// FILL_TYPE_WINDING
+// )
+
+ a.recycle()
+
+ @Suppress("IMPLICIT_CAST_TO_ANY")
+ val fillBrush = if (fillColor.willDraw()) fillColor.color else EmptyBrush
+
+ @Suppress("IMPLICIT_CAST_TO_ANY")
+ val strokeBrush = if (strokeColor.willDraw()) strokeColor.color else EmptyBrush
+
+ builder.addPath(
+ pathData,
+ name,
+ fillBrush,
+ fillAlpha,
+ strokeBrush,
+ strokeAlpha,
+ strokeLineWidth,
+ strokeLineCap,
+ strokeLineJoin,
+ strokeMiterLimit)
+}
+
+@SuppressWarnings("RestrictedApi")
+internal fun XmlPullParser.parseGroup(
+ res: Resources,
+ theme: Resources.Theme?,
+ attrs: AttributeSet,
+ builder: VectorAssetBuilder
+) {
+ val a = TypedArrayUtils.obtainAttributes(
+ res,
+ theme,
+ attrs,
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP
+ )
+
+ // Account for any configuration changes.
+ // mChangingConfigurations |= Utils.getChangingConfigurations(a);
+
+ // Extract the theme attributes, if any.
+ // mThemeAttrs = null // TODO TINT THEME Not supported yet a.extractThemeAttrs();
+
+ // This is added in API 11
+ val rotate = TypedArrayUtils.getNamedFloat(
+ a,
+ this,
+ "rotation",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_ROTATION,
+ DefaultRotation
+ )
+
+ val pivotX = a.getFloat(
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_X,
+ DefaultPivotX
+ )
+ val pivotY = a.getFloat(
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_Y,
+ DefaultPivotY
+ )
+
+ // This is added in API 11
+ val scaleX = TypedArrayUtils.getNamedFloat(
+ a,
+ this,
+ "scaleX",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_X,
+ DefaultScaleX
+ )
+
+ // This is added in API 11
+ val scaleY = TypedArrayUtils.getNamedFloat(
+ a,
+ this,
+ "scaleY",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_Y,
+ DefaultScaleY
+ )
+
+ val translateX = TypedArrayUtils.getNamedFloat(
+ a,
+ this,
+ "translationX",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_X,
+ DefaultTranslationX
+ )
+ val translateY = TypedArrayUtils.getNamedFloat(
+ a,
+ this,
+ "translationY",
+ AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_Y,
+ DefaultTranslationY
+ )
+
+ val name: String =
+ a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_NAME) ?: ""
+
+ // TODO parse clip path
+ val clipPathData = EmptyPath
+
+ a.recycle()
+
+ builder.pushGroup(
+ name,
+ rotate,
+ scaleX,
+ scaleY,
+ translateX,
+ translateY,
+ pivotX,
+ pivotY,
+ clipPathData
+ )
+}
diff --git a/ui/ui-framework/src/test/java/androidx/ui/core/gesture/DoubleTapGestureDetectorTest.kt b/ui/ui-framework/src/test/java/androidx/ui/core/gesture/DoubleTapGestureDetectorTest.kt
new file mode 100644
index 0000000..1890b9f
--- /dev/null
+++ b/ui/ui-framework/src/test/java/androidx/ui/core/gesture/DoubleTapGestureDetectorTest.kt
@@ -0,0 +1,522 @@
+/*
+ * Copyright 2019 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.ui.core.gesture
+
+import androidx.ui.core.PxPosition
+import androidx.ui.core.consumeDownChange
+import androidx.ui.core.milliseconds
+import androidx.ui.core.millisecondsToTimestamp
+import androidx.ui.core.px
+import androidx.ui.testutils.consume
+import androidx.ui.testutils.down
+import androidx.ui.testutils.invokeOverAllPasses
+import androidx.ui.testutils.moveTo
+import androidx.ui.testutils.up
+import com.google.common.truth.Truth.assertThat
+import com.nhaarman.mockitokotlin2.any
+import com.nhaarman.mockitokotlin2.mock
+import com.nhaarman.mockitokotlin2.never
+import com.nhaarman.mockitokotlin2.verify
+import kotlinx.coroutines.ObsoleteCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineContext
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.util.concurrent.TimeUnit
+
+// TODO(shepshapard): Add more tests for:
+// 1. More complex multi-pointer scenarios testing how consumption affects firing events
+// 2. More complex multi-pointer scenarios testing how pointers effect consumption
+
+@ObsoleteCoroutinesApi
+@RunWith(JUnit4::class)
+class DoubleTapGestureDetectorTest {
+
+ private val DoubleTapTimeoutMillis = 100.milliseconds
+ private val testContext = TestCoroutineContext()
+ private val onDoubleTap: (PxPosition) -> Unit = mock()
+ private lateinit var mRecognizer: DoubleTapGestureRecognizer
+
+ @Before
+ fun setup() {
+ mRecognizer = DoubleTapGestureRecognizer(onDoubleTap, testContext)
+ mRecognizer.doubleTapTimeout = DoubleTapTimeoutMillis
+ }
+
+ // Tests that verify conditions under which onDoubleTap will not be called.
+
+ @Test
+ fun pointerInputHandler_down_onDoubleTapNotCalled() {
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down()))
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUp_onDoubleTapNotCalled() {
+ val down = down(timestamp = 0L.millisecondsToTimestamp())
+ val up = down.up(timestamp = 1L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownWithinTimeout_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val up = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 100L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownOutsideTimeout_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val up = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 101L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up)
+ testContext.advanceTimeBy(100, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownOutsideTimeoutUp_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 101L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(100, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downMoveConsumedUpDownInsideTimeoutUp_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val moveConsumed = down1.moveTo(1L.millisecondsToTimestamp(), x = 1f).consume(dx = 1f)
+ val up1 = moveConsumed.up(timestamp = 2L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 101L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(moveConsumed)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownInsideTimeoutMoveConsumedUp_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 100L.millisecondsToTimestamp())
+ val moveConsumed = down2.moveTo(101L.millisecondsToTimestamp(), x = 1f).consume(dx = 1f)
+ val up2 = moveConsumed.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(moveConsumed)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_2Down1MoveConsumedUpDownInsideTimeoutUp_onLongPressNotCalled() {
+ val down1A = down(0, timestamp = 0L.millisecondsToTimestamp())
+ val down1B = down(1, timestamp = 0L.millisecondsToTimestamp())
+ val moveConsumed1A = down1A.moveTo(1L.millisecondsToTimestamp(), x = 1f).consume(dx = 1f)
+ val move1B = down1B.moveTo(1L.millisecondsToTimestamp())
+ val up1A = moveConsumed1A.up(timestamp = 2L.millisecondsToTimestamp())
+ val up1B = move1B.up(timestamp = 2L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 101L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1A, down1B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(moveConsumed1A, move1B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1A, up1B)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUp2DownInsideTimeout1MoveConsumedUp_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val up2 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2A = down(0, timestamp = 100L.millisecondsToTimestamp())
+ val down2B = down(1, timestamp = 100L.millisecondsToTimestamp())
+ val moveConsumed2A = down2A.moveTo(101L.millisecondsToTimestamp(), x = 1f).consume(dx = 1f)
+ val move2B = down2B.moveTo(101L.millisecondsToTimestamp())
+ val up2A = moveConsumed2A.up(timestamp = 102L.millisecondsToTimestamp())
+ val up2B = move2B.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2A, down2B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(moveConsumed2A, move2B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2A, up2B)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downConsumedUpDownWithinTimeoutUp_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp()).consumeDownChange()
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(0, timestamp = 100L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUpConsumedDownWithinTimeoutUp_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp()).consumeDownChange()
+ val down2 = down(0, timestamp = 100L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownConsumedWithinTimeoutUp_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(0, timestamp = 100L.millisecondsToTimestamp()).consumeDownChange()
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownWithinTimeoutUpConsumed_onLongPressNotCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(0, timestamp = 100L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp()).consumeDownChange()
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_2down1Up1DownWithinTimeout1Up_onLongPressNotCalled() {
+ val down1A = down(0, timestamp = 0L.millisecondsToTimestamp())
+ val down1B = down(1, timestamp = 0L.millisecondsToTimestamp())
+ val move1A1 = down1A.moveTo(2L.millisecondsToTimestamp())
+ val up2B = down1B.up(timestamp = 2L.millisecondsToTimestamp())
+ val move1A2 = move1A1.moveTo(101L.millisecondsToTimestamp())
+ val down2 = down(id = 1, timestamp = 101L.millisecondsToTimestamp())
+ val move1A3 = move1A2.moveTo(102L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1A, down1B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move1A1, up2B)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move1A2, down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move1A3, up2)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_1down1Up2DownWithinTimeout1Up_onLongPressNotCalled() {
+ val down1 = down(id = 0, timestamp = 0L.millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2A = down(0, timestamp = 100L.millisecondsToTimestamp())
+ val down2B = down(1, timestamp = 100L.millisecondsToTimestamp())
+ val move2A = down2A.moveTo(timestamp = 101L.millisecondsToTimestamp())
+ val up2B = down2B.up(timestamp = 101L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2A, down2B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move2A, up2B)
+
+ verify(onDoubleTap, never()).invoke(any())
+ }
+
+ // Tests that verify conditions under which onDoubleTap will be called.
+
+ @Test
+ fun pointerInputHandler_downUpDownInsideTimeoutUp_onLongPressCalled() {
+ val down1 = down(timestamp = (0L).millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 100L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 101L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downMoveUpDownInsideTimeoutUp_onLongPressCalled() {
+ val down1 = down(timestamp = (0L).millisecondsToTimestamp())
+ val move = down1.moveTo(1L.millisecondsToTimestamp(), x = 1f)
+ val up1 = move.up(timestamp = 2L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 101L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownInsideTimeoutMoveUp_onLongPressCalled() {
+ val down1 = down(timestamp = (0L).millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 10L.millisecondsToTimestamp())
+ val move = down2.moveTo(101L.millisecondsToTimestamp(), x = 1f)
+ val up2 = move.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_2Down1MoveUpDownInsideTimeoutUp_onLongPressCalled() {
+ val down1A = down(0, timestamp = 0L.millisecondsToTimestamp())
+ val down1B = down(1, timestamp = 0L.millisecondsToTimestamp())
+ val move1A = down1A.moveTo(1L.millisecondsToTimestamp(), x = 1f)
+ val move1B = down1B.moveTo(1L.millisecondsToTimestamp())
+ val up1A = move1A.up(timestamp = 2L.millisecondsToTimestamp())
+ val up1B = move1B.up(timestamp = 2L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 101L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1A, down1B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move1A, move1B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1A, up1B)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap).invoke(any())
+ }
+
+ @Test
+ fun pointerInputHandler_downUp2DownInsideTimeout1MoveUp_onLongPressCalled() {
+ val down1 = down(timestamp = 0L.millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2A = down(0, timestamp = 100L.millisecondsToTimestamp())
+ val down2B = down(1, timestamp = 100L.millisecondsToTimestamp())
+ val move2A = down2A.moveTo(101L.millisecondsToTimestamp(), x = 1f)
+ val move2B = down2B.moveTo(101L.millisecondsToTimestamp())
+ val up2A = move2A.up(timestamp = 102L.millisecondsToTimestamp())
+ val up2B = move2B.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2A, down2B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move2A, move2B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2A, up2B)
+
+ verify(onDoubleTap).invoke(any())
+ }
+
+ // Tests that verify correctness of PxPosition value passed to onDoubleTap
+
+ @Test
+ fun pointerInputHandler_downUpDownUpAllAtOrigin_onDoubleTapCalledWithOrigin() {
+ val down1 = down(timestamp = (0L).millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 100L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 101L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap).invoke(PxPosition.Origin)
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownMoveUp_onDoubleTapCalledWithFinalMovePosition() {
+ val down1 = down(timestamp = (0L).millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 100L.millisecondsToTimestamp())
+ val move2 = down2.moveTo(101L.millisecondsToTimestamp(), 3f, 5f)
+ val up2 = move2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ verify(onDoubleTap).invoke(PxPosition(3.px, 5.px))
+ }
+
+ @Test
+ fun pointerInputHandler_downUp2Down2Move1UpThen1Up_onDoubleTapCalledWithFinalFingerPosition() {
+ val down1 = down(timestamp = (0L).millisecondsToTimestamp())
+ val up1 = down1.up(timestamp = 1L.millisecondsToTimestamp())
+ val down2A = down(id = 0, timestamp = 100L.millisecondsToTimestamp())
+ val down2B = down(id = 1, timestamp = 100L.millisecondsToTimestamp())
+ val move2A = down2A.moveTo(101L.millisecondsToTimestamp(), 3f, 5f)
+ val move2B1 = down2B.moveTo(101L.millisecondsToTimestamp(), -7f, -11f)
+ val up2A = move2A.up(timestamp = 102L.millisecondsToTimestamp())
+ val move2B2 = move2B1.moveTo(timestamp = 102L.millisecondsToTimestamp(), x = -7f, y = -11f)
+ val up2B = move2B2.up(timestamp = 103L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up1)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2A, down2B)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(move2A, move2B1)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2A, move2B2)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up2B)
+
+ verify(onDoubleTap).invoke(PxPosition((-7).px, (-11).px))
+ }
+
+ // Tests that verify that consumption behavior
+
+ @Test
+ fun pointerInputHandler_down_downNotConsumed() {
+ val down = down()
+ val result = mRecognizer.pointerInputHandler.invokeOverAllPasses(down)
+ assertThat(result[0].consumed.downChange).isFalse()
+ }
+
+ @Test
+ fun pointerInputHandler_downUp_upNotConsumed() {
+ val down = down()
+ val up = down.up(1L.millisecondsToTimestamp())
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down)
+ val result = mRecognizer.pointerInputHandler.invokeOverAllPasses(up)
+ assertThat(result[0].consumed.downChange).isFalse()
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownInsideTimeout_lastDownNotConsumed() {
+ val down = down()
+ val up = down.up(1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 100L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ val result = mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+
+ assertThat(result[0].consumed.downChange).isFalse()
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownOutsideTimeoutUp_lastUpNotConsumed() {
+ val down = down()
+ val up = down.up(1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 101L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 102L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up)
+ testContext.advanceTimeBy(100, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ val result = mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ assertThat(result[0].consumed.downChange).isFalse()
+ }
+
+ @Test
+ fun pointerInputHandler_downUpDownInsideTimeoutUp_lastUpConsumed() {
+ val down = down()
+ val up = down.up(1L.millisecondsToTimestamp())
+ val down2 = down(timestamp = 100L.millisecondsToTimestamp())
+ val up2 = down2.up(timestamp = 101L.millisecondsToTimestamp())
+
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(up)
+ testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+ mRecognizer.pointerInputHandler.invokeOverAllPasses(down2)
+ val result = mRecognizer.pointerInputHandler.invokeOverAllPasses(up2)
+
+ assertThat(result[0].consumed.downChange).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
index 52c2f1c..568e0a2 100644
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
+++ b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
@@ -227,7 +227,7 @@
}
@Composable
-fun SingleCompositionRow(@Children children: @Composable() () -> Unit) {
+fun SingleCompositionRow(children: @Composable() () -> Unit) {
Layout(layoutBlock = { measurables, constraints ->
val placeables = measurables.map {
it.measure(constraints.copy(minWidth = 0.ipx, maxWidth = IntPx.Infinity))
@@ -246,7 +246,7 @@
}
@Composable
-fun SingleCompositionColumn(@Children children: @Composable() () -> Unit) {
+fun SingleCompositionColumn(children: @Composable() () -> Unit) {
Layout(layoutBlock = { measurables, constraints ->
val placeables = measurables.map {
it.measure(constraints.copy(minHeight = 0.ipx, maxHeight = IntPx.Infinity))
@@ -306,7 +306,7 @@
}
@Composable
-fun SingleCompositionRowWithIntrinsics(@Children children: @Composable() () -> Unit) {
+fun SingleCompositionRowWithIntrinsics(children: @Composable() () -> Unit) {
ComplexLayout(children = children, block = {
layout { measurables, constraints ->
val placeables = measurables.map { measurable ->
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
index 1b94f79..c42418b 100644
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
+++ b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
@@ -53,7 +53,7 @@
width: Dp? = null,
height: Dp? = null,
color: Color,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Wrap {
DrawRectangle(color = color)
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/IntrinsicTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/IntrinsicTest.kt
index dd375d3..556d6b4 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/IntrinsicTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/IntrinsicTest.kt
@@ -500,7 +500,7 @@
maxIntrinsicWidth: Dp,
minIntrinsicHeight: Dp,
height: Dp, maxIntrinsicHeight: Dp,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
ComplexLayout(children) {
layout { _, constraints ->
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/LayoutTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/LayoutTest.kt
index 0042948..d8c6eb1 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/LayoutTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/LayoutTest.kt
@@ -70,7 +70,7 @@
activityTestRule.runOnUiThread(runnable)
}
- internal fun show(@Children composable: @Composable() () -> Unit) {
+ internal fun show(composable: @Composable() () -> Unit) {
val runnable: Runnable = object : Runnable {
override fun run() {
activity.setContent(composable)
@@ -149,6 +149,7 @@
{ w -> measurable.maxIntrinsicHeight(w) }
)
layoutLatch.countDown()
+ layoutResult(0.ipx, 0.ipx) {}
}
minIntrinsicWidth { _, _ -> 0.ipx }
maxIntrinsicWidth { _, _ -> 0.ipx }
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/OnPositionedTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/OnPositionedTest.kt
index f38837a..deca623 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/OnPositionedTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/OnPositionedTest.kt
@@ -17,6 +17,7 @@
package androidx.ui.layout.test
import android.widget.FrameLayout
+import androidx.compose.Model
import androidx.test.filters.SmallTest
import androidx.compose.composer
import androidx.ui.core.LayoutCoordinates
@@ -192,4 +193,31 @@
assertThat(realGlobalPosition).isEqualTo(globalPosition)
assertThat(realLocalPosition).isEqualTo(localPosition)
}
+
+ @Test
+ fun justAddedOnPositionedCallbackFiredWithoutLayoutChanges() = withDensity(density) {
+ val needCallback = NeedCallback(false)
+
+ val callbackLatch = CountDownLatch(1)
+ show {
+ Container(expanded = true) {
+ if (needCallback.value) {
+ OnPositioned(onPositioned = {
+ callbackLatch.countDown()
+ })
+ }
+ }
+ }
+
+ activityTestRule.runOnUiThread(object : Runnable {
+ override fun run() {
+ needCallback.value = true
+ }
+ })
+
+ assertThat(callbackLatch.await(1000, TimeUnit.SECONDS)).isEqualTo(true)
+ }
}
+
+@Model
+private data class NeedCallback(var value: Boolean)
\ No newline at end of file
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt
index 07144eb..888706f 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt
@@ -69,7 +69,7 @@
* For a widget that does alignment and tries to be the same size as its child, see [Wrap].
*/
@Composable
-fun Align(alignment: Alignment, @Children children: @Composable() () -> Unit) {
+fun Align(alignment: Alignment, children: @Composable() () -> Unit) {
Layout(layoutBlock = { measurables, constraints ->
val measurable = measurables.firstOrNull()
// The child cannot be larger than our max constraints, but we ignore min constraints.
@@ -112,6 +112,6 @@
* }
*/
@Composable
-fun Center(@Children children: @Composable() () -> Unit) {
+fun Center(children: @Composable() () -> Unit) {
Align(alignment = Alignment.Center, children = children)
}
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt
index 8117ad6..2856b4b 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt
@@ -51,7 +51,7 @@
@Composable
fun AspectRatio(
aspectRatio: Float,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Layout(children) { measurables, constraints ->
val size = listOf(
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/ConstrainedBox.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/ConstrainedBox.kt
index e30c9b3..7ad2633 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/ConstrainedBox.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/ConstrainedBox.kt
@@ -38,7 +38,7 @@
@Composable
fun ConstrainedBox(
constraints: DpConstraints,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
ComplexLayout(children) {
layout { measurables, incomingConstraints ->
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt
index 9027e93..dc48a4b 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt
@@ -61,7 +61,7 @@
constraints: DpConstraints = DpConstraints(),
width: Dp? = null,
height: Dp? = null,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
trace("UI:Container") {
Layout(children = children, layoutBlock = { measurables, incomingConstraints ->
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
index 4860313..21a9a37 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
@@ -178,7 +178,7 @@
mainAxisSize: FlexSize = FlexSize.Max,
crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Center,
crossAxisSize: FlexSize = FlexSize.Min,
- @Children block: @Composable() () -> Unit
+ block: @Composable() () -> Unit
) {
FlexRow(
mainAxisAlignment = mainAxisAlignment,
@@ -214,7 +214,7 @@
mainAxisSize: FlexSize = FlexSize.Max,
crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Center,
crossAxisSize: FlexSize = FlexSize.Min,
- @Children block: @Composable() () -> Unit
+ block: @Composable() () -> Unit
) {
FlexColumn(
mainAxisAlignment = mainAxisAlignment,
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
index 58b8205..3bd2915 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
@@ -42,7 +42,7 @@
* the [ConstrainedBox]s to use the same width.
*/
@Composable
-fun MinIntrinsicWidth(@Children children: @Composable() () -> Unit) {
+fun MinIntrinsicWidth(children: @Composable() () -> Unit) {
ComplexLayout(children) {
layout { measurables, constraints ->
val measurable = measurables.firstOrNull()
@@ -87,7 +87,7 @@
* the divider to use the same height.
*/
@Composable
-fun MinIntrinsicHeight(@Children children: @Composable() () -> Unit) {
+fun MinIntrinsicHeight(children: @Composable() () -> Unit) {
ComplexLayout(children) {
layout { measurables, constraints ->
val measurable = measurables.firstOrNull()
@@ -132,7 +132,7 @@
* The sample is a layout containing three widgets having the same width as the widest one.
*/
@Composable
-fun MaxIntrinsicWidth(@Children children: @Composable() () -> Unit) {
+fun MaxIntrinsicWidth(children: @Composable() () -> Unit) {
ComplexLayout(children) {
layout { measurables, constraints ->
val measurable = measurables.firstOrNull()
@@ -177,7 +177,7 @@
* and the divider to use the same height.
*/
@Composable
-fun MaxIntrinsicHeight(@Children children: @Composable() () -> Unit) {
+fun MaxIntrinsicHeight(children: @Composable() () -> Unit) {
ComplexLayout(children) {
layout { measurables, constraints ->
val measurable = measurables.firstOrNull()
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Padding.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Padding.kt
index 8e8bbe45..79225a6 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Padding.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Padding.kt
@@ -53,7 +53,7 @@
@Composable
fun Padding(
padding: EdgeInsets,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Layout(layoutBlock = { measurables, constraints ->
val measurable = measurables.firstOrNull()
@@ -98,7 +98,7 @@
top: Dp = 0.dp,
right: Dp = 0.dp,
bottom: Dp = 0.dp,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Padding(
padding = EdgeInsets(left = left, top = top, right = right, bottom = bottom),
@@ -121,7 +121,7 @@
@Composable
fun Padding(
padding: Dp,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Padding(padding = EdgeInsets(padding), children = children)
}
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt
index 69bfe04..677be31 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt
@@ -52,7 +52,7 @@
private fun VerticalDragGestureDetector(
max: Px = Px.Infinity,
offsetChange: (Px) -> Unit,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val offset = +state { 0.px }
DragGestureDetector(
@@ -105,7 +105,7 @@
onScrollChanged: (position: Px, maxPosition: Px) -> Unit = { position, _ ->
scrollerPosition.position = position
},
- @Children child: @Composable() () -> Unit
+ child: @Composable() () -> Unit
) {
val maxPosition = +state { 0.px }
Layout(children = {
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt
index 5a08cf8..3846114 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt
@@ -35,7 +35,7 @@
* For a widget that does alignment and tries to be as large as possible, see [Align].
*/
@Composable
-fun Wrap(alignment: Alignment = Alignment.TopLeft, @Children children: @Composable() () -> Unit) {
+fun Wrap(alignment: Alignment = Alignment.TopLeft, children: @Composable() () -> Unit) {
Layout(layoutBlock = { measurables, constraints ->
val measurable = measurables.firstOrNull()
// The child cannot be larger than our max constraints, but we ignore min constraints.
diff --git a/ui/ui-material/build.gradle b/ui/ui-material/build.gradle
index 0392547..a6cacda 100644
--- a/ui/ui-material/build.gradle
+++ b/ui/ui-material/build.gradle
@@ -42,6 +42,7 @@
implementation project(":ui:ui-text")
implementation project(":ui:ui-animation")
implementation project(":ui:ui-foundation")
+ implementation project(":ui:ui-vector")
testImplementation(ANDROIDX_TEST_RULES)
testImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/MissingMaterialComponents.kt b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/MissingMaterialComponents.kt
index bc83b65..ff66edf 100644
--- a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/MissingMaterialComponents.kt
+++ b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/MissingMaterialComponents.kt
@@ -31,7 +31,7 @@
*/
@Composable
-fun Scaffold(appBar: @Composable() () -> Unit, @Children children: @Composable() () -> Unit) {
+fun Scaffold(appBar: @Composable() () -> Unit, children: @Composable() () -> Unit) {
FlexColumn {
inflexible {
appBar()
diff --git a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyTheme.kt b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyTheme.kt
index e80124f..f1d3435 100644
--- a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyTheme.kt
+++ b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyTheme.kt
@@ -36,7 +36,7 @@
val rallyBlue = Color(0xFF72DEFF.toInt())
@Composable
-fun RallyTheme(@Children children: @Composable() () -> Unit) {
+fun RallyTheme(children: @Composable() () -> Unit) {
val colors = MaterialColors(
primary = rallyGreen,
surface = Color(0xFF26282F.toInt()),
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
index d43cd7a..67f7537 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
@@ -63,7 +63,7 @@
private val options = listOf(itemOne, itemTwo, itemThree)
@Composable
- fun VerticalRadioGroupforTests(@Children children: @Composable() RadioGroupScope.() -> Unit) {
+ fun VerticalRadioGroupforTests(children: @Composable() RadioGroupScope.() -> Unit) {
RadioGroup {
Column {
children(p1 = this)
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/RippleEffectTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/RippleEffectTest.kt
index d7707a2..173bb9f 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/RippleEffectTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/RippleEffectTest.kt
@@ -192,7 +192,7 @@
private fun RippleCallback(
onRippleDrawn: (Matrix4) -> Unit = {},
onDispose: () -> Unit = {},
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val theme = RippleTheme(testRippleEffect(onRippleDrawn, onDispose)) { Color(0) }
CurrentRippleTheme.Provider(value = theme, children = children)
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Button.kt b/ui/ui-material/src/main/java/androidx/ui/material/Button.kt
index 9ece28d..5137a35 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Button.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Button.kt
@@ -74,7 +74,7 @@
color: Color = +themeColor { primary },
border: Border? = null,
elevation: Dp = 0.dp,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val textStyle = +themeTextStyle { button }
Surface(shape = shape, color = color, border = border, elevation = elevation) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt b/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
index 3ce7acf..019eb31 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
@@ -64,7 +64,7 @@
shape: Shape = CircleShape,
color: Color = +themeColor { primary },
elevation: Dp = 0.dp, // TODO(Andrey) add the default elevation when it ready b/123215187
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Button(color = color, onClick = onClick, shape = shape, elevation = elevation) {
Container(constraints = DpConstraints(minWidth = minSize, minHeight = minSize)) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/MaterialTheme.kt b/ui/ui-material/src/main/java/androidx/ui/material/MaterialTheme.kt
index 296208b..76ce952 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/MaterialTheme.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/MaterialTheme.kt
@@ -57,7 +57,6 @@
fun MaterialTheme(
colors: MaterialColors = MaterialColors(),
typography: MaterialTypography = MaterialTypography(),
- @Children
children: @Composable() () -> Unit
) {
Colors.Provider(value = colors) {
@@ -222,7 +221,7 @@
* guidelines for descendants
*/
@Composable
-fun MaterialRippleTheme(@Children children: @Composable() () -> Unit) {
+fun MaterialRippleTheme(children: @Composable() () -> Unit) {
val materialColors = +ambient(Colors)
val defaultTheme = +memo {
RippleTheme(
@@ -287,7 +286,7 @@
* Applies the default [Shape]s for all the surfaces.
*/
@Composable
-fun MaterialButtonShapeTheme(@Children children: @Composable() () -> Unit) {
+fun MaterialButtonShapeTheme(children: @Composable() () -> Unit) {
val value = +withDensity {
Shapes(
button = RoundedCornerShape(CornerSizes(4.dp))
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/ProgressIndicator.kt b/ui/ui-material/src/main/java/androidx/ui/material/ProgressIndicator.kt
index a8485a0..e2da5bc 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/ProgressIndicator.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/ProgressIndicator.kt
@@ -282,7 +282,7 @@
}
@Composable
-private fun CircularIndicatorContainer(@Children children: @Composable() () -> Unit) {
+private fun CircularIndicatorContainer(children: @Composable() () -> Unit) {
Wrap {
Padding(CircularIndicatorPadding) {
Container(width = CircularIndicatorDiameter, height = CircularIndicatorDiameter) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt b/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
index c3bbe64..ae0f300 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
@@ -76,7 +76,7 @@
* onOptionSelected = { ... })
*/
@Composable
-fun RadioGroup(@Children children: @Composable RadioGroupScope.() -> Unit) {
+fun RadioGroup(children: @Composable RadioGroupScope.() -> Unit) {
val scope = +memo { RadioGroupScope() }
children(p1 = scope)
}
@@ -142,7 +142,7 @@
fun RadioGroupItem(
selected: Boolean,
onSelect: () -> Unit,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Container {
Ripple(bounded = true) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/ripple/Ripple.kt b/ui/ui-material/src/main/java/androidx/ui/material/ripple/Ripple.kt
index 139a554..382e2ab 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/ripple/Ripple.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/ripple/Ripple.kt
@@ -60,7 +60,7 @@
fun Ripple(
bounded: Boolean,
radius: Dp? = null,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val density = +ambientDensity()
val rippleSurface = +ambientRippleSurface()
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/ripple/RippleSurface.kt b/ui/ui-material/src/main/java/androidx/ui/material/ripple/RippleSurface.kt
index 48d547f..aa0dd8c 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/ripple/RippleSurface.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/ripple/RippleSurface.kt
@@ -87,7 +87,7 @@
@Composable
fun RippleSurface(
color: Color?,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
val owner = +memo { RippleSurfaceOwnerImpl() }
owner.backgroundColor = color
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/surface/Card.kt b/ui/ui-material/src/main/java/androidx/ui/material/surface/Card.kt
index 52f1f13f..544666b 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/surface/Card.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/surface/Card.kt
@@ -49,7 +49,7 @@
color: Color = +themeColor { surface },
border: Border? = null,
elevation: Dp = 0.dp,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
// TODO(Andrey: This currently adds no logic on top of Surface, I just reserve the name
// for now. We will see what will be the additional Card specific logic later.
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/surface/Surface.kt b/ui/ui-material/src/main/java/androidx/ui/material/surface/Surface.kt
index 0981820..8a75fb3 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/surface/Surface.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/surface/Surface.kt
@@ -84,7 +84,7 @@
color: Color = +themeColor { surface },
border: Border? = null,
elevation: Dp = 0.dp,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
SurfaceLayout {
DrawShadow(shape = shape, elevation = elevation)
@@ -110,7 +110,7 @@
* TODO("Andrey: Should be replaced with some basic layout implementation when we have it")
*/
@Composable
-private fun SurfaceLayout(@Children children: @Composable() () -> Unit) {
+private fun SurfaceLayout(children: @Composable() () -> Unit) {
Layout(children = children, layoutBlock = { measurables, constraints ->
if (measurables.size > 1) {
throw IllegalStateException("Surface can have only one direct measurable child!")
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/surface/TransparentSurface.kt b/ui/ui-material/src/main/java/androidx/ui/material/surface/TransparentSurface.kt
index 29aea48..f68b95c 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/surface/TransparentSurface.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/surface/TransparentSurface.kt
@@ -38,7 +38,7 @@
@Composable
fun TransparentSurface(
shape: Shape = RectangleShape,
- @Children children: @Composable() () -> Unit
+ children: @Composable() () -> Unit
) {
Surface(shape = shape, children = children, color = Color.Transparent)
}
diff --git a/ui/ui-platform/build.gradle b/ui/ui-platform/build.gradle
index b6a9b9a..651fc72 100644
--- a/ui/ui-platform/build.gradle
+++ b/ui/ui-platform/build.gradle
@@ -54,6 +54,8 @@
androidTestImplementation MOCKITO_KOTLIN, {
exclude group: 'org.mockito' // to keep control on the mockito version
}
+
+ implementation "androidx.core:core:1.0.0"
}
androidx {
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
index 1bdfbc3..bd000ca 100644
--- a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
@@ -777,7 +777,12 @@
* @property key The key object used to identify the key
* @property value The value of the data being stored in the hierarchy
*/
-class DataNode<T>(val key: DataNodeKey<T>, var value: T) : ComponentNode()
+class DataNode<T>(val key: DataNodeKey<T>, var value: T) : ComponentNode() {
+ override fun attach(owner: Owner) {
+ super.attach(owner)
+ parentLayoutNode?.requestRemeasure()
+ }
+}
/**
* Returns [ComponentNode.owner] or throws if it is null.
diff --git a/ui/ui-text/api/1.0.0-alpha01.txt b/ui/ui-text/api/1.0.0-alpha01.txt
index a6e76c9..a37b2f2 100644
--- a/ui/ui-text/api/1.0.0-alpha01.txt
+++ b/ui/ui-text/api/1.0.0-alpha01.txt
@@ -38,10 +38,12 @@
package androidx.ui.text {
public final class AnnotatedString {
- ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+ ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
method public String component1();
method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> component2();
- method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+ method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> component3();
+ method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
+ method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> getParagraphStyles();
method public String getText();
method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> getTextStyles();
}
@@ -76,6 +78,10 @@
method public String _canonicalizeRegionCode(String regionCode);
}
+ public final class MultiParagraphKt {
+ ctor public MultiParagraphKt();
+ }
+
public interface Paragraph {
method public float getBaseline();
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
@@ -127,6 +133,7 @@
method public androidx.ui.text.style.TextAlign? getTextAlign();
method public androidx.ui.text.style.TextDirection? getTextDirection();
method public androidx.ui.text.style.TextIndent? getTextIndent();
+ method public androidx.ui.text.ParagraphStyle merge(androidx.ui.text.ParagraphStyle? other = null);
}
public final class TextPainter {
diff --git a/ui/ui-text/api/current.txt b/ui/ui-text/api/current.txt
index a6e76c9..a37b2f2 100644
--- a/ui/ui-text/api/current.txt
+++ b/ui/ui-text/api/current.txt
@@ -38,10 +38,12 @@
package androidx.ui.text {
public final class AnnotatedString {
- ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+ ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
method public String component1();
method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> component2();
- method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+ method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> component3();
+ method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
+ method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> getParagraphStyles();
method public String getText();
method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> getTextStyles();
}
@@ -76,6 +78,10 @@
method public String _canonicalizeRegionCode(String regionCode);
}
+ public final class MultiParagraphKt {
+ ctor public MultiParagraphKt();
+ }
+
public interface Paragraph {
method public float getBaseline();
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
@@ -127,6 +133,7 @@
method public androidx.ui.text.style.TextAlign? getTextAlign();
method public androidx.ui.text.style.TextDirection? getTextDirection();
method public androidx.ui.text.style.TextIndent? getTextIndent();
+ method public androidx.ui.text.ParagraphStyle merge(androidx.ui.text.ParagraphStyle? other = null);
}
public final class TextPainter {
diff --git a/ui/ui-text/api/restricted_1.0.0-alpha01.txt b/ui/ui-text/api/restricted_1.0.0-alpha01.txt
index 1b16ca0..d02f68726 100644
--- a/ui/ui-text/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-text/api/restricted_1.0.0-alpha01.txt
@@ -52,10 +52,12 @@
package androidx.ui.text {
public final class AnnotatedString {
- ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+ ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
method public String component1();
method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> component2();
- method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+ method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> component3();
+ method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
+ method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> getParagraphStyles();
method public String getText();
method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> getTextStyles();
}
@@ -90,6 +92,10 @@
method public String _canonicalizeRegionCode(String regionCode);
}
+ public final class MultiParagraphKt {
+ ctor public MultiParagraphKt();
+ }
+
public interface Paragraph {
method public float getBaseline();
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
@@ -141,6 +147,7 @@
method public androidx.ui.text.style.TextAlign? getTextAlign();
method public androidx.ui.text.style.TextDirection? getTextDirection();
method public androidx.ui.text.style.TextIndent? getTextIndent();
+ method public androidx.ui.text.ParagraphStyle merge(androidx.ui.text.ParagraphStyle? other = null);
}
public final class TextPainter {
diff --git a/ui/ui-text/api/restricted_current.txt b/ui/ui-text/api/restricted_current.txt
index 1b16ca0..d02f68726 100644
--- a/ui/ui-text/api/restricted_current.txt
+++ b/ui/ui-text/api/restricted_current.txt
@@ -52,10 +52,12 @@
package androidx.ui.text {
public final class AnnotatedString {
- ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+ ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
method public String component1();
method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> component2();
- method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+ method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> component3();
+ method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
+ method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> getParagraphStyles();
method public String getText();
method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> getTextStyles();
}
@@ -90,6 +92,10 @@
method public String _canonicalizeRegionCode(String regionCode);
}
+ public final class MultiParagraphKt {
+ ctor public MultiParagraphKt();
+ }
+
public interface Paragraph {
method public float getBaseline();
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
@@ -141,6 +147,7 @@
method public androidx.ui.text.style.TextAlign? getTextAlign();
method public androidx.ui.text.style.TextDirection? getTextDirection();
method public androidx.ui.text.style.TextIndent? getTextIndent();
+ method public androidx.ui.text.ParagraphStyle merge(androidx.ui.text.ParagraphStyle? other = null);
}
public final class TextPainter {
diff --git a/ui/ui-text/build.gradle b/ui/ui-text/build.gradle
index feccef84..9772691 100644
--- a/ui/ui-text/build.gradle
+++ b/ui/ui-text/build.gradle
@@ -40,6 +40,7 @@
implementation project(":compose:compose-runtime")
implementation project(":ui:ui-android-text")
implementation project(":ui:ui-core")
+ implementation project(":ui:ui-vector")
testImplementation(ANDROIDX_TEST_RULES)
testImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt
index faaf76d..a0e9bda 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt
@@ -47,6 +47,8 @@
import androidx.ui.text.style.TextOverflow
import androidx.ui.core.Sp
import androidx.ui.core.sp
+import androidx.ui.text.AnnotatedString
+import androidx.ui.text.style.TextIndent
val displayText = "Text Demo"
val displayTextChinese = "文本演示"
@@ -99,6 +101,16 @@
TextDemoComposableTextSpan()
TagLine(tag = "fontSizeScale")
TextDemoFontSizeScale()
+ TagLine(tag = "multiple paragraphs basic")
+ TextDemoParagraph()
+ TagLine(tag = "multiple paragraphs TextAlign")
+ TextDemoParagraphTextAlign()
+ TagLine(tag = "multiple paragraphs line height")
+ TextDemoParagraphLineHeight()
+ TagLine(tag = "multiple paragraphs TextIndent")
+ TextDemoParagraphIndent()
+ TagLine(tag = "multiple paragraphs TextDirection")
+ TextDemoParagraphTextDirection()
}
}
}
@@ -685,3 +697,131 @@
}
}
}
+
+@Composable
+fun TextDemoParagraph() {
+ val text1 = "paragraph1 paragraph1 paragraph1 paragraph1 paragraph1"
+ val text2 = "paragraph2 paragraph2 paragraph2 paragraph2 paragraph2"
+ Text(
+ text = AnnotatedString(
+ text = text1 + text2,
+ textStyles = listOf(),
+ paragraphStyles = listOf(
+ AnnotatedString.Item(ParagraphStyle(), text1.length, text1.length)
+ )
+ ),
+ style = TextStyle(fontSize = fontSize8)
+ )
+}
+
+@Composable
+fun TextDemoParagraphTextAlign() {
+ var text = ""
+ val paragraphStyles = mutableListOf<AnnotatedString.Item<ParagraphStyle>>()
+ TextAlign.values().map { textAlign ->
+ val str = List(4) { "TextAlign.$textAlign" }.joinToString(" ")
+ val paragraphStyle = ParagraphStyle(textAlign = textAlign)
+ Pair(str, paragraphStyle)
+ }.forEach { (str, paragraphStyle) ->
+ paragraphStyles.add(
+ AnnotatedString.Item(
+ paragraphStyle,
+ text.length,
+ text.length + str.length
+ )
+ )
+ text += str
+ }
+
+ Text(
+ text = AnnotatedString(
+ text = text,
+ textStyles = listOf(),
+ paragraphStyles = paragraphStyles
+ ),
+ style = TextStyle(fontSize = fontSize8)
+ )
+}
+
+@Composable
+fun TextDemoParagraphLineHeight() {
+ val text1 = "LineHeight=1.0f LineHeight=1.0f LineHeight=1.0f LineHeight=1.0f"
+ val text2 = "LineHeight=1.5f LineHeight=1.5f LineHeight=1.5f LineHeight=1.5f"
+ val text3 = "LineHeight=3.0f LineHeight=3.0f LineHeight=3.0f LineHeight=3.0f"
+
+ Text(
+ text = AnnotatedString(
+ text = text1 + text2 + text3,
+ textStyles = listOf(),
+ paragraphStyles = listOf(
+ AnnotatedString.Item(
+ ParagraphStyle(lineHeight = 1.0f),
+ 0,
+ text1.length
+ ),
+ AnnotatedString.Item(
+ ParagraphStyle(lineHeight = 1.5f),
+ text1.length,
+ text1.length + text2.length
+ ),
+ AnnotatedString.Item(
+ ParagraphStyle(lineHeight = 2f),
+ text1.length + text2.length,
+ text1.length + text2.length + text3.length
+ )
+ )
+ ),
+ style = TextStyle(fontSize = fontSize8)
+ )
+}
+
+@Composable
+fun TextDemoParagraphIndent() {
+ val text1 = "TextIndent firstLine TextIndent firstLine TextIndent firstLine"
+ val text2 = "TextIndent restLine TextIndent restLine TextIndent restLine"
+
+ Text(
+ text = AnnotatedString(
+ text = text1 + text2,
+ textStyles = listOf(),
+ paragraphStyles = listOf(
+ AnnotatedString.Item(
+ ParagraphStyle(textIndent = TextIndent(firstLine = 100.px)),
+ 0,
+ text1.length
+ ),
+ AnnotatedString.Item(
+ ParagraphStyle(textIndent = TextIndent(restLine = 100.px)),
+ text1.length,
+ text1.length + text2.length
+ )
+ )
+ ),
+ style = TextStyle(fontSize = fontSize8)
+ )
+}
+
+@Composable
+fun TextDemoParagraphTextDirection() {
+ val ltrText = "Hello World! Hello World! Hello World! Hello World! Hello World!"
+ val rtlText = "مرحبا بالعالم مرحبا بالعالم مرحبا بالعالم مرحبا بالعالم مرحبا بالعالم"
+ Text(
+ text = AnnotatedString(
+ text = ltrText + rtlText,
+ textStyles = listOf(),
+ paragraphStyles = listOf(
+ AnnotatedString.Item(
+ ParagraphStyle(textDirection = TextDirection.Ltr),
+ 0,
+ ltrText.length
+ ),
+ AnnotatedString.Item(
+ ParagraphStyle(textDirection = TextDirection.Rtl),
+ ltrText.length,
+ ltrText.length + rtlText.length
+ )
+ )
+ ),
+ style = TextStyle(fontSize = fontSize8)
+ )
+}
\ No newline at end of file
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt
new file mode 100644
index 0000000..75c21b6
--- /dev/null
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt
@@ -0,0 +1,1925 @@
+/*
+ * Copyright 2019 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.ui.text
+
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import androidx.test.filters.Suppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.ui.core.Density
+import androidx.ui.core.PxPosition
+import androidx.ui.core.Sp
+import androidx.ui.core.px
+import androidx.ui.core.sp
+import androidx.ui.core.withDensity
+import androidx.ui.engine.geometry.Rect
+import androidx.ui.text.style.TextDirection
+import androidx.ui.text.FontTestData.Companion.BASIC_MEASURE_FONT
+import androidx.ui.text.font.FontFamily
+import androidx.ui.text.font.asFontFamily
+import androidx.ui.painting.Path
+import androidx.ui.painting.PathOperation
+import androidx.ui.text.style.TextAlign
+import androidx.ui.text.style.TextIndent
+import com.nhaarman.mockitokotlin2.mock
+import org.hamcrest.Matchers.equalTo
+import org.junit.Assert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+@SmallTest
+class MultiParagraphIntegrationTest {
+ private val fontFamilyMeasureFont = BASIC_MEASURE_FONT.asFontFamily()
+ private val context = InstrumentationRegistry.getInstrumentation().context
+ private val defaultDensity = Density(density = 1f)
+ private val cursorWidth = 4f
+
+ @Test
+ fun empty_string() {
+ withDensity(defaultDensity) {
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val text = ""
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = 100.0f))
+
+ assertThat(paragraph.width, equalTo(100.0f))
+
+ assertThat(paragraph.height, equalTo(fontSizeInPx))
+ // defined in sample_font
+ assertThat(paragraph.baseline, equalTo(fontSizeInPx * 0.8f))
+ assertThat(paragraph.maxIntrinsicWidth, equalTo(0.0f))
+ assertThat(paragraph.minIntrinsicWidth, equalTo(0.0f))
+ // TODO(Migration/siyamed): no baseline query per line?
+ // TODO(Migration/siyamed): no line count?
+ }
+ }
+
+ @Test
+ fun single_line_default_values() {
+ withDensity(defaultDensity) {
+ val fontSize = 50.sp
+ val fontSizeInpx = fontSize.toPx().value
+
+ for (text in arrayOf("xyz", "\u05D0\u05D1\u05D2")) {
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ // width greater than text width - 150
+ paragraph.layout(ParagraphConstraints(width = 200.0f))
+
+ assertThat(text, paragraph.width, equalTo(200.0f))
+ assertThat(text, paragraph.height, equalTo(fontSizeInpx))
+ // defined in sample_font
+ assertThat(text, paragraph.baseline, equalTo(fontSizeInpx * 0.8f))
+ assertThat(
+ text,
+ paragraph.maxIntrinsicWidth,
+ equalTo(fontSizeInpx * text.length)
+ )
+ assertThat(text, paragraph.minIntrinsicWidth, equalTo(0.0f))
+ }
+ }
+ }
+
+ @Test
+ fun line_break_default_values() {
+ withDensity(defaultDensity) {
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+
+ for (text in arrayOf("abcdef", "\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5")) {
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ // 3 chars width
+ paragraph.layout(ParagraphConstraints(width = 3 * fontSizeInPx))
+
+ // 3 chars
+ assertThat(text, paragraph.width, equalTo(3 * fontSizeInPx))
+ // 2 lines, 1 line gap
+ assertThat(
+ text,
+ paragraph.height,
+ equalTo(2 * fontSizeInPx + fontSizeInPx / 5.0f)
+ )
+ // defined in sample_font
+ assertThat(text, paragraph.baseline, equalTo(fontSizeInPx * 0.8f))
+ assertThat(
+ text,
+ paragraph.maxIntrinsicWidth,
+ equalTo(fontSizeInPx * text.length)
+ )
+ assertThat(text, paragraph.minIntrinsicWidth, equalTo(0.0f))
+ }
+ }
+ }
+
+ @Test
+ fun didExceedMaxLines_withMaxLinesSmallerThanTextLines_returnsTrue() {
+ val text = "aaa\naa"
+ val maxLines = text.lines().size - 1
+ val paragraph = simpleMultiParagraph(text = text, maxLines = maxLines)
+
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+ assertThat(paragraph.didExceedMaxLines, equalTo(true))
+ }
+
+ @Test
+ fun didExceedMaxLines_withMaxLinesEqualToTextLines_returnsFalse() {
+ val text = "aaa\naa"
+ val maxLines = text.lines().size
+ val paragraph = simpleMultiParagraph(text = text, maxLines = maxLines)
+
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+ assertThat(paragraph.didExceedMaxLines, equalTo(false))
+ }
+
+ @Test
+ fun didExceedMaxLines_withMaxLinesGreaterThanTextLines_returnsFalse() {
+ val text = "aaa\naa"
+ val maxLines = text.lines().size + 1
+ val paragraph = simpleMultiParagraph(text = text, maxLines = maxLines)
+
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+ assertThat(paragraph.didExceedMaxLines, equalTo(false))
+ }
+
+ @Test
+ fun didExceedMaxLines_withMaxLinesSmallerThanTextLines_withLineWrap_returnsTrue() {
+ withDensity(defaultDensity) {
+ val text = "aa"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val maxLines = 1
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ maxLines = maxLines
+ )
+
+ // One line can only contain 1 character
+ paragraph.layout(ParagraphConstraints(width = fontSizeInPx))
+ assertThat(paragraph.didExceedMaxLines, equalTo(true))
+ }
+ }
+
+ @Test
+ fun didExceedMaxLines_withMaxLinesEqualToTextLines_withLineWrap_returnsFalse() {
+ val text = "a"
+ val maxLines = text.lines().size
+ val paragraph = simpleMultiParagraph(text = text, fontSize = 50.sp, maxLines = maxLines)
+
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+ assertThat(paragraph.didExceedMaxLines, equalTo(false))
+ }
+
+ @Test
+ fun didExceedMaxLines_withMaxLinesGreaterThanTextLines_withLineWrap_returnsFalse() {
+ withDensity(defaultDensity) {
+ val text = "aa"
+ val maxLines = 3
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ maxLines = maxLines
+ )
+
+ // One line can only contain 1 character
+ paragraph.layout(ParagraphConstraints(width = fontSizeInPx))
+ assertThat(paragraph.didExceedMaxLines, equalTo(false))
+ }
+ }
+
+ @Test
+ fun getOffsetForPosition_ltr() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+ // test positions that are 1, fontSize+1, 2fontSize+1 which maps to chars 0, 1, 2 ...
+ for (i in 0..text.length) {
+ val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+ val offset = paragraph.getOffsetForPosition(position)
+ assertThat(
+ "offset at index $i, position $position does not match",
+ offset,
+ equalTo(i)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getOffsetForPosition_rtl() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ // test positions that are 1, fontSize+1, 2fontSize+1 which maps to chars .., 2, 1, 0
+ for (i in 0..text.length) {
+ val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+ val offset = paragraph.getOffsetForPosition(position)
+ assertThat(
+ "offset at index $i, position $position does not match",
+ offset,
+ equalTo(text.length - i)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getOffsetForPosition_ltr_multiline() {
+ withDensity(defaultDensity) {
+ val firstLine = "abc"
+ val secondLine = "def"
+ val text = firstLine + secondLine
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = firstLine.length * fontSizeInPx))
+
+ // test positions are 1, fontSize+1, 2fontSize+1 and always on the second line
+ // which maps to chars 3, 4, 5
+ for (i in 0..secondLine.length) {
+ val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx * 1.5f).px)
+ val offset = paragraph.getOffsetForPosition(position)
+ assertThat(
+ "offset at index $i, position $position, second line does not match",
+ offset,
+ equalTo(i + firstLine.length)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getOffsetForPosition_rtl_multiline() {
+ withDensity(defaultDensity) {
+ val firstLine = "\u05D0\u05D1\u05D2"
+ val secondLine = "\u05D3\u05D4\u05D5"
+ val text = firstLine + secondLine
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = firstLine.length * fontSizeInPx))
+
+ // test positions are 1, fontSize+1, 2fontSize+1 and always on the second line
+ // which maps to chars 5, 4, 3
+ for (i in 0..secondLine.length) {
+ val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx * 1.5f).px)
+ val offset = paragraph.getOffsetForPosition(position)
+ assertThat(
+ "offset at index $i, position $position, second line does not match",
+ offset,
+ equalTo(text.length - i)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getOffsetForPosition_ltr_width_outOfBounds() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ // greater than width
+ var position = PxPosition((fontSizeInPx * text.length * 2).px, (fontSizeInPx / 2).px)
+ var offset = paragraph.getOffsetForPosition(position)
+ assertThat(offset, equalTo(text.length))
+
+ // negative
+ position = PxPosition((-1 * fontSizeInPx).px, (fontSizeInPx / 2).px)
+ offset = paragraph.getOffsetForPosition(position)
+ assertThat(offset, equalTo(0))
+ }
+ }
+
+ @Test
+ fun getOffsetForPosition_ltr_height_outOfBounds() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ // greater than height
+ var position = PxPosition((fontSizeInPx / 2).px, (fontSizeInPx * text.length * 2).px)
+ var offset = paragraph.getOffsetForPosition(position)
+ assertThat(offset, equalTo(0))
+
+ // negative
+ position = PxPosition((fontSizeInPx / 2).px, (-1 * fontSizeInPx).px)
+ offset = paragraph.getOffsetForPosition(position)
+ assertThat(offset, equalTo(0))
+ }
+ }
+
+ @Test
+ fun getOffsetForPosition_lineBreak() {
+ withDensity(defaultDensity) {
+ val text = "abc\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ assertThat(
+ paragraph.getOffsetForPosition(PxPosition((3 * fontSizeInPx).px, 0.px)),
+ equalTo(3)
+ )
+
+ assertThat(
+ paragraph.getOffsetForPosition(PxPosition(0.px, (fontSizeInPx * 1.5f).px)),
+ equalTo(4)
+ )
+ }
+ }
+
+ @Test
+ fun getOffsetForPosition_multiple_paragraph() {
+ withDensity(defaultDensity) {
+ val text = "abcdef"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ paragraphStyles = listOf(
+ AnnotatedString.Item(ParagraphStyle(), 0, 3)
+ )
+ )
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ for (i in 0 until 3) {
+ assertThat(
+ paragraph.getOffsetForPosition(PxPosition((i * fontSizeInPx).px, 0.px)),
+ equalTo(i)
+ )
+ }
+
+ for (i in 3 until 6) {
+ assertThat(
+ paragraph.getOffsetForPosition(
+ PxPosition(((i - 3) * fontSizeInPx).px, fontSizeInPx.px)
+ ),
+ equalTo(i)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getBoundingBox_ltr_singleLine() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+ for (i in 0 until text.length) {
+ val box = paragraph.getBoundingBox(i)
+ assertThat(box.left, equalTo(i * fontSizeInPx))
+ assertThat(box.right, equalTo((i + 1) * fontSizeInPx))
+ assertThat(box.top, equalTo(0f))
+ assertThat(box.bottom, equalTo(fontSizeInPx))
+ }
+ }
+ }
+
+ @Test
+ fun getBoundingBox_ltr_multiLines() {
+ withDensity(defaultDensity) {
+ val firstLine = "abc"
+ val secondLine = "def"
+ val text = firstLine + secondLine
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = firstLine.length * fontSizeInPx))
+
+ // test positions are 3, 4, 5 and always on the second line
+ // which maps to chars 3, 4, 5
+ for (i in 0..secondLine.length - 1) {
+ val textPosition = i + firstLine.length
+ val box = paragraph.getBoundingBox(textPosition)
+ assertThat(box.left, equalTo(i * fontSizeInPx))
+ assertThat(box.right, equalTo((i + 1) * fontSizeInPx))
+ assertThat(box.top, equalTo(fontSizeInPx))
+ assertThat(box.bottom, equalTo((2f + 1 / 5f) * fontSizeInPx))
+ }
+ }
+ }
+
+ @Test(expected = java.lang.IndexOutOfBoundsException::class)
+ fun getBoundingBox_ltr_textPosition_negative() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ paragraph.getBoundingBox(-1)
+ }
+ }
+
+ @Suppress
+ @Test(expected = java.lang.IndexOutOfBoundsException::class)
+ fun getBoundingBox_ltr_textPosition_larger_than_length_throw_exception() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ val textPosition = text.length + 1
+ paragraph.getBoundingBox(textPosition)
+ }
+ }
+
+ @Test(expected = java.lang.AssertionError::class)
+ fun getCursorRect_larger_than_length_throw_exception() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ paragraph.getCursorRect(text.length + 1)
+ }
+ }
+
+ @Test(expected = java.lang.AssertionError::class)
+ fun getCursorRect_negative_throw_exception() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ paragraph.getCursorRect(-1)
+ }
+ }
+
+ @Test
+ fun getCursorRect_ltr_singleLine() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ for (i in 0 until text.length) {
+ val cursorRect = paragraph.getCursorRect(i)
+ val cursorXOffset = i * fontSizeInPx
+ assertThat(
+ cursorRect,
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = 0f,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getCursorRect_ltr_multiLines() {
+ withDensity(defaultDensity) {
+ val text = "abcdef"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val charsPerLine = 3
+
+ paragraph.layout(ParagraphConstraints(width = charsPerLine * fontSizeInPx))
+
+ for (i in 0 until charsPerLine) {
+ val cursorXOffset = i * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = 0f,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+ }
+
+ for (i in charsPerLine until text.length) {
+ val cursorXOffset = (i % charsPerLine) * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = fontSizeInPx,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx * 2.2f
+ ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getCursorRect_rtl_singleLine() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ for (i in 0 until text.length) {
+ val cursorXOffset = (text.length - i) * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = 0f,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getCursorRect_rtl_multiLines() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val charsPerLine = 3
+
+ paragraph.layout(ParagraphConstraints(width = charsPerLine * fontSizeInPx))
+
+ for (i in 0 until charsPerLine) {
+ val cursorXOffset = (charsPerLine - i) * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = 0f,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+ }
+
+ for (i in charsPerLine until text.length) {
+ val cursorXOffset = (charsPerLine - i % charsPerLine) * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = fontSizeInPx,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx * 2.2f
+ ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_ltr_singleLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ for (i in 0..text.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(fontSizeInPx * i)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_rtl_singleLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ for (i in 0..text.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(width - fontSizeInPx * i)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_bidi_singleLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val ltrText = "abc"
+ val rtlText = "\u05D0\u05D1\u05D2"
+ val text = ltrText + rtlText
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ for (i in 0..ltrText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(fontSizeInPx * i)
+ )
+ }
+
+ for (i in 1 until rtlText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i + ltrText.length),
+ equalTo(width - fontSizeInPx * i)
+ )
+ }
+
+ assertThat(
+ paragraph.getPrimaryHorizontal(text.length),
+ equalTo(width)
+ )
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_ltr_singleLine_textDirectionRtl() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Rtl
+ )
+ val width = text.length * fontSizeInPx
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(0), equalTo(width))
+
+ for (i in 1 until text.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(fontSizeInPx * i)
+ )
+ }
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_rtl_singleLine_textDirectionLtr() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Ltr
+ )
+ val width = text.length * fontSizeInPx
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(0), equalTo(0f))
+
+ for (i in 1 until text.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(width - fontSizeInPx * i)
+ )
+ }
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(width))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_bidi_singleLine_textDirectionLtr() {
+ withDensity(defaultDensity) {
+ val ltrText = "abc"
+ val rtlText = "\u05D0\u05D1\u05D2"
+ val text = ltrText + rtlText
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Ltr
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ for (i in 0..ltrText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(fontSizeInPx * i)
+ )
+ }
+
+ for (i in 1 until rtlText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i + ltrText.length),
+ equalTo(width - fontSizeInPx * i)
+ )
+ }
+
+ assertThat(
+ paragraph.getPrimaryHorizontal(text.length),
+ equalTo(width)
+ )
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_bidi_singleLine_textDirectionRtl() {
+ withDensity(defaultDensity) {
+ val ltrText = "abc"
+ val rtlText = "\u05D0\u05D1\u05D2"
+ val text = ltrText + rtlText
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Rtl
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(0), equalTo(width))
+ // Notice that abc is
+ for (i in 1 until ltrText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(rtlText.length * fontSizeInPx + i * fontSizeInPx)
+ )
+ }
+
+ for (i in 0..rtlText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i + ltrText.length),
+ equalTo(rtlText.length * fontSizeInPx - i * fontSizeInPx)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_ltr_newLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val text = "abc\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_rtl_newLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_ltr_newLine_textDirectionRtl() {
+ withDensity(defaultDensity) {
+ val text = "abc\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Rtl
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(width))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_rtl_newLine_textDirectionLtr() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Ltr
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+ }
+ }
+
+ @Test
+ fun getLineForOffset_singleLine() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ for (i in 0..text.lastIndex) {
+ assertThat(paragraph.getLineForOffset(i), equalTo(0))
+ }
+ }
+ }
+
+ @Test
+ fun getLineForOffset_multiLines() {
+ withDensity(defaultDensity) {
+ val text = "a\nb\nc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ for (i in 0..text.lastIndex) {
+ assertThat(paragraph.getLineForOffset(i), equalTo(i / 2))
+ }
+ }
+ }
+
+ @Test
+ fun getLineForOffset_multiParagraph() {
+ withDensity(defaultDensity) {
+ val text = "abcd"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ paragraphStyles = listOf(AnnotatedString.Item(ParagraphStyle(), 0, 2))
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+ assertThat(paragraph.getLineForOffset(0), equalTo(0))
+ assertThat(paragraph.getLineForOffset(1), equalTo(0))
+ assertThat(paragraph.getLineForOffset(2), equalTo(1))
+ assertThat(paragraph.getLineForOffset(3), equalTo(1))
+ }
+ }
+
+ @Test
+ fun getLineForOffset_emptyParagraph() {
+ withDensity(defaultDensity) {
+ val text = "abcd"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ paragraphStyles = listOf(AnnotatedString.Item(ParagraphStyle(), 2, 2))
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+ assertThat(paragraph.getLineForOffset(0), equalTo(0))
+ assertThat(paragraph.getLineForOffset(1), equalTo(0))
+ // The empty paragraph takes one line
+ assertThat(paragraph.getLineForOffset(2), equalTo(2))
+ assertThat(paragraph.getLineForOffset(3), equalTo(2))
+ }
+ }
+
+ @Test(expected = java.lang.IndexOutOfBoundsException::class)
+ fun getLineForOffset_negativeOffset() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ paragraph.getLineForOffset(-1)
+ }
+ }
+
+ @Test(expected = java.lang.IndexOutOfBoundsException::class)
+ fun getLineForOffset_outOfBoundary() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ paragraph.getLineForOffset(text.length)
+ }
+ }
+
+ @Test
+ fun testGetPathForRange_singleLine() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val expectedPath = Path()
+ val lineLeft = paragraph.getLineLeft(0)
+ val lineRight = paragraph.getLineRight(0)
+ expectedPath.addRect(
+ Rect(
+ lineLeft,
+ 0f,
+ lineRight - fontSizeInPx,
+ fontSizeInPx
+ )
+ )
+
+ // Select "ab"
+ val actualPath = paragraph.getPathForRange(0, 2)
+
+ val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+ assertThat(diff, equalTo(Rect.zero))
+ }
+ }
+
+ @Test
+ fun testGetPathForRange_multiLines() {
+ withDensity(defaultDensity) {
+ val text = "abc\nabc"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val expectedPath = Path()
+ val firstLineLeft = paragraph.getLineLeft(0)
+ val secondLineLeft = paragraph.getLineLeft(1)
+ val firstLineRight = paragraph.getLineRight(0)
+ val secondLineRight = paragraph.getLineRight(1)
+ expectedPath.addRect(
+ Rect(
+ firstLineLeft + fontSizeInPx,
+ 0f,
+ firstLineRight,
+ fontSizeInPx
+ )
+ )
+ expectedPath.addRect(
+ Rect(
+ secondLineLeft,
+ fontSizeInPx,
+ secondLineRight - fontSizeInPx,
+ paragraph.height
+ )
+ )
+
+ // Select "bc\nab"
+ val actualPath = paragraph.getPathForRange(1, 6)
+
+ val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+ assertThat(diff, equalTo(Rect.zero))
+ }
+ }
+
+ @Test
+ fun testGetPathForRange_Bidi() {
+ withDensity(defaultDensity) {
+ val textLTR = "Hello"
+ val textRTL = "שלום"
+ val text = textLTR + textRTL
+ val selectionLTRStart = 2
+ val selectionRTLEnd = 2
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val expectedPath = Path()
+ val lineLeft = paragraph.getLineLeft(0)
+ val lineRight = paragraph.getLineRight(0)
+ expectedPath.addRect(
+ Rect(
+ lineLeft + selectionLTRStart * fontSizeInPx,
+ 0f,
+ lineLeft + textLTR.length * fontSizeInPx,
+ fontSizeInPx
+ )
+ )
+ expectedPath.addRect(
+ Rect(
+ lineRight - selectionRTLEnd * fontSizeInPx,
+ 0f,
+ lineRight,
+ fontSizeInPx
+ )
+ )
+
+ // Select "llo..של"
+ val actualPath =
+ paragraph.getPathForRange(selectionLTRStart, textLTR.length + selectionRTLEnd)
+
+ val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+ assertThat(diff, equalTo(Rect.zero))
+ }
+ }
+
+ @Test
+ fun testGetPathForRange_Start_Equals_End_Returns_Empty_Path() {
+ val text = "abc"
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = 20.sp
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val actualPath = paragraph.getPathForRange(1, 1)
+
+ assertThat(actualPath.getBounds(), equalTo(Rect.zero))
+ }
+
+ @Test
+ fun testGetPathForRange_Empty_Text() {
+ val text = ""
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = 20.sp
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val actualPath = paragraph.getPathForRange(0, 0)
+
+ assertThat(actualPath.getBounds(), equalTo(Rect.zero))
+ }
+
+ @Test
+ fun testGetPathForRange_Surrogate_Pair_Start_Middle_Second_Character_Selected() {
+ withDensity(defaultDensity) {
+ val text = "\uD834\uDD1E\uD834\uDD1F"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val expectedPath = Path()
+ val lineRight = paragraph.getLineRight(0)
+ expectedPath.addRect(Rect(lineRight / 2, 0f, lineRight, fontSizeInPx))
+
+ // Try to select "\uDD1E\uD834\uDD1F", only "\uD834\uDD1F" is selected.
+ val actualPath = paragraph.getPathForRange(1, text.length)
+
+ val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+ assertThat(diff, equalTo(Rect.zero))
+ }
+ }
+
+ @Test
+ fun testGetPathForRange_Surrogate_Pair_End_Middle_Second_Character_Selected() {
+ withDensity(defaultDensity) {
+ val text = "\uD834\uDD1E\uD834\uDD1F"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val expectedPath = Path()
+ val lineRight = paragraph.getLineRight(0)
+ expectedPath.addRect(Rect(lineRight / 2, 0f, lineRight, fontSizeInPx))
+
+ // Try to select "\uDD1E\uD834", actually "\uD834\uDD1F" is selected.
+ val actualPath = paragraph.getPathForRange(1, text.length - 1)
+
+ val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+ assertThat(diff, equalTo(Rect.zero))
+ }
+ }
+
+ @Test
+ fun testGetPathForRange_Surrogate_Pair_Start_Middle_End_Same_Character_Returns_Line_Segment() {
+ withDensity(defaultDensity) {
+ val text = "\uD834\uDD1E\uD834\uDD1F"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val expectedPath = Path()
+ val lineRight = paragraph.getLineRight(0)
+ expectedPath.addRect(Rect(lineRight / 2, 0f, lineRight / 2, fontSizeInPx))
+
+ // Try to select "\uDD1E", get vertical line segment after this character.
+ val actualPath = paragraph.getPathForRange(1, 2)
+
+ val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+ assertThat(diff, equalTo(Rect.zero))
+ }
+ }
+
+ @Test
+ fun testGetPathForRange_Emoji_Sequence() {
+ withDensity(defaultDensity) {
+ val text = "\u1F600\u1F603\u1F604\u1F606"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val expectedPath = Path()
+ val lineLeft = paragraph.getLineLeft(0)
+ val lineRight = paragraph.getLineRight(0)
+ expectedPath.addRect(
+ Rect(
+ lineLeft + fontSizeInPx,
+ 0f,
+ lineRight - fontSizeInPx,
+ fontSizeInPx
+ )
+ )
+
+ // Select "\u1F603\u1F604"
+ val actualPath = paragraph.getPathForRange(1, text.length - 1)
+
+ val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+ assertThat(diff, equalTo(Rect.zero))
+ }
+ }
+
+ @Test
+ fun testGetPathForRange_Unicode_200D_Return_Line_Segment() {
+ withDensity(defaultDensity) {
+ val text = "\u200D"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val expectedPath = Path()
+ val lineLeft = paragraph.getLineLeft(0)
+ val lineRight = paragraph.getLineRight(0)
+ expectedPath.addRect(Rect(lineLeft, 0f, lineRight, fontSizeInPx))
+
+ val actualPath = paragraph.getPathForRange(0, 1)
+
+ assertThat(lineLeft, equalTo(lineRight))
+ val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+ assertThat(diff, equalTo(Rect.zero))
+ }
+ }
+
+ @Test
+ fun testGetPathForRange_Unicode_2066_Return_Line_Segment() {
+ withDensity(defaultDensity) {
+ val text = "\u2066"
+ val fontSize = 20f.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val expectedPath = Path()
+ val lineLeft = paragraph.getLineLeft(0)
+ val lineRight = paragraph.getLineRight(0)
+ expectedPath.addRect(Rect(lineLeft, 0f, lineRight, fontSizeInPx))
+
+ val actualPath = paragraph.getPathForRange(0, 1)
+
+ assertThat(lineLeft, equalTo(lineRight))
+ val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+ assertThat(diff, equalTo(Rect.zero))
+ }
+ }
+
+ @Test
+ fun testGetWordBoundary() {
+ val text = "abc def"
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = 20.sp
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val result = paragraph.getWordBoundary(text.indexOf('a'))
+
+ assertThat(result.start, equalTo(text.indexOf('a')))
+ assertThat(result.end, equalTo(text.indexOf(' ')))
+ }
+
+ @Test
+ fun testGetWordBoundary_Bidi() {
+ val text = "abc \u05d0\u05d1\u05d2 def"
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontFamily = fontFamilyMeasureFont,
+ fontSize = 20.sp
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ val resultEnglish = paragraph.getWordBoundary(text.indexOf('a'))
+ val resultHebrew = paragraph.getWordBoundary(text.indexOf('\u05d1'))
+
+ assertThat(resultEnglish.start, equalTo(text.indexOf('a')))
+ assertThat(resultEnglish.end, equalTo(text.indexOf(' ')))
+ assertThat(resultHebrew.start, equalTo(text.indexOf('\u05d0')))
+ assertThat(resultHebrew.end, equalTo(text.indexOf('\u05d2') + 1))
+ }
+
+ @Test
+ fun width_default_value() {
+ val paragraph = simpleMultiParagraph()
+
+ assertThat(paragraph.width, equalTo(0.0f))
+ }
+
+ @Test
+ fun height_default_value() {
+ val paragraph = simpleMultiParagraph()
+
+ assertThat(paragraph.height, equalTo(0.0f))
+ }
+
+ @Test
+ fun minIntrinsicWidth_default_value() {
+ val paragraph = simpleMultiParagraph()
+
+ assertThat(paragraph.minIntrinsicWidth, equalTo(0.0f))
+ }
+
+ @Test
+ fun maxIntrinsicWidth_default_value() {
+ val paragraph = simpleMultiParagraph()
+
+ assertThat(paragraph.maxIntrinsicWidth, equalTo(0.0f))
+ }
+
+ @Test
+ fun alphabeticBaseline_default_value() {
+ val paragraph = simpleMultiParagraph()
+
+ assertThat(paragraph.baseline, equalTo(0.0f))
+ }
+
+ @Test
+ fun didExceedMaxLines_default_value() {
+ val paragraph = simpleMultiParagraph()
+
+ assertThat(paragraph.didExceedMaxLines, equalTo(false))
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun paint_throws_exception_if_layout_is_not_called() {
+ val paragraph = simpleMultiParagraph()
+
+ paragraph.paint(mock())
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun getPositionForOffset_throws_exception_if_layout_is_not_called() {
+ val paragraph = simpleMultiParagraph()
+
+ paragraph.getOffsetForPosition(PxPosition.Origin)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun getPathForRange_throws_exception_if_start_larger_than_end() {
+ val text = "ab"
+ val textStart = 0
+ val textEnd = text.length
+ val paragraph = simpleMultiParagraph(text = text)
+
+ paragraph.getPathForRange(textEnd, textStart)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun getPathForRange_throws_exception_if_start_is_smaller_than_zero() {
+ val text = "ab"
+ val textStart = 0
+ val textEnd = text.length
+ val paragraph = simpleMultiParagraph(text = text)
+
+ paragraph.getPathForRange(textStart - 2, textEnd - 1)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun getPathForRange_throws_exception_if_end_is_larger_than_text_length() {
+ val text = "ab"
+ val textStart = 0
+ val textEnd = text.length
+ val paragraph = simpleMultiParagraph(text = text)
+
+ paragraph.getPathForRange(textStart, textEnd + 1)
+ }
+
+ @Test
+ fun textAlign_defaultValue_alignsStart() {
+ withDensity(defaultDensity) {
+ val textLTR = "aa"
+ val textRTL = "\u05D0\u05D0"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+
+ val paragraphLTR = simpleMultiParagraph(
+ text = textLTR,
+ fontSize = fontSize
+ )
+ val layoutLTRWidth = (textLTR.length + 2) * fontSizeInPx
+ paragraphLTR.layout(ParagraphConstraints(width = layoutLTRWidth))
+
+ val paragraphRTL = simpleMultiParagraph(
+ text = textRTL,
+ fontSize = fontSize
+ )
+ val layoutRTLWidth = (textRTL.length + 2) * fontSizeInPx
+ paragraphRTL.layout(ParagraphConstraints(width = layoutRTLWidth))
+
+ // When textAlign is TextAlign.start, LTR aligns to left, RTL aligns to right.
+ assertThat(paragraphLTR.getLineLeft(0), equalTo(0.0f))
+ assertThat(paragraphRTL.getLineRight(0), equalTo(layoutRTLWidth))
+ }
+ }
+
+ @Test
+ fun textAlign_whenAlignLeft_returnsZeroForGetLineLeft() {
+ withDensity(defaultDensity) {
+ val texts = listOf("aa", "\u05D0\u05D0")
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+
+ texts.map { text ->
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textAlign = TextAlign.Left,
+ fontSize = fontSize
+ )
+ val layoutWidth = (text.length + 2) * fontSizeInPx
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+ assertThat(paragraph.getLineLeft(0), equalTo(0.0f))
+ }
+ }
+ }
+
+ @Test
+ fun textAlign_whenAlignRight_returnsLayoutWidthForGetLineRight() {
+ withDensity(defaultDensity) {
+ val texts = listOf("aa", "\u05D0\u05D0")
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+
+ texts.map { text ->
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textAlign = TextAlign.Right,
+ fontSize = fontSize
+ )
+ val layoutWidth = (text.length + 2) * fontSizeInPx
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+ assertThat(paragraph.getLineRight(0), equalTo(layoutWidth))
+ }
+ }
+ }
+
+ @Test
+ fun textAlign_whenAlignCenter_textIsCentered() {
+ withDensity(defaultDensity) {
+ val texts = listOf("aa", "\u05D0\u05D0")
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+
+ texts.map { text ->
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textAlign = TextAlign.Center,
+ fontSize = fontSize
+ )
+ val layoutWidth = (text.length + 2) * fontSizeInPx
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+ val textWidth = text.length * fontSizeInPx
+
+ assertThat(
+ paragraph.getLineLeft(0),
+ equalTo(layoutWidth / 2 - textWidth / 2)
+ )
+ assertThat(
+ paragraph.getLineRight(0),
+ equalTo(layoutWidth / 2 + textWidth / 2)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun textAlign_whenAlignStart_withLTR_returnsZeroForGetLineLeft() {
+ withDensity(defaultDensity) {
+ val text = "aa"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = (text.length + 2) * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textAlign = TextAlign.Start,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+ assertThat(paragraph.getLineLeft(0), equalTo(0.0f))
+ }
+ }
+
+ @Test
+ fun textAlign_whenAlignEnd_withLTR_returnsLayoutWidthForGetLineRight() {
+ withDensity(defaultDensity) {
+ val text = "aa"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = (text.length + 2) * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textAlign = TextAlign.End,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+ assertThat(paragraph.getLineRight(0), equalTo(layoutWidth))
+ }
+ }
+
+ @Test
+ fun textAlign_whenAlignStart_withRTL_returnsLayoutWidthForGetLineRight() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D0"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = (text.length + 2) * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textAlign = TextAlign.Start,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+ assertThat(paragraph.getLineRight(0), equalTo(layoutWidth))
+ }
+ }
+
+ @Test
+ fun textAlign_whenAlignEnd_withRTL_returnsZeroForGetLineLeft() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D0"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = (text.length + 2) * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textAlign = TextAlign.End,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+ assertThat(paragraph.getLineLeft(0), equalTo(0.0f))
+ }
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 28)
+ // We have to test justification above API 28 because of this bug b/68009059, where devices
+ // before API 28 may have an extra space at the end of line.
+ fun textAlign_whenAlignJustify_justifies() {
+ withDensity(defaultDensity) {
+ val text = "a a a"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = ("a a".length + 1) * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textAlign = TextAlign.Justify,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+ assertThat(paragraph.getLineLeft(0), equalTo(0.0f))
+ assertThat(paragraph.getLineRight(0), equalTo(layoutWidth))
+ // Last line should align start
+ assertThat(paragraph.getLineLeft(1), equalTo(0.0f))
+ }
+ }
+
+ @Test
+ fun textDirection_whenLTR_dotIsOnRight() {
+ withDensity(defaultDensity) {
+ val text = "a.."
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = text.length * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textDirection = TextDirection.Ltr,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+ // The position of the last character in display order.
+ val position = PxPosition(("a.".length * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+ val charIndex = paragraph.getOffsetForPosition(position)
+ assertThat(charIndex, equalTo(2))
+ }
+ }
+
+ @Test
+ fun textDirection_whenRTL_dotIsOnLeft() {
+ withDensity(defaultDensity) {
+ val text = "a.."
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = text.length * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textDirection = TextDirection.Rtl,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+ // The position of the first character in display order.
+ val position = PxPosition((fontSizeInPx / 2 + 1).px, (fontSizeInPx / 2).px)
+ val charIndex = paragraph.getOffsetForPosition(position)
+ assertThat(charIndex, equalTo(2))
+ }
+ }
+
+ @Test
+ fun textDirection_whenDefault_withoutStrongChar_directionIsLTR() {
+ withDensity(defaultDensity) {
+ val text = "..."
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = text.length * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+ for (i in 0..text.length) {
+ // The position of the i-th character in display order.
+ val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+ val charIndex = paragraph.getOffsetForPosition(position)
+ assertThat(charIndex, equalTo(i))
+ }
+ }
+ }
+
+ @Test
+ fun textDirection_whenDefault_withFirstStrongCharLTR_directionIsLTR() {
+ withDensity(defaultDensity) {
+ val text = "a\u05D0."
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = text.length * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+ for (i in 0 until text.length) {
+ // The position of the i-th character in display order.
+ val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+ val charIndex = paragraph.getOffsetForPosition(position)
+ assertThat(charIndex, equalTo(i))
+ }
+ }
+ }
+
+ @Test
+ fun textDirection_whenDefault_withFirstStrongCharRTL_directionIsRTL() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0a."
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = text.length * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+ // The first character in display order should be '.'
+ val position = PxPosition((fontSizeInPx / 2 + 1).px, (fontSizeInPx / 2).px)
+ val index = paragraph.getOffsetForPosition(position)
+ assertThat(index, equalTo(2))
+ }
+ }
+
+ @Test
+ fun lineHeight_returnsSameAsGiven() {
+ withDensity(defaultDensity) {
+ val text = "abcdefgh"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ // Make the layout 4 lines
+ val layoutWidth = text.length * fontSizeInPx / 4
+ val lineHeight = 1.5f
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ lineHeight = lineHeight
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+ assertThat(paragraph.lineCount, equalTo(4))
+ // TODO(Migration/haoyuchang): Due to bug b/120530738, the height of the first line is
+ // wrong in the framework. Will fix it when the lineHeight in TextSpan is implemented.
+ for (i in 1 until paragraph.lineCount - 1) {
+ val actualHeight = paragraph.getLineHeight(i)
+ // In the sample_font.ttf, the height of the line should be
+ // fontSize + 0.2f * fontSize(line gap)
+ assertThat(
+ "line number $i",
+ actualHeight,
+ equalTo(1.2f * fontSizeInPx * lineHeight)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun lineHeight_hasNoEffectOnLastLine() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val layoutWidth = (text.length - 1) * fontSizeInPx
+ val lineHeight = 1.5f
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ fontSize = fontSize,
+ lineHeight = lineHeight
+ )
+ paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+ val lastLine = paragraph.lineCount - 1
+ // In the sample_font.ttf, the height of the line should be
+ // fontSize + 0.2 * fontSize(line gap)
+ assertThat(paragraph.getLineHeight(lastLine), equalTo(1.2f * fontSizeInPx))
+ }
+ }
+
+ @Test
+ fun textIndent_onSingleLine() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val indent = 20.0f
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textIndent = TextIndent(firstLine = indent.px),
+ fontSize = fontSize,
+ fontFamily = fontFamilyMeasureFont
+ )
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ // This position should point to the first character 'a' if indent is applied.
+ // Otherwise this position will point to the second character 'b'.
+ val position = PxPosition((indent + 1).px, (fontSizeInPx / 2).px)
+ // The offset corresponding to the position should be the first char 'a'.
+ assertThat(paragraph.getOffsetForPosition(position), equalTo(0))
+ }
+ }
+
+ @Test
+ fun textIndent_onFirstLine() {
+ withDensity(defaultDensity) {
+ val text = "abcdef"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val indent = 20.0f
+ val paragraphWidth = "abcd".length * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textIndent = TextIndent(firstLine = indent.px),
+ fontSize = fontSize,
+ fontFamily = fontFamilyMeasureFont
+ )
+ paragraph.layout(ParagraphConstraints(width = paragraphWidth))
+
+ assertThat(paragraph.lineCount, equalTo(2))
+ // This position should point to the first character of the first line if indent is
+ // applied. Otherwise this position will point to the second character of the second line.
+ val position = PxPosition((indent + 1).px, (fontSizeInPx / 2).px)
+ // The offset corresponding to the position should be the first char 'a'.
+ assertThat(paragraph.getOffsetForPosition(position), equalTo(0))
+ }
+ }
+
+ @Test
+ fun textIndent_onRestLine() {
+ withDensity(defaultDensity) {
+ val text = "abcde"
+ val fontSize = 20.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val indent = 20.0f
+ val paragraphWidth = "abc".length * fontSizeInPx
+
+ val paragraph = simpleMultiParagraph(
+ text = text,
+ textIndent = TextIndent(
+ firstLine = 0.px,
+ restLine = indent.px
+ ),
+ fontSize = fontSize,
+ fontFamily = fontFamilyMeasureFont
+ )
+ paragraph.layout(ParagraphConstraints(width = paragraphWidth))
+
+ // This position should point to the first character of the second line if indent is
+ // applied. Otherwise this position will point to the second character of the second line.
+ val position = PxPosition((indent + 1).px, (fontSizeInPx / 2 + fontSizeInPx).px)
+ // The offset corresponding to the position should be the 'd' in the second line.
+ assertThat(
+ paragraph.getOffsetForPosition(position),
+ equalTo("abcd".length - 1)
+ )
+ }
+ }
+
+ private fun simpleMultiParagraph(
+ text: String = "",
+ textIndent: TextIndent? = null,
+ textAlign: TextAlign? = null,
+ textDirection: TextDirection? = null,
+ fontSize: Sp? = null,
+ maxLines: Int? = null,
+ lineHeight: Float? = null,
+ textStyles: List<AnnotatedString.Item<TextStyle>> = listOf(),
+ paragraphStyles: List<AnnotatedString.Item<ParagraphStyle>> = listOf(),
+ fontFamily: FontFamily = fontFamilyMeasureFont,
+ locale: Locale? = null,
+ textStyle: TextStyle? = null,
+ density: Density? = null
+ ): MultiParagraph {
+ return MultiParagraph(
+ annotatedString = AnnotatedString(
+ text = text,
+ textStyles = textStyles,
+ paragraphStyles = paragraphStyles
+ ),
+ textStyle = TextStyle(
+ fontFamily = fontFamily,
+ fontSize = fontSize,
+ locale = locale
+ ).merge(textStyle),
+ paragraphStyle = ParagraphStyle(
+ textIndent = textIndent,
+ textAlign = textAlign,
+ textDirection = textDirection,
+ lineHeight = lineHeight
+ ),
+ maxLines = maxLines,
+ density = density ?: defaultDensity,
+ resourceLoader = TestFontResourceLoader(context)
+ )
+ }
+}
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt
index 841eb1b..9718437 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt
@@ -62,6 +62,8 @@
private val resourceLoader = TestFontResourceLoader(context)
+ private val cursorWidth = 4f
+
@Test
fun empty_string() {
withDensity(defaultDensity) {
@@ -430,6 +432,566 @@
}
}
+ @Test(expected = java.lang.AssertionError::class)
+ fun getCursorRect_larger_than_length_throw_exception() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ paragraph.getCursorRect(text.length + 1)
+ }
+ }
+
+ @Test(expected = java.lang.AssertionError::class)
+ fun getCursorRect_negative_throw_exception() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ paragraph.getCursorRect(-1)
+ }
+ }
+
+ @Test
+ fun getCursorRect_ltr_singleLine() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ for (i in 0 until text.length) {
+ val cursorRect = paragraph.getCursorRect(i)
+ val cursorXOffset = i * fontSizeInPx
+ assertThat(
+ cursorRect,
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = 0f,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getCursorRect_ltr_multiLines() {
+ withDensity(defaultDensity) {
+ val text = "abcdef"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+ val charsPerLine = 3
+
+ paragraph.layout(ParagraphConstraints(width = charsPerLine * fontSizeInPx))
+
+ for (i in 0 until charsPerLine) {
+ val cursorXOffset = i * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = 0f,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+ }
+
+ for (i in charsPerLine until text.length) {
+ val cursorXOffset = (i % charsPerLine) * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = fontSizeInPx,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx * 2.2f
+ ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getCursorRect_ltr_newLine() {
+ withDensity(defaultDensity) {
+ val text = "abc\ndef"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ // Cursor before '\n'
+ assertThat(
+ paragraph.getCursorRect(3),
+ equalTo(Rect(
+ left = 3 * fontSizeInPx - cursorWidth / 2,
+ top = 0f,
+ right = 3 * fontSizeInPx + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+
+ // Cursor after '\n'
+ assertThat(
+ paragraph.getCursorRect(4),
+ equalTo(Rect(
+ left = -cursorWidth / 2,
+ top = fontSizeInPx,
+ right = cursorWidth / 2,
+ bottom = fontSizeInPx * 2.2f
+ ))
+ )
+ }
+ }
+
+ @Test
+ fun getCursorRect_ltr_newLine_last_char() {
+ withDensity(defaultDensity) {
+ val text = "abc\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+ // Cursor before '\n'
+ assertThat(
+ paragraph.getCursorRect(3),
+ equalTo(Rect(
+ left = 3 * fontSizeInPx - cursorWidth / 2,
+ top = 0f,
+ right = 3 * fontSizeInPx + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+
+ // Cursor after '\n'
+ assertThat(
+ paragraph.getCursorRect(4),
+ equalTo(Rect(
+ left = -cursorWidth / 2,
+ top = fontSizeInPx,
+ right = cursorWidth / 2,
+ bottom = fontSizeInPx * 2.2f
+ ))
+ )
+ }
+ }
+
+ @Test
+ fun getCursorRect_rtl_singleLine() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ for (i in 0 until text.length) {
+ val cursorXOffset = (text.length - i) * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = 0f,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getCursorRect_rtl_multiLines() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+ val charsPerLine = 3
+
+ paragraph.layout(ParagraphConstraints(width = charsPerLine * fontSizeInPx))
+
+ for (i in 0 until charsPerLine) {
+ val cursorXOffset = (charsPerLine - i) * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = 0f,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+ }
+
+ for (i in charsPerLine until text.length) {
+ val cursorXOffset = (charsPerLine - i % charsPerLine) * fontSizeInPx
+ assertThat(
+ paragraph.getCursorRect(i),
+ equalTo(Rect(
+ left = cursorXOffset - cursorWidth / 2,
+ top = fontSizeInPx,
+ right = cursorXOffset + cursorWidth / 2,
+ bottom = fontSizeInPx * 2.2f
+ ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getCursorRect_rtl_newLine() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2\n\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = 3 * fontSizeInPx))
+
+ // Cursor before '\n'
+ assertThat(
+ paragraph.getCursorRect(3),
+ equalTo(Rect(
+ left = 0 - cursorWidth / 2,
+ top = 0f,
+ right = 0 + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+
+ // Cursor after '\n'
+ assertThat(
+ paragraph.getCursorRect(4),
+ equalTo(Rect(
+ left = 3 * fontSizeInPx - cursorWidth / 2,
+ top = fontSizeInPx,
+ right = 3 * fontSizeInPx + cursorWidth / 2,
+ bottom = fontSizeInPx * 2.2f
+ ))
+ )
+ }
+ }
+
+ @Test
+ fun getCursorRect_rtl_newLine_last_char() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = 3 * fontSizeInPx))
+
+ // Cursor before '\n'
+ assertThat(
+ paragraph.getCursorRect(3),
+ equalTo(Rect(
+ left = 0 - cursorWidth / 2,
+ top = 0f,
+ right = 0 + cursorWidth / 2,
+ bottom = fontSizeInPx
+ ))
+ )
+
+ // Cursor after '\n'
+ assertThat(
+ paragraph.getCursorRect(4),
+ equalTo(Rect(
+ left = -cursorWidth / 2,
+ top = fontSizeInPx,
+ right = +cursorWidth / 2,
+ bottom = fontSizeInPx * 2.2f
+ ))
+ )
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_ltr_singleLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+ paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+ for (i in 0..text.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(fontSizeInPx * i)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_rtl_singleLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ for (i in 0..text.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(width - fontSizeInPx * i)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_bidi_singleLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val ltrText = "abc"
+ val rtlText = "\u05D0\u05D1\u05D2"
+ val text = ltrText + rtlText
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ for (i in 0..ltrText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(fontSizeInPx * i)
+ )
+ }
+
+ for (i in 1 until rtlText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i + ltrText.length),
+ equalTo(width - fontSizeInPx * i)
+ )
+ }
+
+ assertThat(
+ paragraph.getPrimaryHorizontal(text.length),
+ equalTo(width)
+ )
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_ltr_singleLine_textDirectionRtl() {
+ withDensity(defaultDensity) {
+ val text = "abc"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Rtl
+ )
+ val width = text.length * fontSizeInPx
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(0), equalTo(width))
+
+ for (i in 1 until text.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(fontSizeInPx * i)
+ )
+ }
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_rtl_singleLine_textDirectionLtr() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Ltr
+ )
+ val width = text.length * fontSizeInPx
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(0), equalTo(0f))
+
+ for (i in 1 until text.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(width - fontSizeInPx * i)
+ )
+ }
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(width))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_bidi_singleLine_textDirectionLtr() {
+ withDensity(defaultDensity) {
+ val ltrText = "abc"
+ val rtlText = "\u05D0\u05D1\u05D2"
+ val text = ltrText + rtlText
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Ltr
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ for (i in 0..ltrText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(fontSizeInPx * i)
+ )
+ }
+
+ for (i in 1 until rtlText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i + ltrText.length),
+ equalTo(width - fontSizeInPx * i)
+ )
+ }
+
+ assertThat(
+ paragraph.getPrimaryHorizontal(text.length),
+ equalTo(width)
+ )
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_bidi_singleLine_textDirectionRtl() {
+ withDensity(defaultDensity) {
+ val ltrText = "abc"
+ val rtlText = "\u05D0\u05D1\u05D2"
+ val text = ltrText + rtlText
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Rtl
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(0), equalTo(width))
+ for (i in 1 until ltrText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i),
+ equalTo(rtlText.length * fontSizeInPx + i * fontSizeInPx)
+ )
+ }
+
+ for (i in 0..rtlText.length) {
+ assertThat(
+ paragraph.getPrimaryHorizontal(i + ltrText.length),
+ equalTo(rtlText.length * fontSizeInPx - i * fontSizeInPx)
+ )
+ }
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_ltr_newLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val text = "abc\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_rtl_newLine_textDirectionDefault() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_ltr_newLine_textDirectionRtl() {
+ withDensity(defaultDensity) {
+ val text = "abc\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Rtl
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(width))
+ }
+ }
+
+ @Test
+ fun getPrimaryHorizontal_rtl_newLine_textDirectionLtr() {
+ withDensity(defaultDensity) {
+ val text = "\u05D0\u05D1\u05D2\n"
+ val fontSize = 50.sp
+ val fontSizeInPx = fontSize.toPx().value
+ val paragraph = simpleParagraph(
+ text = text,
+ fontSize = fontSize,
+ textDirection = TextDirection.Ltr
+ )
+ val width = text.length * fontSizeInPx
+
+ paragraph.layout(ParagraphConstraints(width))
+
+ assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+ }
+ }
+
@Test
fun locale_withCJK_shouldNotDrawSame() {
withDensity(defaultDensity) {
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/TextPainterIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/TextPainterIntegrationTest.kt
index 41f1cf3..3b17aae 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/TextPainterIntegrationTest.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/TextPainterIntegrationTest.kt
@@ -26,7 +26,6 @@
import androidx.ui.core.px
import androidx.ui.core.sp
import androidx.ui.core.withDensity
-import androidx.ui.engine.geometry.Offset
import androidx.ui.engine.geometry.Rect
import androidx.ui.engine.geometry.Size
import androidx.ui.text.FontTestData.Companion.BASIC_MEASURE_FONT
@@ -291,7 +290,7 @@
textPainter.layout(Constraints(0.ipx, 20.ipx))
- assertThat(textPainter.paragraph).isNotNull()
+ assertThat(textPainter.multiParagraph).isNotNull()
}
@Test
@@ -460,10 +459,10 @@
val defaultSelectionColor = Color(0x6633B5E5)
expectedPaint.color = defaultSelectionColor
- val firstLineLeft = textPainter.paragraph?.getLineLeft(0)
- val secondLineLeft = textPainter.paragraph?.getLineLeft(1)
- val firstLineRight = textPainter.paragraph?.getLineRight(0)
- val secondLineRight = textPainter.paragraph?.getLineRight(1)
+ val firstLineLeft = textPainter.multiParagraph?.getLineLeft(0)
+ val secondLineLeft = textPainter.multiParagraph?.getLineLeft(1)
+ val firstLineRight = textPainter.multiParagraph?.getLineRight(0)
+ val secondLineRight = textPainter.multiParagraph?.getLineRight(1)
expectedCanvas.drawRect(
Rect(firstLineLeft!!, 0f, firstLineRight!!, fontSizeInPx),
expectedPaint
@@ -473,7 +472,7 @@
secondLineLeft!!,
fontSizeInPx,
secondLineRight!!,
- textPainter.paragraph!!.height
+ textPainter.multiParagraph!!.height
),
expectedPaint
)
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/AnnotatedString.kt b/ui/ui-text/src/main/java/androidx/ui/text/AnnotatedString.kt
index 97fda37..28140c5 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/AnnotatedString.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/AnnotatedString.kt
@@ -21,8 +21,23 @@
*/
data class AnnotatedString(
val text: String,
- val textStyles: List<Item<TextStyle>> = listOf()
+ val textStyles: List<Item<TextStyle>> = listOf(),
+ val paragraphStyles: List<Item<ParagraphStyle>> = listOf()
) {
+
+ init {
+ var lastStyleEnd = -1
+ for (paragraphStyle in paragraphStyles) {
+ if (paragraphStyle.start < lastStyleEnd) {
+ throw IllegalArgumentException("ParagraphStyle should not overlap")
+ }
+ if (paragraphStyle.end > text.length) {
+ throw IllegalArgumentException("ParagraphStyle range " +
+ "[${paragraphStyle.start}, ${paragraphStyle.end}) is out of boundary")
+ }
+ lastStyleEnd = paragraphStyle.end
+ }
+ }
/**
* The information attached on the text such as a TextStyle.
*
@@ -31,5 +46,11 @@
* @param end The end of the range where [style] takes effect. It's exclusive.
*/
// TODO(haoyuchang): Check some other naming options.
- data class Item<T>(val style: T, val start: Int, val end: Int)
+ data class Item<T>(val style: T, val start: Int, val end: Int) {
+ init {
+ if (start > end) {
+ throw IllegalArgumentException("Reversed range is not supported")
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/MultiParagraph.kt b/ui/ui-text/src/main/java/androidx/ui/text/MultiParagraph.kt
new file mode 100644
index 0000000..830a776
--- /dev/null
+++ b/ui/ui-text/src/main/java/androidx/ui/text/MultiParagraph.kt
@@ -0,0 +1,634 @@
+/*
+ * Copyright 2019 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.ui.text
+
+import androidx.ui.core.Density
+import androidx.ui.core.Px
+import androidx.ui.core.PxPosition
+import androidx.ui.core.px
+import androidx.ui.engine.geometry.Offset
+import androidx.ui.engine.geometry.Rect
+import androidx.ui.painting.Canvas
+import androidx.ui.painting.Path
+import androidx.ui.text.font.Font
+import java.lang.IllegalStateException
+import kotlin.math.max
+
+/**
+ * The class that renders multiple paragraphs at once.
+ *
+ * It's designed to support multiple [ParagraphStyle]s in single text widget.
+ */
+internal class MultiParagraph(
+ val annotatedString: AnnotatedString,
+ private val textStyle: TextStyle = TextStyle(),
+ private val paragraphStyle: ParagraphStyle = ParagraphStyle(),
+ val maxLines: Int? = null,
+ val ellipsis: Boolean? = null,
+ val density: Density,
+ val resourceLoader: Font.ResourceLoader
+) {
+ /**
+ * The minimum width that this paragraph could be without failing to paint
+ * its contents within itself.
+ *
+ * Valid only after [layout] has been called.
+ */
+ val minIntrinsicWidth: Float
+ get() = paragraphInfoList.foldRight(0f) { paragraphInfo, minWidth ->
+ max(paragraphInfo.paragraph.minIntrinsicWidth, minWidth)
+ }
+
+ /**
+ * Returns the smallest width beyond which increasing the width never
+ * decreases the height.
+ *
+ * Valid only after [layout] has been called.
+ */
+ val maxIntrinsicWidth: Float
+ get() = paragraphInfoList.foldRight(0f) { paragraphInfo, maxWidth ->
+ max(paragraphInfo.paragraph.maxIntrinsicWidth, maxWidth)
+ }
+
+ /**
+ * True if there is more vertical content, but the text was truncated, either
+ * because we reached `maxLines` lines of text or because the `maxLines` was
+ * null, `ellipsis` was not null, and one of the lines exceeded the width
+ * constraint.
+ *
+ * See the discussion of the `maxLines` and `ellipsis` arguments at [ParagraphStyle].
+ */
+ var didExceedMaxLines: Boolean = false
+ private set
+
+ /**
+ * The amount of horizontal space this paragraph occupies.
+ *
+ * Valid only after [layout] has been called.
+ */
+ var width: Float = 0f
+ private set
+
+ /**
+ * The amount of vertical space this paragraph occupies.
+ *
+ * Valid only after [layout] has been called.
+ */
+ var height: Float = 0f
+ private set
+
+ /**
+ * The distance from the top of the paragraph to the alphabetic
+ * baseline of the first line, in logical pixels.
+ */
+ val baseline: Float
+ get() = if (paragraphInfoList.isEmpty()) 0f else paragraphInfoList[0].paragraph.baseline
+
+ /** The total number of lines in the text. */
+ var lineCount: Int = 0
+ private set
+
+ private var needLayout = true
+
+ private val paragraphInfoList: List<ParagraphInfo>
+
+ init {
+ val paragraphStyles = fillInParagraphRanges(annotatedString, this.paragraphStyle)
+ this.paragraphInfoList = paragraphStyles.map { (style, start, end) ->
+ // TODO(haoyuchang): Change substring to Paragraph receiving text and range.
+ val textInParagraph = if (start != end) {
+ annotatedString.text.substring(start, end)
+ } else {
+ ""
+ }
+ val textStylesInParagraph = annotatedString.getLocalStyles(start, end)
+
+ // TODO(haoyuchang): remove the top and bottom padding between two paragraphs
+ val paragraph = Paragraph(
+ textInParagraph,
+ this.textStyle,
+ style,
+ textStylesInParagraph,
+ maxLines,
+ ellipsis,
+ density,
+ resourceLoader
+ )
+
+ ParagraphInfo(
+ paragraph = paragraph,
+ startIndex = start,
+ endIndex = end
+ )
+ }
+ }
+
+ /**
+ * Computes the size and position of each glyph in the paragraph.
+ *
+ * The [ParagraphConstraints] control how wide the text is allowed to be.
+ */
+ fun layout(constraints: ParagraphConstraints) {
+ this.needLayout = false
+ this.width = constraints.width
+ this.didExceedMaxLines = false
+
+ var currentLineCount = 0
+ var currentHeight = 0f
+
+ for ((index, paragraphInfo) in paragraphInfoList.withIndex()) {
+ val paragraph = paragraphInfo.paragraph
+ paragraph.layout(constraints)
+
+ paragraphInfo.startLineIndex = currentLineCount
+ paragraphInfo.endLineIndex = currentLineCount + paragraph.lineCount
+ currentLineCount = paragraphInfo.endLineIndex
+
+ paragraphInfo.top = currentHeight.px
+ paragraphInfo.bottom = (currentHeight + paragraph.height).px
+ currentHeight += paragraph.height
+
+ // TODO(haoyuchang): solve the corner case where the ellipsis won't be applied when
+ // currentLineNum == maxLines but there are still more paragraphs
+ if (paragraph.didExceedMaxLines ||
+ (currentLineCount == maxLines && index != this.paragraphInfoList.lastIndex)
+ ) {
+ this.didExceedMaxLines = true
+ break
+ }
+ }
+ this.lineCount = currentLineCount
+ this.height = currentHeight
+ }
+
+ /** Paint the paragraphs to canvas. */
+ fun paint(canvas: Canvas) {
+ assertNeedLayout()
+
+ canvas.save()
+ paragraphInfoList.forEach {
+ it.paragraph.paint(canvas)
+ canvas.translate(0f, it.paragraph.height)
+ }
+ canvas.restore()
+ }
+
+ /** Returns path that enclose the given text range. */
+ fun getPathForRange(start: Int, end: Int): Path {
+ if (start !in 0..end || end > annotatedString.text.length) {
+ throw AssertionError(
+ "Start($start) or End($end) is out of range [0..${annotatedString.text.length})," +
+ " or start > end!"
+ )
+ }
+ assertNeedLayout()
+
+ if (start == end) return Path()
+
+ val paragraphIndex = findParagraphByIndex(paragraphInfoList, start)
+ val path = Path()
+
+ paragraphInfoList.drop(paragraphIndex)
+ .takeWhile { it.startIndex < end }
+ .filterNot { it.startIndex == it.endIndex }
+ .forEach {
+ with(it) {
+ path.addPath(
+ path = paragraph.getPathForRange(
+ start = start.toLocalIndex(),
+ end = end.toLocalIndex()
+ ).toGlobal()
+ )
+ }
+ }
+ return path
+ }
+
+ /** Returns the character offset closest to the given graphical position. */
+ fun getOffsetForPosition(position: PxPosition): Int {
+ assertNeedLayout()
+ val paragraphIndex = when {
+ position.y.value <= 0f -> 0
+ position.y.value >= height -> paragraphInfoList.lastIndex
+ else -> findParagraphByY(paragraphInfoList, position.y)
+ }
+ return with(paragraphInfoList[paragraphIndex]) {
+ if (length == 0) {
+ max(0, startIndex - 1)
+ } else {
+ paragraph.getOffsetForPosition(position.toLocal()).toGlobalIndex()
+ }
+ }
+ }
+
+ /**
+ * Returns the bounding box as Rect of the character for given character offset. Rect
+ * includes the top, bottom, left and right of a character.
+ */
+ fun getBoundingBox(offset: Int): Rect {
+ assertNeedLayout()
+ assertIndexInRange(offset)
+
+ val paragraphIndex = findParagraphByIndex(paragraphInfoList, offset)
+ return with(paragraphInfoList[paragraphIndex]) {
+ val localOffset = offset.toLocalIndex()
+ paragraph.getBoundingBox(localOffset).toGlobal()
+ }
+ }
+
+ /** Get the primary horizontal position for the specified text offset. */
+ fun getPrimaryHorizontal(offset: Int): Float {
+ assertNeedLayout()
+ if (offset !in 0..annotatedString.text.length) {
+ throw AssertionError("offset($offset) is out of bounds " +
+ "(0,${annotatedString.text.length}")
+ }
+
+ val paragraphIndex = if (offset == annotatedString.text.length) {
+ paragraphInfoList.lastIndex
+ } else {
+ findParagraphByIndex(paragraphInfoList, offset)
+ }
+
+ return with(paragraphInfoList[paragraphIndex]) {
+ paragraph.getPrimaryHorizontal(offset.toLocalIndex())
+ }
+ }
+
+ /**
+ * Returns the TextRange of the word at the given character offset. Characters not
+ * part of a word, such as spaces, symbols, and punctuation, have word breaks
+ * on both sides. In such cases, this method will return TextRange(offset, offset+1).
+ * Word boundaries are defined more precisely in Unicode Standard Annex #29
+ * http://www.unicode.org/reports/tr29/#Word_Boundaries
+ */
+ fun getWordBoundary(offset: Int): TextRange {
+ assertNeedLayout()
+ assertIndexInRange(offset)
+
+ val paragraphIndex = findParagraphByIndex(paragraphInfoList, offset)
+
+ return with(paragraphInfoList[paragraphIndex]) {
+ paragraph.getWordBoundary(offset.toLocalIndex()).toGlobal()
+ }
+ }
+
+ /** Returns rectangle of the cursor area. */
+ fun getCursorRect(offset: Int): Rect {
+ assertNeedLayout()
+ if (offset !in 0..annotatedString.text.length) {
+ throw AssertionError("offset($offset) is out of bounds " +
+ "(0,${annotatedString.text.length}")
+ }
+
+ val paragraphIndex = if (offset == annotatedString.text.length) {
+ paragraphInfoList.lastIndex
+ } else {
+ findParagraphByIndex(paragraphInfoList, offset)
+ }
+
+ return with(paragraphInfoList[paragraphIndex]) {
+ paragraph.getCursorRect(offset.toLocalIndex()).toGlobal()
+ }
+ }
+
+ /**
+ * Returns the line number on which the specified text offset appears.
+ * If you ask for a position before 0, you get 0; if you ask for a position
+ * beyond the end of the text, you get the last line.
+ */
+ fun getLineForOffset(offset: Int): Int {
+ assertNeedLayout()
+ assertIndexInRange(offset)
+
+ val paragraphIndex = findParagraphByIndex(paragraphInfoList, offset)
+ return with(paragraphInfoList[paragraphIndex]) {
+ paragraph.getLineForOffset(offset.toLocalIndex()).toGlobalLineIndex()
+ }
+ }
+
+ /** Returns the left x Coordinate of the given line. */
+ fun getLineLeft(lineIndex: Int): Float {
+ assertNeedLayout()
+ assertLineIndexInRange(lineIndex)
+
+ val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+ return with(paragraphInfoList[paragraphIndex]) {
+ paragraph.getLineLeft(lineIndex.toLocalLineIndex())
+ }
+ }
+
+ /** Returns the right x Coordinate of the given line. */
+ fun getLineRight(lineIndex: Int): Float {
+ assertNeedLayout()
+ assertLineIndexInRange(lineIndex)
+
+ val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+ return with(paragraphInfoList[paragraphIndex]) {
+ paragraph.getLineRight(lineIndex.toLocalLineIndex())
+ }
+ }
+
+ /** Returns the bottom y coordinate of the given line. */
+ fun getLineBottom(lineIndex: Int): Float {
+ assertNeedLayout()
+ assertLineIndexInRange(lineIndex)
+
+ val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+ return with(paragraphInfoList[paragraphIndex]) {
+ paragraph.getLineBottom(lineIndex.toLocalLineIndex())
+ }
+ }
+
+ /** Returns the height of the given line. */
+ fun getLineHeight(lineIndex: Int): Float {
+ assertNeedLayout()
+ assertLineIndexInRange(lineIndex)
+
+ val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+ return with(paragraphInfoList[paragraphIndex]) {
+ paragraph.getLineHeight(lineIndex.toLocalLineIndex())
+ }
+ }
+
+ /** Returns the width of the given line. */
+ fun getLineWidth(lineIndex: Int): Float {
+ assertNeedLayout()
+ assertLineIndexInRange(lineIndex)
+
+ val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+ return with(paragraphInfoList[paragraphIndex]) {
+ paragraph.getLineWidth(lineIndex.toLocalLineIndex())
+ }
+ }
+
+ private fun assertNeedLayout() {
+ if (needLayout) {
+ throw IllegalStateException("")
+ }
+ }
+
+ private fun assertIndexInRange(offset: Int) {
+ if (offset !in (0 until annotatedString.text.length)) {
+ throw IndexOutOfBoundsException("offset($offset) is out of bounds" +
+ " [0, ${annotatedString.text.length})")
+ }
+ }
+
+ private fun assertLineIndexInRange(lineIndex: Int) {
+ if (lineIndex !in (0 until lineCount)) {
+ throw IndexOutOfBoundsException("lineIndex($lineIndex) is out of bounds" +
+ " [0, $lineIndex)")
+ }
+ }
+}
+
+/**
+ * Given an character index of [MultiParagraph.annotatedString], find the corresponding
+ * [ParagraphInfo] which covers the provided index.
+ *
+ * @param paragraphInfoList The list of [ParagraphInfo] containing the information of each
+ * paragraph in the [MultiParagraph].
+ * @param index The target index in the [MultiParagraph]. It should be in the range of
+ * [0, text.length)
+ * @return The index of the target [ParagraphInfo] in [paragraphInfoList].
+ */
+private fun findParagraphByIndex(paragraphInfoList: List<ParagraphInfo>, index: Int): Int {
+ return paragraphInfoList.binarySearch { paragraphInfo ->
+ when {
+ paragraphInfo.startIndex > index -> 1
+ paragraphInfo.endIndex <= index -> -1
+ else -> 0
+ }
+ }
+}
+
+/**
+ * Given the y graphical position relative to this [MultiParagraph], find the index of the
+ * corresponding [ParagraphInfo] which occupies the provided position.
+ *
+ * @param paragraphInfoList The list of [ParagraphInfo] containing the information of each
+ * paragraph in the [MultiParagraph].
+ * @param y The y coordinate position relative to the [MultiParagraph]. It should be in the range
+ * of [0, [MultiParagraph.height]].
+ * @return The index of the target [ParagraphInfo] in [paragraphInfoList].
+ */
+private fun findParagraphByY(paragraphInfoList: List<ParagraphInfo>, y: Px): Int {
+ return paragraphInfoList.binarySearch { paragraphInfo ->
+ when {
+ paragraphInfo.top > y -> 1
+ paragraphInfo.bottom <= y -> -1
+ else -> 0
+ }
+ }
+}
+
+/**
+ * Given an line index in [MultiParagraph], find the corresponding [ParagraphInfo] which
+ * covers the provided line index.
+ *
+ * @param paragraphInfoList The list of [ParagraphInfo] containing the information of each
+ * paragraph in the [MultiParagraph].
+ * @param lineIndex The target line index in the [MultiParagraph], it should be in the range of
+ * [0, [MultiParagraph.lineCount])
+ * @return The index of the target [ParagraphInfo] in [paragraphInfoList].
+ */
+private fun findParagraphByLineIndex(paragraphInfoList: List<ParagraphInfo>, lineIndex: Int): Int {
+ return paragraphInfoList.binarySearch { paragraphInfo ->
+ when {
+ paragraphInfo.startLineIndex > lineIndex -> 1
+ paragraphInfo.endLineIndex <= lineIndex -> -1
+ else -> 0
+ }
+ }
+}
+
+/**
+ * A helper function used to determine the paragraph boundaries in [MultiParagraph].
+ *
+ * It reads paragraph information from [AnnotatedString.paragraphStyles] where only some parts of
+ * text has [ParagraphStyle] specified, and unspecified parts(gaps between specified paragraphs)
+ * are considered as default paragraph with default [ParagraphStyle].
+ * For example, the following string with a specified paragraph denoted by "[]"
+ * "Hello WorldHi!"
+ * [ ]
+ * The result paragraphs are "Hello World" and "Hi!".
+ *
+ * @param annotatedString: The [AnnotatedString] on which the paragraph boundaries need to be
+ * determined.
+ * @param defaultParagraphStyle The default [ParagraphStyle]. It's used for both unspecified
+ * default paragraphs and specified paragraph. When a specified paragraph's [ParagraphStyle] has
+ * a null attribute, the default one will be used instead.
+ */
+internal fun fillInParagraphRanges(
+ annotatedString: AnnotatedString,
+ defaultParagraphStyle: ParagraphStyle
+): List<AnnotatedString.Item<ParagraphStyle>> {
+ val length = annotatedString.text.length
+ val paragraphStyles = annotatedString.paragraphStyles
+
+ var lastOffset = 0
+ val result = mutableListOf<AnnotatedString.Item<ParagraphStyle>>()
+ for ((style, start, end) in paragraphStyles) {
+ if (start != lastOffset) {
+ result.add(AnnotatedString.Item(defaultParagraphStyle, lastOffset, start))
+ }
+ result.add(AnnotatedString.Item(defaultParagraphStyle.merge(style), start, end))
+ lastOffset = end
+ }
+ if (lastOffset != length) {
+ result.add(AnnotatedString.Item(defaultParagraphStyle, lastOffset, length))
+ }
+ // This is a corner case where annotatedString is an empty string without any ParagraphStyle.
+ // In this case, a dummy ParagraphStyle is created.
+ if (result.isEmpty()) {
+ result.add(AnnotatedString.Item(defaultParagraphStyle, 0, 0))
+ }
+ return result
+}
+
+/**
+ * Helper function used to find the [TextStyle]s in the given paragraph range and also convert the
+ * range of those [TextStyle]s to paragraph local range.
+ *
+ * @param start The start index of the paragraph range, inclusive.
+ * @param end The end index of the paragraph range, exclusive.
+ * @return The list of converted [TextStyle]s in the given paragraph range.
+ */
+private fun AnnotatedString.getLocalStyles(
+ start: Int,
+ end: Int
+): List<AnnotatedString.Item<TextStyle>> {
+ if (start == end) {
+ return listOf()
+ }
+ // If the given range covers the whole AnnotatedString, return textStyles without conversion.
+ if (start == 0 && end >= this.text.length) {
+ return textStyles
+ }
+ return textStyles.filter { it.start < end && it.end > start }
+ .map {
+ AnnotatedString.Item(
+ it.style,
+ it.start.coerceIn(start, end) - start,
+ it.end.coerceIn(start, end) - start
+ )
+ }
+}
+
+/**
+ * This is a helper data structure to store the information of a single [Paragraph] in an
+ * [MultiParagraph]. It's mainly used to convert a global index, lineNumber and [Offset] to the
+ * local ones inside the [paragraph], and vice versa.
+ *
+ * @param paragraph The [Paragraph] object corresponding to this [ParagraphInfo].
+ * @param startIndex The start index of this paragraph in the parent [MultiParagraph], inclusive.
+ * @param endIndex The end index of this paragraph in the parent [MultiParagraph], exclusive.
+ * @param startLineIndex The start line index of this paragraph in the parent [MultiParagraph],
+ * inclusive.
+ * @param endLineIndex The end line index of this paragraph in the parent [MultiParagraph],
+ * exclusive.
+ * @param top The top position of the [paragraph] relative to the parent [MultiParagraph].
+ * @param bottom The bottom position of the [paragraph] relative to the parent [MultiParagraph].
+ */
+internal data class ParagraphInfo(
+ val paragraph: Paragraph,
+ val startIndex: Int,
+ val endIndex: Int,
+ var startLineIndex: Int = -1,
+ var endLineIndex: Int = -1,
+ var top: Px = (-1).px,
+ var bottom: Px = (-1).px
+) {
+
+ /**
+ * The length of the text in the covered by this paragraph.
+ */
+ val length
+ get() = endIndex - startIndex
+
+ /**
+ * Convert an index in the parent [MultiParagraph] to the local index in the [paragraph].
+ */
+ fun Int.toLocalIndex(): Int {
+ return this.coerceIn(startIndex, endIndex) - startIndex
+ }
+
+ /**
+ * Convert a local index in the [paragraph] to the global index in the parent [MultiParagraph].
+ */
+ fun Int.toGlobalIndex(): Int {
+ return this + startIndex
+ }
+
+ /**
+ * Convert a line index in the parent [MultiParagraph] to the local line index in the
+ * [paragraph].
+ *
+ */
+ fun Int.toLocalLineIndex(): Int {
+ return this - startLineIndex
+ }
+
+ /**
+ * Convert a local line index in the [paragraph] to the global line index in the parent
+ * [MultiParagraph].
+ */
+ fun Int.toGlobalLineIndex(): Int {
+ return this + startLineIndex
+ }
+
+ /**
+ * Convert a [PxPosition] relative to the parent [MultiParagraph] to the local [PxPosition]
+ * relative to the [paragraph].
+ */
+ fun PxPosition.toLocal(): PxPosition {
+ return copy(y = y - top)
+ }
+
+ /**
+ * Convert a [Rect] relative to the [paragraph] to the [Rect] relative to the parent
+ * [MultiParagraph].
+ */
+ fun Rect.toGlobal(): Rect {
+ return shift(Offset(dx = 0f, dy = this@ParagraphInfo.top.value))
+ }
+
+ /**
+ * Convert a [Path] relative to the [paragraph] to the [Path] relative to the parent
+ * [MultiParagraph].
+ *
+ * Notice that this function changes the input value.
+ */
+ fun Path.toGlobal(): Path {
+ shift(Offset(dx = 0f, dy = top.value))
+ return this
+ }
+
+ /**
+ * Convert a [TextRange] in to the [paragraph] to the [TextRange] in the parent
+ * [MultiParagraph].
+ */
+ fun TextRange.toGlobal(): TextRange {
+ return TextRange(start = start.toGlobalIndex(), end = end.toGlobalIndex())
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt b/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt
index dd7c248..4fd974e 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt
@@ -47,19 +47,20 @@
}
}
// TODO(siyamed) uncomment
-// /**
-// * Returns a new paragraph style that is a combination of this style and the given [other] style.
-// *
-// * If the given paragraph style is null, returns this paragraph style.
-// */
-// fun merge(other: ParagraphStyle? = null): ParagraphStyle {
-// if (other == null) return this
-//
-// return ParagraphStyle(
-// lineHeight = other.lineHeight ?: this.lineHeight,
-// textIndent = other.textIndent ?: this.textIndent,
-// textAlign = other.textAlign ?: this.textAlign,
-// textDirection = other.textDirection ?: this.textDirection
-// )
-// }
+ /**
+ * Returns a new paragraph style that is a combination of this style and the given [other]
+ * style.
+ *
+ * If the given paragraph style is null, returns this paragraph style.
+ */
+ fun merge(other: ParagraphStyle? = null): ParagraphStyle {
+ if (other == null) return this
+
+ return ParagraphStyle(
+ lineHeight = other.lineHeight ?: this.lineHeight,
+ textIndent = other.textIndent ?: this.textIndent,
+ textAlign = other.textAlign ?: this.textAlign,
+ textDirection = other.textDirection ?: this.textDirection
+ )
+ }
}
\ No newline at end of file
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt b/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt
index 7880c2d..0a5d93a 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt
@@ -118,7 +118,7 @@
}
@VisibleForTesting
- internal var paragraph: Paragraph? = null
+ internal var multiParagraph: MultiParagraph? = null
private set
@VisibleForTesting
@@ -143,7 +143,7 @@
set(value) {
if (field == value) return
field = value
- paragraph = null
+ multiParagraph = null
needsLayout = true
}
@@ -219,7 +219,7 @@
val minIntrinsicWidth: Float
get() {
assertNeedsLayout("minIntrinsicWidth")
- return applyFloatingPointHack(paragraph!!.minIntrinsicWidth)
+ return applyFloatingPointHack(multiParagraph!!.minIntrinsicWidth)
}
/**
@@ -230,7 +230,7 @@
val maxIntrinsicWidth: Float
get() {
assertNeedsLayout("maxIntrinsicWidth")
- return applyFloatingPointHack(paragraph!!.maxIntrinsicWidth)
+ return applyFloatingPointHack(multiParagraph!!.maxIntrinsicWidth)
}
/**
@@ -281,7 +281,7 @@
val didExceedMaxLines: Boolean
get() {
assertNeedsLayout("didExceedMaxLines")
- return paragraph!!.didExceedMaxLines
+ return multiParagraph!!.didExceedMaxLines
}
/**
@@ -308,25 +308,25 @@
if (!needsLayout && minWidth == lastMinWidth && finalMaxWidth == lastMaxWidth) return
needsLayout = false
- if (paragraph == null) {
- paragraph = Paragraph(
- text = text!!.text,
- style = createTextStyle(),
- paragraphStyle = createParagraphStyle(),
- textStyles = text!!.textStyles,
- maxLines = maxLines,
- ellipsis = isEllipsis,
- density = density,
- resourceLoader = resourceLoader
+
+ if (multiParagraph == null) {
+ multiParagraph = MultiParagraph(
+ text!!,
+ createTextStyle(),
+ paragraphStyle ?: ParagraphStyle(),
+ maxLines,
+ isEllipsis,
+ density,
+ resourceLoader
)
}
lastMinWidth = minWidth
lastMaxWidth = finalMaxWidth
- paragraph!!.layout(ParagraphConstraints(width = finalMaxWidth))
+ multiParagraph!!.layout(ParagraphConstraints(width = finalMaxWidth))
if (minWidth != finalMaxWidth) {
val newWidth = maxIntrinsicWidth.coerceIn(minWidth, finalMaxWidth)
- if (newWidth != paragraph!!.width) {
- paragraph!!.layout(ParagraphConstraints(width = newWidth))
+ if (newWidth != multiParagraph!!.width) {
+ multiParagraph!!.layout(ParagraphConstraints(width = newWidth))
}
}
}
@@ -336,11 +336,11 @@
val didOverflowHeight = didExceedMaxLines
size = constraints.constrain(
- IntPxSize(paragraph!!.width.px.round(), paragraph!!.height.px.round())
+ IntPxSize(multiParagraph!!.width.px.round(), multiParagraph!!.height.px.round())
).let {
Size(it.width.value.toFloat(), it.height.value.toFloat())
}
- val didOverflowWidth = size.width < paragraph!!.width
+ val didOverflowWidth = size.width < multiParagraph!!.width
// TODO(abarth): We're only measuring the sizes of the line boxes here. If
// the glyphs draw outside the line boxes, we might think that there isn't
// visual overflow when there actually is visual overflow. This can become
@@ -356,8 +356,8 @@
resourceLoader = resourceLoader
)
fadeSizePainter.layoutText()
- val fadeWidth = fadeSizePainter.paragraph!!.width
- val fadeHeight = fadeSizePainter.paragraph!!.height
+ val fadeWidth = fadeSizePainter.multiParagraph!!.width
+ val fadeHeight = fadeSizePainter.multiParagraph!!.height
if (didOverflowWidth) {
val (fadeStart, fadeEnd) = if (textDirection == TextDirection.Rtl) {
Pair(fadeWidth, 0.0f)
@@ -426,7 +426,8 @@
}
canvas.clipRect(bounds)
}
- paragraph!!.paint(canvas)
+
+ multiParagraph!!.paint(canvas)
if (hasVisualOverflow) {
if (overflowShader != null) {
val bounds = Rect.fromLTWH(0f, 0f, size.width, size.height)
@@ -452,7 +453,7 @@
fun paintBackground(start: Int, end: Int, color: Color, canvas: Canvas) {
assert(!needsLayout)
if (start == end) return
- val selectionPath = paragraph!!.getPathForRange(start, end)
+ val selectionPath = multiParagraph!!.getPathForRange(start, end)
// TODO(haoyuchang): check if move this paint to parameter is better
canvas.drawPath(selectionPath, Paint().apply { this.color = color })
}
@@ -467,7 +468,7 @@
*/
fun paintCursor(offset: Int, canvas: Canvas) {
assert(!needsLayout)
- val cursorRect = paragraph!!.getCursorRect(offset)
+ val cursorRect = multiParagraph!!.getCursorRect(offset)
canvas.drawRect(cursorRect, Paint().apply { this.color = Color.Black })
}
@@ -479,7 +480,7 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
fun getLineBottom(lineIndex: Int): Float {
assert(!needsLayout)
- return paragraph!!.getLineBottom(lineIndex)
+ return multiParagraph!!.getLineBottom(lineIndex)
}
/**
@@ -492,7 +493,7 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
fun getLineForOffset(offset: Int): Int {
assert(!needsLayout)
- return paragraph!!.getLineForOffset(offset)
+ return multiParagraph!!.getLineForOffset(offset)
}
/**
@@ -503,13 +504,13 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
fun getPrimaryHorizontal(offset: Int): Float {
assert(!needsLayout)
- return paragraph!!.getPrimaryHorizontal(offset)
+ return multiParagraph!!.getPrimaryHorizontal(offset)
}
/** Returns the character offset closest to the given graphical position. */
fun getOffsetForPosition(position: PxPosition): Int {
assert(!needsLayout)
- return paragraph!!.getOffsetForPosition(position)
+ return multiParagraph!!.getOffsetForPosition(position)
}
/**
@@ -523,7 +524,7 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
fun getBoundingBox(offset: Int): Rect {
assert(!needsLayout)
- return paragraph!!.getBoundingBox(offset)
+ return multiParagraph!!.getBoundingBox(offset)
}
/**
@@ -536,6 +537,6 @@
*/
fun getWordBoundary(offset: Int): TextRange {
assert(!needsLayout)
- return paragraph!!.getWordBoundary(offset)
+ return multiParagraph!!.getWordBoundary(offset)
}
}
diff --git a/ui/ui-text/src/test/java/androidx/ui/text/MultiParagraphTest.kt b/ui/ui-text/src/test/java/androidx/ui/text/MultiParagraphTest.kt
new file mode 100644
index 0000000..ce472ad
--- /dev/null
+++ b/ui/ui-text/src/test/java/androidx/ui/text/MultiParagraphTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019 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.ui.text
+
+import androidx.ui.text.style.TextAlign
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class MultiParagraphTest {
+ @Test
+ fun `test fillInParagraphRanges`() {
+ val text = "Hello World"
+ val paragraphStyle = ParagraphStyle(textAlign = TextAlign.Center)
+ val paragraphStyles = listOf(AnnotatedString.Item(paragraphStyle, 0, 5))
+ val annotatedString = AnnotatedString(text = text, paragraphStyles = paragraphStyles)
+ val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+ val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+ assertThat(paragraphs.size).isEqualTo(2)
+
+ assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle.merge(paragraphStyle))
+ assertThat(paragraphs[0].start).isEqualTo(0)
+ assertThat(paragraphs[0].end).isEqualTo(5)
+
+ assertThat(paragraphs[1].style).isEqualTo(defaultParagraphStyle)
+ assertThat(paragraphs[1].start).isEqualTo(5)
+ assertThat(paragraphs[1].end).isEqualTo(text.length)
+ }
+
+ @Test
+ fun `test fillInParagraphRanges only string`() {
+ val text = "Hello World"
+ val annotatedString = AnnotatedString(text = text)
+ val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+ val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+ assertThat(paragraphs.size).isEqualTo(1)
+
+ assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle)
+ assertThat(paragraphs[0].start).isEqualTo(0)
+ assertThat(paragraphs[0].end).isEqualTo(text.length)
+ }
+
+ @Test
+ fun `test fillInParagraphRanges empty string`() {
+ val text = ""
+ val annotatedString = AnnotatedString(text = text)
+ val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+ val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+ assertThat(paragraphs.size).isEqualTo(1)
+
+ assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle)
+ assertThat(paragraphs[0].start).isEqualTo(0)
+ assertThat(paragraphs[0].end).isEqualTo(text.length)
+ }
+
+ @Test
+ fun `test fillInParagraphRanges with newLine`() {
+ val text = "Hello\nWorld"
+ val annotatedString = AnnotatedString(text = text)
+ val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+ val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+ assertThat(paragraphs.size).isEqualTo(1)
+
+ assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle)
+ assertThat(paragraphs[0].start).isEqualTo(0)
+ assertThat(paragraphs[0].end).isEqualTo(text.length)
+ }
+
+ @Test
+ fun `test fillInParagraphRanges with only lineFeed`() {
+ val text = "\n"
+ val annotatedString = AnnotatedString(text = text)
+ val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+ val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+ assertThat(paragraphs.size).isEqualTo(1)
+
+ assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle)
+ assertThat(paragraphs[0].start).isEqualTo(0)
+ assertThat(paragraphs[0].end).isEqualTo(1)
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-text/src/test/java/androidx/ui/text/TextPainterTest.kt b/ui/ui-text/src/test/java/androidx/ui/text/TextPainterTest.kt
index 6c07a79..5dcae69 100644
--- a/ui/ui-text/src/test/java/androidx/ui/text/TextPainterTest.kt
+++ b/ui/ui-text/src/test/java/androidx/ui/text/TextPainterTest.kt
@@ -127,7 +127,7 @@
textPainter.text = text
assertThat(textPainter.text).isEqualTo(text)
- assertThat(textPainter.paragraph).isNull()
+ assertThat(textPainter.multiParagraph).isNull()
assertThat(textPainter.needsLayout).isTrue()
}
diff --git a/ui/ui-vector/OWNERS b/ui/ui-vector/OWNERS
new file mode 100644
index 0000000..c3a259b
--- /dev/null
+++ b/ui/ui-vector/OWNERS
@@ -0,0 +1,13 @@
+pavlis@google.com
+adamp@google.com
+mount@google.com
+popam@google.com
+andreykulikov@google.com
+ryanmentley@google.com
+shepshapard@google.com
+njawad@google.com
+haoyuchang@google.com
+nona@google.com
+siyamed@google.com
+qqd@google.com
+sumir@google.com
\ No newline at end of file
diff --git a/ui/ui-vector/build.gradle b/ui/ui-vector/build.gradle
new file mode 100644
index 0000000..014b6cb
--- /dev/null
+++ b/ui/ui-vector/build.gradle
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 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.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("AndroidXUiPlugin")
+ id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+ kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+
+ implementation project(':ui:ui-core')
+ implementation "androidx.collection:collection:1.0.0"
+ implementation project(":compose:compose-runtime")
+ implementation (KOTLIN_COMPOSE_STDLIB)
+
+ // TODO: Non-Kotlin dependency, move to Android-specific code
+// implementation "androidx.collection:collection:1.0.0-alpha01"
+ // TODO: Non-Kotlin dependency, move to Android-specific code
+ implementation "androidx.core:core:1.0.0"
+}
+
+androidx {
+ name = "AndroidX UI Vector"
+ publish = Publish.SNAPSHOT_AND_RELEASE
+ mavenVersion = LibraryVersions.UI
+ mavenGroup = LibraryGroups.UI
+ inceptionYear = "2019"
+ description = "AndroidX UI Vector"
+}
+
+tasks.withType(KotlinCompile).configureEach {
+ kotlinOptions {
+ // TODO(njawad): Temporary disabled, make it true when IR bug b/129076229 is fixed.
+ useIR = false
+ }
+}
diff --git a/ui/ui-vector/src/androidTest/AndroidManifest.xml b/ui/ui-vector/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..d974f89
--- /dev/null
+++ b/ui/ui-vector/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 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.
+ -->
+<manifest package="androidx.ui.vector" xmlns:android="http://schemas.android.com/apk/res/android">
+</manifest>
+
diff --git a/ui/ui-vector/src/main/AndroidManifest.xml b/ui/ui-vector/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d974f89
--- /dev/null
+++ b/ui/ui-vector/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 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.
+ -->
+<manifest package="androidx.ui.vector" xmlns:android="http://schemas.android.com/apk/res/android">
+</manifest>
+
diff --git a/ui/ui-vector/src/main/java/androidx/ui/vector/VectorComposeNonIR.kt b/ui/ui-vector/src/main/java/androidx/ui/vector/VectorComposeNonIR.kt
new file mode 100644
index 0000000..c9fb0acd
--- /dev/null
+++ b/ui/ui-vector/src/main/java/androidx/ui/vector/VectorComposeNonIR.kt
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2019 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.ui.vector
+
+import androidx.compose.Applier
+import androidx.compose.ApplyAdapter
+import androidx.compose.Component
+import androidx.compose.Composable
+import androidx.compose.Composer
+import androidx.compose.ComposerUpdater
+import androidx.compose.CompositionContext
+import androidx.compose.CompositionReference
+import androidx.compose.Effect
+import androidx.compose.Recomposer
+import androidx.compose.SlotTable
+import androidx.compose.ViewValidator
+import androidx.compose.cache
+import androidx.ui.graphics.vectorgraphics.GroupComponent
+import androidx.ui.graphics.vectorgraphics.VNode
+import androidx.ui.graphics.vectorgraphics.VectorComponent
+import java.util.WeakHashMap
+
+private val VectorTreeRoots = WeakHashMap<VectorComponent, VectorTree>()
+
+class VectorScope(val composer: VectorComposition)
+
+private fun obtainVectorTree(container: VectorComponent): VectorTree {
+ var vectorTree = VectorTreeRoots[container]
+ if (vectorTree == null) {
+ vectorTree = VectorTree()
+ VectorTreeRoots[container] = vectorTree
+ }
+ return vectorTree
+}
+
+fun composeVector(
+ container: VectorComponent,
+ parent: CompositionReference? = null,
+ composable: @Composable() VectorScope.() -> Unit
+) {
+ var root = VectorTreeRoots[container]
+ if (root == null) {
+ lateinit var composer: VectorComposer
+ root = obtainVectorTree(container)
+ root.context = CompositionContext.prepare(root, parent) {
+ VectorComposer(container.root, this).also { composer = it }
+ }
+ root.scope = VectorScope(VectorComposition(composer))
+ }
+ root.composable = composable
+ root.context.compose()
+}
+
+class VectorComposer(
+ val root: VNode,
+ recomposer: Recomposer
+) : Composer<VNode>(SlotTable(), Applier(root, VectorApplyAdapter()), recomposer)
+
+fun disposeVector(container: VectorComponent, parent: CompositionReference? = null) {
+ composeVector(container, parent) {}
+ VectorTreeRoots.remove(container)
+}
+
+private class VectorTree : Component() {
+
+ lateinit var scope: VectorScope
+ lateinit var composable: @Composable() VectorScope.() -> Unit
+ lateinit var context: CompositionContext
+
+ override fun compose() {
+ with(context.composer) {
+ startGroup(0) // TODO (njawad) what key should be used here?
+ scope.composable()
+ endGroup()
+ }
+ }
+}
+
+@PublishedApi
+internal val VectorGroupKey = Object()
+
+internal class VectorApplyAdapter : ApplyAdapter<VNode> {
+ override fun VNode.start(instance: VNode) {
+ // NO-OP
+ }
+
+ override fun VNode.insertAt(index: Int, instance: VNode) {
+ obtainGroup().insertAt(index, instance)
+ }
+
+ override fun VNode.removeAt(index: Int, count: Int) {
+ obtainGroup().remove(index, count)
+ }
+
+ override fun VNode.move(from: Int, to: Int, count: Int) {
+ obtainGroup().move(from, to, count)
+ }
+
+ override fun VNode.end(instance: VNode, parent: VNode) {
+ // NO-OP
+ }
+
+ fun VNode.obtainGroup(): GroupComponent {
+ return when (this) {
+ is GroupComponent -> this
+ else -> throw IllegalArgumentException("Cannot only insert VNode into Group")
+ }
+ }
+}
+
+typealias VectorUpdater<T> = ComposerUpdater<VNode, T>
+
+class VectorComposition(val composer: VectorComposer) {
+ @Suppress("NOTHING_TO_INLINE")
+ inline operator fun <V> Effect<V>.unaryPlus(): V = resolve(this@VectorComposition.composer)
+
+ inline fun <T: VNode> emit(
+ key: Any,
+ /*crossinline*/
+ ctor: () -> T,
+ update: VectorUpdater<VNode>.() -> Unit
+ ) = with(composer) {
+ startNode(key)
+
+ @Suppress("UNCHECKED_CAST")
+ val node = if (inserting) {
+ ctor().also {
+ emitNode(it)
+ }
+ } else {
+ useNode()
+ }
+
+ VectorUpdater(this, node).update()
+ endNode()
+ }
+
+ inline fun emit(
+ key: Any,
+ /*crossinline*/
+ ctor: () -> GroupComponent,
+ update: VectorUpdater<GroupComponent>.() -> Unit,
+ children: () -> Unit
+ ) = with(composer) {
+ startNode(key)
+
+ @Suppress("UNCHECKED_CAST")
+ val node = if (inserting) {
+ ctor().also {
+ emitNode(it)
+ }
+ } else {
+ useNode() as GroupComponent
+ }
+
+ VectorUpdater(this, node).update()
+ children()
+ endNode()
+ }
+
+ @Suppress("NOTHING_TO_INLINE")
+ inline fun joinKey(left: Any, right: Any?): Any = composer.joinKey(left, right)
+
+ inline fun call(
+ key: Any,
+ /*crossinline*/
+ invalid: ViewValidator.() -> Boolean,
+ block: () -> Unit
+ ) = with(composer) {
+ startGroup(key)
+ if (ViewValidator(composer).invalid() || inserting) {
+ startGroup(0)
+ block()
+ endGroup()
+ } else {
+ skipGroup(0)
+ }
+ endGroup()
+ }
+
+ inline fun <T> call(
+ key: Any,
+ /*crossinline*/
+ ctor: () -> T,
+ /*crossinline*/
+ invalid: ViewValidator.(f: T) -> Boolean,
+ block: (f: T) -> Unit
+ ) = with(composer) {
+ startGroup(key)
+ val f = cache(true, ctor)
+ if (ViewValidator(this).invalid(f) || inserting) {
+ startGroup(0)
+ block(f)
+ endGroup()
+ } else {
+ skipGroup(0)
+ }
+ endGroup()
+ }
+}
\ No newline at end of file
diff --git a/viewpager/build.gradle b/viewpager/build.gradle
index f1f7e65..2559de8 100644
--- a/viewpager/build.gradle
+++ b/viewpager/build.gradle
@@ -17,7 +17,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
api(project(":customview"))
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/viewpager2/build.gradle b/viewpager2/build.gradle
index 29ea26d..9de6aa7 100644
--- a/viewpager2/build.gradle
+++ b/viewpager2/build.gradle
@@ -27,7 +27,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.1.0-rc01")
api("androidx.fragment:fragment:1.1.0-rc01")
api("androidx.recyclerview:recyclerview:1.1.0-beta01")
implementation("androidx.collection:collection:1.1.0")
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeWhileSmoothScrollTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeWhileSmoothScrollTest.kt
new file mode 100644
index 0000000..a1bc911
--- /dev/null
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeWhileSmoothScrollTest.kt
@@ -0,0 +1,405 @@
+/*
+ * Copyright 2019 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.viewpager2.widget
+
+import androidx.test.filters.LargeTest
+import androidx.testutils.LocaleTestUtils
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.Event.MarkerEvent
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.Event.OnPageScrollStateChangedEvent
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.Event.OnPageScrolledEvent
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.Event.OnPageSelectedEvent
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.Modification.REMOVE_FIRST_VISIBLE
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.Modification.SHIFT_FIRST_VISIBLE
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.Modification.SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.Modification.SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST_AND_LAST
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.Modification.SHIFT_FIRST_VISIBLE_THEN_REMOVE_LAST
+import androidx.viewpager2.widget.AdapterDataSetChangeWhileSmoothScrollTest.TestConfig
+import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
+import androidx.viewpager2.widget.ViewPager2.ORIENTATION_VERTICAL
+import androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE
+import androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING
+import org.hamcrest.CoreMatchers.equalTo
+import org.hamcrest.Matchers.greaterThan
+import org.junit.Assert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.concurrent.TimeUnit.SECONDS
+import kotlin.math.roundToInt
+
+/** Number of pages */
+private const val pageCount = 25
+/** Page where VP2 starts */
+private const val initialPage = 0
+/** Page where we smooth scroll to */
+private const val targetPage = 20
+
+/** Id of the mark we make when modifying the dataset */
+private const val modificationMark = 1
+
+/** How many pages from x before x gets bound? */
+private const val bindThreshold = 2
+/** Value between 0 and 1/pageSizePx */
+private const val epsilon = 0.00001f
+
+@RunWith(Parameterized::class)
+@LargeTest
+class AdapterDataSetChangeWhileSmoothScrollTest(private val config: TestConfig) : BaseTest() {
+ data class TestConfig(
+ @ViewPager2.Orientation val orientation: Int,
+ val rtl: Boolean,
+ val targetBound: Boolean,
+ val modification: Modification,
+ val adapterProvider: AdapterProviderForItems,
+ val expectedFinalPage: Int,
+ val expectedFinalPageText: String
+ )
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun spec(): List<TestConfig> = createTestSet()
+ }
+
+ enum class Modification {
+ SHIFT_FIRST_VISIBLE,
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST,
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_LAST,
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST_AND_LAST,
+ REMOVE_FIRST_VISIBLE
+ }
+
+ // start and end of the window of opportunity to modify the dataset
+ private val windowStart = targetPage - bindThreshold - if (config.targetBound) 0 else 1
+ private val windowEnd = targetPage - if (config.targetBound) 1 else bindThreshold
+
+ private lateinit var test: Context
+ private lateinit var dataSet: MutableList<String>
+
+ override fun setUp() {
+ super.setUp()
+ if (config.rtl) {
+ localeUtil.resetLocale()
+ localeUtil.setLocale(LocaleTestUtils.RTL_LANGUAGE)
+ }
+
+ test = setUpTest(config.orientation)
+ activityTestRule.runOnUiThread { test.viewPager.offscreenPageLimit = 1 }
+ dataSet = stringSequence(pageCount).toMutableList()
+ test.setAdapterSync(config.adapterProvider(dataSet))
+ }
+
+ @Test
+ fun test() {
+ tryNTimes(3, resetBlock = { test.resetViewPagerTo(initialPage) }) {
+ // when we are scrolling to the target
+ val recorder = test.viewPager.addNewRecordingCallback()
+ val idleLatch = test.viewPager.addWaitForIdleLatch()
+
+ scrollToTargetUntilWindowStart()
+
+ // and we remove the first visible item
+ test.modifyDataSetSync {
+ verifyWindowOfOpportunity(recorder.scrollEvents.last())
+ recorder.markModification()
+ makeModification()
+ }
+ idleLatch.await(10, SECONDS)
+
+ // then
+ test.assertBasicState(config.expectedFinalPage, config.expectedFinalPageText)
+ recorder.apply {
+ val removeItemMarkIx = markerIx(modificationMark)
+ val expectedSelectEvents = if (targetPage == config.expectedFinalPage) {
+ listOf(targetPage)
+ } else {
+ listOf(targetPage, config.expectedFinalPage)
+ }
+ // verify all events
+ assertThat(settlingIx, equalTo(0))
+ assertThat(pageSelectedIx(targetPage), equalTo(1))
+ assertThat(removeItemMarkIx, greaterThan(1))
+ assertThat(idleIx, equalTo(lastIx))
+ assertThat(selectEvents.map { it.position }, equalTo(expectedSelectEvents))
+ assertThat(scrollEventCount, equalTo(eventCount - 3 - expectedSelectEvents.size))
+
+ // verify scroll events _before_ and _after_ the marker
+ val scrollsBeforeMarker = scrollEventsBefore(removeItemMarkIx)
+ val scrollsAfterMarker = scrollEventsAfter(removeItemMarkIx)
+ listOf(scrollsBeforeMarker, scrollsAfterMarker).forEach {
+ it.assertPositionSorted(SortOrder.ASC)
+ it.assertOffsetSorted(SortOrder.ASC)
+ it.assertValueSanity(0, targetPage, test.viewPager.pageSize)
+ }
+ // Only check assertMaxShownPages on scroll events _before_ the marker:
+ // after the data set change, it can scroll an arbitrary number of pages
+ scrollsBeforeMarker.assertMaxShownPages()
+ // Only check assertLastCorrect on scroll events _after_ the marker:
+ // the target is not reached before the data set change
+ scrollsAfterMarker.assertLastCorrect(config.expectedFinalPage)
+ }
+ }
+ }
+
+ private fun scrollToTargetUntilWindowStart() {
+ val latch = test.viewPager
+ .addWaitForDistanceToTarget(targetPage, targetPage - windowStart - epsilon)
+ test.runOnUiThreadSync {
+ test.viewPager.setCurrentItem(targetPage, true)
+ }
+ latch.await(2, SECONDS)
+ }
+
+ private fun verifyWindowOfOpportunity(lastScrollEvent: OnPageScrolledEvent) {
+ val lastScrollPosition = lastScrollEvent.let {
+ it.position + it.positionOffset.toDouble()
+ }
+ if (lastScrollPosition >= windowEnd) {
+ throw RetryException("Data set should be modified while scrolling through " +
+ "($windowStart, $windowEnd), but was modified at $lastScrollPosition")
+ }
+ }
+
+ private fun makeModification() {
+ when (config.modification) {
+ REMOVE_FIRST_VISIBLE -> {
+ removeCurrentPage()
+ }
+ SHIFT_FIRST_VISIBLE -> {
+ shiftCurrentPageToStart()
+ }
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST -> {
+ shiftCurrentPageToStart()
+ removeFirstPages()
+ }
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_LAST -> {
+ shiftCurrentPageToStart()
+ removeLastPages()
+ }
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST_AND_LAST -> {
+ shiftCurrentPageToStart()
+ removeLastPages()
+ removeFirstPages()
+ }
+ }
+ }
+
+ private fun shiftCurrentPageToStart() {
+ // Move currently visible position and target apart from each other
+ repeat(test.viewPager.linearLayoutManager.findFirstVisibleItemPosition()) {
+ val item = dataSet.removeAt(0)
+ dataSet.add(targetPage - 1, item)
+ test.viewPager.adapter!!.notifyItemMoved(0, targetPage - 1)
+ }
+ }
+
+ private fun removeCurrentPage() {
+ val position = test.viewPager.linearLayoutManager.findFirstVisibleItemPosition()
+ dataSet.removeAt(position)
+ test.viewPager.adapter!!.notifyItemRemoved(position)
+ }
+
+ private fun removeLastPages() {
+ // Remove last items (including the target)
+ val removeCount = pageCount - targetPage
+ repeat(removeCount) {
+ dataSet.removeAt(targetPage)
+ }
+ test.viewPager.adapter!!.notifyItemRangeRemoved(targetPage, removeCount)
+ }
+
+ private fun removeFirstPages() {
+ // Remove first items (including the first visible item)
+ repeat(2) { dataSet.removeAt(0) }
+ test.viewPager.adapter!!.notifyItemRangeRemoved(0, 2)
+ }
+
+ private fun ViewPager2.addNewRecordingCallback(): RecordingCallback {
+ return RecordingCallback().also { registerOnPageChangeCallback(it) }
+ }
+
+ private sealed class Event {
+ data class OnPageScrolledEvent(
+ val position: Int,
+ val positionOffset: Float,
+ val positionOffsetPixels: Int
+ ) : Event()
+ data class OnPageSelectedEvent(val position: Int) : Event()
+ data class OnPageScrollStateChangedEvent(val state: Int) : Event()
+ data class MarkerEvent(val id: Int) : Event()
+ }
+
+ private class RecordingCallback : ViewPager2.OnPageChangeCallback() {
+ private val events = mutableListOf<Event>()
+
+ val scrollEvents get() = events.mapNotNull { it as? OnPageScrolledEvent }
+ val selectEvents get() = events.mapNotNull { it as? OnPageSelectedEvent }
+ val eventCount get() = events.size
+ val scrollEventCount get() = scrollEvents.size
+ val lastIx get() = events.size - 1
+ val settlingIx get() = events.indexOf(OnPageScrollStateChangedEvent(SCROLL_STATE_SETTLING))
+ val idleIx get() = events.indexOf(OnPageScrollStateChangedEvent(SCROLL_STATE_IDLE))
+ val pageSelectedIx: (page: Int) -> Int = { events.indexOf(OnPageSelectedEvent(it)) }
+ val markerIx: (id: Int) -> Int = { events.indexOf(MarkerEvent(it)) }
+
+ val scrollEventsBefore: (ix: Int) -> List<OnPageScrolledEvent> =
+ { scrollEventsBetween(0, it) }
+ val scrollEventsAfter: (ix: Int) -> List<OnPageScrolledEvent> =
+ { scrollEventsBetween(it + 1, events.size) }
+ val scrollEventsBetween: (fromIx: Int, toIx: Int) -> List<OnPageScrolledEvent> = { a, b ->
+ events.subList(a, b).mapNotNull { it as? OnPageScrolledEvent }
+ }
+
+ override fun onPageScrolled(
+ position: Int,
+ positionOffset: Float,
+ positionOffsetPixels: Int
+ ) {
+ events.add(OnPageScrolledEvent(position, positionOffset, positionOffsetPixels))
+ }
+
+ override fun onPageSelected(position: Int) {
+ events.add(OnPageSelectedEvent(position))
+ }
+
+ override fun onPageScrollStateChanged(state: Int) {
+ events.add(OnPageScrollStateChangedEvent(state))
+ }
+
+ fun markEvent(id: Int) {
+ events.add(MarkerEvent(id))
+ }
+ }
+
+ private fun RecordingCallback.markModification() {
+ markEvent(modificationMark)
+ }
+
+ private fun List<OnPageScrolledEvent>.assertPositionSorted(sortOrder: SortOrder) {
+ map { it.position }.assertSorted { it * sortOrder.sign }
+ }
+
+ private fun List<OnPageScrolledEvent>.assertLastCorrect(targetPage: Int) {
+ last().apply {
+ assertThat(position, equalTo(targetPage))
+ assertThat(positionOffsetPixels, equalTo(0))
+ }
+ }
+
+ private fun List<OnPageScrolledEvent>.assertValueSanity(
+ initialPage: Int,
+ otherPage: Int,
+ pageSize: Int
+ ) = forEach {
+ assertThat(it.position, isBetweenInInMinMax(initialPage, otherPage))
+ assertThat(it.positionOffset, isBetweenInEx(0f, 1f))
+ assertThat((it.positionOffset * pageSize).roundToInt(), equalTo(it.positionOffsetPixels))
+ }
+
+ private fun List<OnPageScrolledEvent>.assertOffsetSorted(sortOrder: SortOrder) {
+ map { it.position + it.positionOffset.toDouble() }.assertSorted { it * sortOrder.sign }
+ }
+
+ private fun List<OnPageScrolledEvent>.assertMaxShownPages() {
+ assertThat(map { it.position }.distinct().size, isBetweenInIn(0, 4))
+ }
+}
+
+// region Test Suite creation
+
+private fun createTestSet(): List<TestConfig> {
+ return listOf(viewAdapterProvider, viewAdapterProviderValueId).flatMap { adapterProvider ->
+ listOf(ORIENTATION_HORIZONTAL, ORIENTATION_VERTICAL).flatMap { orientation ->
+ listOf(false, true).flatMap { rtl ->
+ listOf(
+ TestConfig(
+ orientation, rtl, true,
+ SHIFT_FIRST_VISIBLE,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = 2,
+ expectedFinalPageText = "0"
+ ),
+ TestConfig(
+ orientation, rtl, true,
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = targetPage - 1,
+ expectedFinalPageText = "${targetPage + 1}"
+ ),
+ TestConfig(
+ orientation, rtl, true,
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_LAST,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = 2,
+ expectedFinalPageText = "0"
+ ),
+ TestConfig(
+ orientation, rtl, true,
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST_AND_LAST,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = targetPage - 3,
+ expectedFinalPageText = "${targetPage - 3}"
+ ),
+ TestConfig(
+ orientation, rtl, true,
+ REMOVE_FIRST_VISIBLE,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = targetPage - 1,
+ expectedFinalPageText = "$targetPage"
+ ),
+ TestConfig(
+ orientation, rtl, false,
+ SHIFT_FIRST_VISIBLE,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = targetPage,
+ expectedFinalPageText = "$targetPage"
+ ),
+ TestConfig(
+ orientation, rtl, false,
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = targetPage,
+ expectedFinalPageText = "${targetPage + 2}"
+ ),
+ TestConfig(
+ orientation, rtl, false,
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_LAST,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = targetPage - 1,
+ expectedFinalPageText = "${targetPage - 4}"
+ ),
+ TestConfig(
+ orientation, rtl, false,
+ SHIFT_FIRST_VISIBLE_THEN_REMOVE_FIRST_AND_LAST,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = targetPage - 3,
+ expectedFinalPageText = "${targetPage - 4}"
+ ),
+ TestConfig(
+ orientation, rtl, false,
+ REMOVE_FIRST_VISIBLE,
+ adapterProvider = adapterProvider,
+ expectedFinalPage = targetPage,
+ expectedFinalPageText = "${targetPage + 1}"
+ )
+ )
+ }
+ }
+ }
+}
+
+// endregion
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
index 1a284f4..8185e8b 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
@@ -45,6 +45,7 @@
import androidx.test.rule.ActivityTestRule
import androidx.testutils.LocaleTestUtils
import androidx.testutils.recreate
+import androidx.testutils.waitForExecution
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.test.R
import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
@@ -503,6 +504,13 @@
assertPageActions()
}
+ fun Context.resetViewPagerTo(page: Int) {
+ viewPager.setCurrentItemSync(page, false, 2, TimeUnit.SECONDS)
+ // VP2 was potentially settling while the RetryException was raised,
+ // in which case we must wait until the IDLE event has been fired
+ activityTestRule.waitForExecution(1)
+ }
+
fun Context.modifyDataSetSync(block: () -> Unit) {
val layoutChangedLatch = viewPager.addWaitForLayoutChangeLatch()
runOnUiThreadSync {
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ChangeDataSetWhileScrollingTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ChangeDataSetWhileScrollingTest.kt
new file mode 100644
index 0000000..861f676
--- /dev/null
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ChangeDataSetWhileScrollingTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 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.viewpager2.widget
+
+import android.os.SystemClock.sleep
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.testutils.PollingCheck
+import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
+import androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class ChangeDataSetWhileScrollingTest : BaseTest() {
+ private val orientation = ORIENTATION_HORIZONTAL
+ private val adapterProvider = viewAdapterProviderValueId
+
+ @Test
+ fun test_regression01() {
+ setUpTest(orientation).apply {
+ val items = listOf("49", "51").toMutableList()
+ setAdapterSync(adapterProvider(items))
+ assertBasicState(0, items[0])
+
+ viewPager.post {
+ viewPager.setCurrentItem(1, true)
+ }
+
+ viewPager.post {
+ items.remove("51")
+ viewPager.adapter!!.notifyDataSetChanged()
+ }
+
+ sleep(200) // introduce some delay, follow-up with pollingCheck
+
+ PollingCheck.waitFor(2000) {
+ viewPager.scrollState == SCROLL_STATE_IDLE && viewPager.currentItem == 0
+ }
+
+ assertBasicState(0, "49")
+ }
+ }
+}
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
index 4a8f194..c3614cf 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
@@ -28,7 +28,6 @@
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
import androidx.testutils.LocaleTestUtils
-import androidx.testutils.waitForExecution
import androidx.viewpager2.widget.BaseTest.Context.SwipeMethod
import androidx.viewpager2.widget.FakeDragTest.Event.OnPageScrollStateChangedEvent
import androidx.viewpager2.widget.FakeDragTest.Event.OnPageScrolledEvent
@@ -400,12 +399,7 @@
) {
val initialPage = test.viewPager.currentItem
- tryNTimes(3, { /* RESET block */
- test.viewPager.setCurrentItemSync(initialPage, false, 2, SECONDS)
- // VP2 was potentially settling while the RetryException was raised, in which case we
- // must wait until the IDLE event has been fired
- activityTestRule.waitForExecution(1)
- }) { /* TRY block */
+ tryNTimes(3, resetBlock = { test.resetViewPagerTo(initialPage) }) {
val recorder = test.viewPager.addNewRecordingCallback()
// start smooth scroll
@@ -508,12 +502,7 @@
val initialPage = test.viewPager.currentItem
val expectedFinalPage = initialPage + 1
- tryNTimes(3, resetBlock = {
- test.viewPager.setCurrentItemSync(initialPage, false, 2, SECONDS)
- // VP2 was potentially settling while the RetryException was raised,
- // in which case we must wait until the IDLE event has been fired
- activityTestRule.waitForExecution(1)
- }) {
+ tryNTimes(3, resetBlock = { test.resetViewPagerTo(initialPage) }) {
val recorder = test.viewPager.addNewRecordingCallback()
// start fake drag
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
index d8afaac..da60cc2 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
@@ -26,7 +26,6 @@
import androidx.test.filters.LargeTest
import androidx.testutils.LocaleTestUtils
import androidx.testutils.PollingCheck
-import androidx.testutils.waitForExecution
import androidx.viewpager.widget.ViewPager
import androidx.viewpager2.test.ui.SparseAdapter
import androidx.viewpager2.widget.BaseTest.Context.SwipeMethod
@@ -706,8 +705,7 @@
// when
tryNTimes(3, resetBlock = {
- test.viewPager.setCurrentItemSync(currentPage, false, 2, SECONDS)
- activityTestRule.waitForExecution(1)
+ test.resetViewPagerTo(currentPage)
test.viewPager.unregisterOnPageChangeCallback(recorder)
recorder = test.viewPager.addNewRecordingCallback()
}) {
@@ -1032,99 +1030,6 @@
recorder.assertAllPagesSelected(listOf(targetPage, targetPage + 1))
}
- @Test
- fun test_removeFirstVisibleItemWhileScrolling_targetNotBound() {
- test_removeFirstVisibleItemWhileScrolling(false)
- }
-
- @Test
- fun test_removeFirstVisibleItemWhileScrolling_targetBound() {
- test_removeFirstVisibleItemWhileScrolling(true)
- }
-
- /** @param targetBound If the target page should be bound by RV when modifying the dataset */
- private fun test_removeFirstVisibleItemWhileScrolling(targetBound: Boolean) {
- // given
- val pageCount = 10 // number of pages
- val initialPage = 0 // page we start at
- val targetPage = 5 // page we smooth scroll to
- val removeItemMark = 1 // id of the mark we make when modifying the dataset
- val bindThreshold = 2 // how many pages from x before x gets bound?
- val epsilon = 0.001f
- // start and end of the window of opportunity to modify the dataset
- val windowStart = targetPage - bindThreshold - if (targetBound) 0 else 1
- val windowEnd = targetPage - if (targetBound) 0 else bindThreshold
-
- val test = setUpTest(config.orientation)
- activityTestRule.runOnUiThread { test.viewPager.offscreenPageLimit = 1 }
- val dataSet = stringSequence(pageCount).toMutableList()
- test.setAdapterSync(viewAdapterProvider(dataSet))
-
- tryNTimes(3, resetBlock = {
- test.viewPager.setCurrentItemSync(initialPage, false, 2, SECONDS)
- // VP2 was potentially settling while the RetryException was raised,
- // in which case we must wait until the IDLE event has been fired
- activityTestRule.waitForExecution(1)
- }) {
- // when we are scrolling to the target
- val recorder = test.viewPager.addNewRecordingCallback()
- val distanceLatch = test.viewPager.addWaitForDistanceToTarget(targetPage,
- targetPage - windowStart - epsilon)
- val idleLatch = test.viewPager.addWaitForIdleLatch()
- test.runOnUiThreadSync { test.viewPager.setCurrentItem(targetPage, true) }
- distanceLatch.await(2, SECONDS)
-
- // and we remove the first visible item
- test.modifyDataSetSync {
- val lastScrollPosition = recorder.scrollEvents.last().let {
- it.position + it.positionOffset.toDouble()
- }
- if (lastScrollPosition >= windowEnd) {
- throw RetryException("Data set should be modified while scrolling through " +
- "($windowStart, $windowEnd), but was modified at $lastScrollPosition")
- }
- recorder.markEvent(removeItemMark)
- val position = test.viewPager.linearLayoutManager.findFirstVisibleItemPosition()
- dataSet.removeAt(position)
- test.viewPager.adapter!!.notifyItemRemoved(position)
- }
- idleLatch.await(2, SECONDS)
-
- // then
- val expectedFinalPage = targetPage - if (targetBound) 1 else 0
- val expectedFinalPageText = expectedFinalPage + 1
- test.assertBasicState(expectedFinalPage, "$expectedFinalPageText")
- recorder.apply {
- val removeItemMarkIx = markerIx(removeItemMark)
- val expectedSelectEvents = listOf(targetPage).plus(
- if (targetPage != expectedFinalPage) listOf(expectedFinalPage) else emptyList()
- )
- // verify all events
- assertThat(settlingIx, equalTo(0))
- assertThat(pageSelectedIx(targetPage), equalTo(1))
- assertThat(removeItemMarkIx, greaterThan(1))
- assertThat(idleIx, equalTo(lastIx))
- assertThat(selectEvents.map { it.position }, equalTo(expectedSelectEvents))
- assertThat(scrollEventCount, equalTo(eventCount - 3 - expectedSelectEvents.size))
-
- // verify scroll events _before_ and _after_ the marker
- val scrollsBeforeMarker = scrollEventsBefore(removeItemMarkIx)
- val scrollsAfterMarker = scrollEventsAfter(removeItemMarkIx)
- listOf(scrollsBeforeMarker, scrollsAfterMarker).forEach {
- it.assertPositionSorted(SortOrder.ASC)
- it.assertOffsetSorted(SortOrder.ASC)
- it.assertValueSanity(0, targetPage, test.viewPager.pageSize)
- }
- // Only check assertMaxShownPages on scroll events _before_ the marker:
- // after the data set change, it can scroll an arbitrary number of pages
- scrollsBeforeMarker.assertMaxShownPages()
- // Only check assertLastCorrect on scroll events _after_ the marker:
- // the target is not reached before the data set change
- scrollsAfterMarker.assertLastCorrect(expectedFinalPage)
- }
- }
- }
-
private fun ViewPager2.addNewRecordingCallback(): RecordingCallback {
return RecordingCallback().also { registerOnPageChangeCallback(it) }
}
@@ -1170,7 +1075,6 @@
val draggingIx get() = events.indexOf(OnPageScrollStateChangedEvent(SCROLL_STATE_DRAGGING))
val idleIx get() = events.indexOf(OnPageScrollStateChangedEvent(SCROLL_STATE_IDLE))
val pageSelectedIx: (page: Int) -> Int = { events.indexOf(OnPageSelectedEvent(it)) }
- val markerIx: (id: Int) -> Int = { events.indexOf(MarkerEvent(it)) }
val scrollEventsBefore: (ix: Int) -> List<OnPageScrolledEvent> =
{ scrollEventsBetween(0, it) }
diff --git a/viewpager2/src/androidTest/res/layout/item_test_layout.xml b/viewpager2/src/androidTest/res/layout/item_test_layout.xml
index 74a0e03..ab051e4 100644
--- a/viewpager2/src/androidTest/res/layout/item_test_layout.xml
+++ b/viewpager2/src/androidTest/res/layout/item_test_layout.xml
@@ -20,4 +20,5 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/primary_text_default_material_light"
+ android:textSize="40sp"
android:gravity="center"/>
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
index fa9f235..456786f 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
@@ -78,6 +78,7 @@
private int mTarget;
private boolean mDispatchSelected;
private boolean mScrollHappened;
+ private boolean mDataSetChangeHappened;
private boolean mFakeDragging;
ScrollEventAdapter(@NonNull ViewPager2 viewPager) {
@@ -96,6 +97,7 @@
mDispatchSelected = false;
mScrollHappened = false;
mFakeDragging = false;
+ mDataSetChangeHappened = false;
}
/**
@@ -125,10 +127,10 @@
}
// Drag is finished (dragging || settling -> idle)
- if (mAdapterState != STATE_IDLE && newState == RecyclerView.SCROLL_STATE_IDLE) {
+ if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_IDLE) {
boolean dispatchIdle = false;
updateScrollEventValues();
- if (!mScrollHappened && isInAnyDraggingState()) {
+ if (!mScrollHappened) {
// Pages didn't move during drag, so either we're at the start or end of the list,
// or there are no pages at all.
// In the first case, ViewPager's contract requires at least one scroll event.
@@ -137,19 +139,11 @@
dispatchScrolled(mScrollValues.mPosition, 0f, 0);
}
dispatchIdle = true;
- } else if (mScrollHappened && mScrollValues.mOffsetPx == 0) {
- // Normally we dispatch the selected page and go to idle in onScrolled after we
- // settled (mOffsetPx == 0), but there are a few exceptions:
- //
- // 1) The drag was still ongoing when onScrolled was called, so we didn't know it
- // was about to end. End it now.
- // 2) If the adapter data set changes during a smooth scroll, RecyclerView may
- // settle at a different position, which we don't know about until we're there.
- // End it now.
- //
- // Now RecyclerView is idle and mOffsetPx == 0, so the view has stabilized. Fire
- // onPageSelected to notify clients of the position settled upon and go to idle.
- //
+ } else if (mScrollValues.mOffsetPx == 0) {
+ // Normally we dispatch the selected page and go to idle in onScrolled when
+ // mOffsetPx == 0, but in this case the drag was still ongoing when onScrolled was
+ // called, so that didn't happen. And since mOffsetPx == 0, there will be no further
+ // scroll events, so fire the onPageSelected event and go to idle now.
// Note that if we _did_ go to idle in that last onScrolled event, this code will
// not be executed because mAdapterState has been reset to STATE_IDLE.
dispatchIdle = true;
@@ -164,6 +158,19 @@
resetState();
}
}
+
+ if (mAdapterState == STATE_IN_PROGRESS_SMOOTH_SCROLL
+ && newState == RecyclerView.SCROLL_STATE_IDLE && mDataSetChangeHappened) {
+ updateScrollEventValues();
+ if (mScrollValues.mOffsetPx == 0) {
+ if (mTarget != mScrollValues.mPosition) {
+ dispatchSelected(
+ mScrollValues.mPosition == NO_POSITION ? 0 : mScrollValues.mPosition);
+ }
+ dispatchStateChanged(SCROLL_STATE_IDLE);
+ resetState();
+ }
+ }
}
/**
@@ -287,6 +294,10 @@
dispatchStateChanged(SCROLL_STATE_DRAGGING);
}
+ void notifyDataSetChangeHappened() {
+ mDataSetChangeHappened = true;
+ }
+
/**
* Let the adapter know a programmatic scroll was initiated.
*/
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
index 7c71ca7..bd4247e 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
@@ -134,6 +134,7 @@
@Override
public void onChanged() {
mCurrentItemDirty = true;
+ mScrollEventAdapter.notifyDataSetChangeHappened();
}
};
@@ -142,7 +143,7 @@
private Parcelable mPendingAdapterState;
private RecyclerView mRecyclerView;
private PagerSnapHelper mPagerSnapHelper;
- private ScrollEventAdapter mScrollEventAdapter;
+ ScrollEventAdapter mScrollEventAdapter;
private CompositeOnPageChangeCallback mPageChangeEventDispatcher;
private FakeDrag mFakeDragger;
private PageTransformerAdapter mPageTransformerAdapter;
diff --git a/webkit/build.gradle b/webkit/build.gradle
index 622bf29..8c0c18d 100644
--- a/webkit/build.gradle
+++ b/webkit/build.gradle
@@ -26,7 +26,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0")
+ api("androidx.core:core:1.1.0-rc01")
androidTestImplementation(OKHTTP_MOCKWEBSERVER)
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcherTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcherTest.java
index 620307f..95b6d4c 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcherTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcherTest.java
@@ -567,6 +567,7 @@
@Test
public void testConstraintsChanged_withFutureWork() throws InterruptedException {
+ when(mBatteryChargingTracker.getInitialState()).thenReturn(true);
// Use a mocked scheduler in this test.
Scheduler scheduler = mock(Scheduler.class);
doCallRealMethod().when(mWorkManager).rescheduleEligibleWork();
diff --git a/work/workmanager/src/main/java/androidx/work/ListenableWorker.java b/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
index 2203618..41ac5d0 100644
--- a/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
+++ b/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
@@ -180,6 +180,9 @@
* A ListenableWorker is given a maximum of ten minutes to finish its execution and return a
* {@link Result}. After this time has expired, the worker will be signalled to stop and its
* {@link ListenableFuture} will be cancelled.
+ * <p>
+ * The future will also be cancelled if this worker is stopped for any reason
+ * (see {@link #onStopped()}).
*
* @return A {@link ListenableFuture} with the {@link Result} of the computation. If you
* cancel this Future, WorkManager will treat this unit of work as failed.
@@ -222,12 +225,13 @@
}
/**
- * This method is invoked when this Worker has been told to stop. This could happen due
- * to an explicit cancellation signal by the user, or because the system has decided to preempt
- * the task. In these cases, the results of the work will be ignored by WorkManager. All
- * processing in this method should be lightweight - there are no contractual guarantees about
- * which thread will invoke this call, so this should not be a long-running or blocking
- * operation.
+ * This method is invoked when this Worker has been told to stop. At this point, the
+ * {@link ListenableFuture} returned by the instance of {@link #startWork()} is
+ * also cancelled. This could happen due to an explicit cancellation signal by the user, or
+ * because the system has decided to preempt the task. In these cases, the results of the
+ * work will be ignored by WorkManager. All processing in this method should be lightweight
+ * - there are no contractual guarantees about which thread will invoke this call, so this
+ * should not be a long-running or blocking operation.
*/
public void onStopped() {
// Do nothing by default.