Merge "Return full classpath from compile files helper" into androidx-main
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 55ed0c2..ad99001 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -75,6 +75,7 @@
cacheableRuntimeOnly(libs.hiltAndroidGradlePlugin)
// room kotlintestapp uses the ksp plugin but it does not publish a plugin marker yet
cacheableApi(libs.kspGradlePlugin)
+ cacheableApi(libs.japicmpPlugin)
// dependencies whose resolutions we don't need to cache
compileOnly(findGradleKotlinDsl()) // Only one file in this configuration, no need to cache it
implementation(project("jetpad-integration")) // Doesn't have a .pom, so not slow to load
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index 7818ed14..013873a 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -112,6 +112,12 @@
*/
const val VERIFY_UP_TO_DATE = "androidx.verifyUpToDate"
+/**
+ * If true, we are building in GitHub and should enable build features related to KMP.
+ * If false, we are in AOSP, where not all KMP features are enabled.
+ */
+const val KMP_GITHUB_BUILD = "androidx.kmp.github.build"
+
val ALL_ANDROIDX_PROPERTIES = setOf(
ALL_WARNINGS_AS_ERRORS,
ALTERNATIVE_PROJECT_URL,
@@ -131,7 +137,8 @@
AffectedModuleDetector.BASE_COMMIT_ARG,
PLAYGROUND_SNAPSHOT_BUILD_ID,
PLAYGROUND_METALAVA_BUILD_ID,
- PLAYGROUND_DOKKA_BUILD_ID
+ PLAYGROUND_DOKKA_BUILD_ID,
+ KMP_GITHUB_BUILD
)
/**
diff --git a/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt b/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
index fb31998..95df021 100644
--- a/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
@@ -24,7 +24,7 @@
object SupportConfig {
const val DEFAULT_MIN_SDK_VERSION = 14
const val INSTRUMENTATION_RUNNER = "androidx.test.runner.AndroidJUnitRunner"
- const val BUILD_TOOLS_VERSION = "30.0.2"
+ const val BUILD_TOOLS_VERSION = "30.0.3"
val NDK_VERSION by lazy {
// TODO(aurimas) b/173737578 remove when we no longer have divergent versions
when (getOperatingSystem()) {
diff --git a/buildSrc/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt b/buildSrc/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
index 5404b4f..d166949 100644
--- a/buildSrc/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
@@ -62,6 +62,7 @@
"generateMetadataFileForDesktopPublication",
"generateMetadataFileForJvmPublication",
"generateMetadataFileForJvmlinux-x64Publication",
+ "generateMetadataFileForJvmlinux-arm64Publication",
"generateMetadataFileForJvmmacos-x64Publication",
"generateMetadataFileForJvmmacos-arm64Publication",
"generateMetadataFileForJvmwindows-x64Publication",
@@ -75,6 +76,7 @@
"generatePomFileForAndroidReleasePublication",
"generatePomFileForDesktopPublication",
"generatePomFileForJvmlinux-x64Publication",
+ "generatePomFileForJvmlinux-arm64Publication",
"generatePomFileForJvmmacos-x64Publication",
"generatePomFileForJvmmacos-arm64Publication",
"generatePomFileForJvmwindows-x64Publication",
@@ -93,6 +95,7 @@
"publishDesktopPublicationToMavenRepository",
"publishJvmPublicationToMavenRepository",
"publishJvmlinux-x64PublicationToMavenRepository",
+ "publishJvmlinux-arm64PublicationToMavenRepository",
"publishJvmmacos-x64PublicationToMavenRepository",
"publishJvmmacos-arm64PublicationToMavenRepository",
"publishJvmwindows-x64PublicationToMavenRepository",
diff --git a/busytown/impl/build.sh b/busytown/impl/build.sh
index 09b1e5f..bc11e7d 100755
--- a/busytown/impl/build.sh
+++ b/busytown/impl/build.sh
@@ -74,7 +74,8 @@
if [ "$DIAGNOSE" == "true" ]; then
# see if diagnose-build-failure.sh can identify the root cauase
echo "running diagnose-build-failure.sh, see build.log" >&2
- ./development/diagnose-build-failure/diagnose-build-failure.sh "--ci saveSystemStats $*"
+ # specify a timeout in case we're running on a remote server, so we don't take too long
+ ./development/diagnose-build-failure/diagnose-build-failure.sh --timeout 14400 "--ci saveSystemStats $*"
fi
exit 1
fi
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
index ec2d453..65a65c1 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
@@ -20,10 +20,8 @@
import android.util.Log
import android.util.Size
import androidx.camera.camera2.Camera2Config
-import androidx.camera.camera2.pipe.integration.CameraPipeConfig
import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraX
-import androidx.camera.core.CameraXConfig
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.internal.CameraUseCaseAdapter
import androidx.camera.testing.CameraUtil
@@ -53,8 +51,7 @@
@LargeTest
@RunWith(Parameterized::class)
class MLKitBarcodeTest(
- private val resolution: Size,
- private val cameraConfig: CameraXConfig
+ private val resolution: Size
) {
@get:Rule
@@ -70,12 +67,7 @@
private val size720p = Size(1280, 720)
@JvmStatic
@Parameterized.Parameters
- fun data() = listOf(
- arrayOf(size480p, Camera2Config.defaultConfig()),
- arrayOf(size720p, Camera2Config.defaultConfig()),
- arrayOf(size480p, CameraPipeConfig.defaultConfig()),
- arrayOf(size720p, CameraPipeConfig.defaultConfig())
- )
+ fun data() = listOf(size480p, size720p)
}
private val context: Context = ApplicationProvider.getApplicationContext()
@@ -85,7 +77,7 @@
@Before
fun setup() {
- CameraX.initialize(context, cameraConfig).get(10, TimeUnit.SECONDS)
+ CameraX.initialize(context, Camera2Config.defaultConfig()).get(10, TimeUnit.SECONDS)
barcodeScanner = BarcodeScanning.getClient(
BarcodeScannerOptions.Builder().setBarcodeFormats(FORMAT_QR_CODE).build()
@@ -135,7 +127,7 @@
}
private fun assertBarcodeDetect(imageAnalysis: ImageAnalysis) {
- val latchForBarcodeDetect = CountDownLatch(4)
+ val latchForBarcodeDetect = CountDownLatch(2)
imageAnalysis.setAnalyzer(
Dispatchers.Main.asExecutor(),
@@ -150,8 +142,8 @@
barcodes.forEach {
if ("Hi, CamX!" == it.displayValue) {
latchForBarcodeDetect.countDown()
- Log.d(TAG, "barcode display value: {${it.displayValue}} ")
}
+ Log.d(TAG, "barcode display value: {${it.displayValue}} ")
}
}
.addOnFailureListener { exception ->
@@ -167,7 +159,12 @@
)
// Verify it is the CameraX lab test environment and can detect qr-code.
- assertTrue(latchForBarcodeDetect.await(DETECT_TIMEOUT, TimeUnit.MILLISECONDS))
+ assertTrue(
+ "Fail to detect qrcode, resolution: $resolution, " +
+ "rearCameraE2E: ${isLoggable(true)}, " +
+ "frontCameraE2E: ${isLoggable(false)} ",
+ latchForBarcodeDetect.await(DETECT_TIMEOUT, TimeUnit.MILLISECONDS)
+ )
}
private fun initImageAnalysis(): ImageAnalysis {
@@ -176,4 +173,12 @@
.setTargetResolution(resolution)
.build()
}
+
+ private fun isLoggable(isRear: Boolean): Boolean {
+ return if (isRear) {
+ Log.isLoggable("rearCameraE2E", Log.DEBUG)
+ } else {
+ Log.isLoggable("frontCameraE2E", Log.DEBUG)
+ }
+ }
}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
index c351703..a682570 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
@@ -313,11 +313,11 @@
*/
@VisibleForTesting
static class MileageListener implements OnCarPropertyResponseListener {
- private final OnCarDataAvailableListener<Mileage> mMileageOnCarDataListener;
+ private final OnCarDataAvailableListener<Mileage> mMileageOnCarDataAvailableListener;
private final Executor mExecutor;
MileageListener(OnCarDataAvailableListener<Mileage> listener, Executor executor) {
- mMileageOnCarDataListener = listener;
+ mMileageOnCarDataAvailableListener = listener;
mExecutor = executor;
}
@@ -355,7 +355,7 @@
new Mileage.Builder().setOdometerMeters(
odometerValue).setDistanceDisplayUnit(
distanceDisplayUnitValue).build();
- mMileageOnCarDataListener.onCarDataAvailable(mileage);
+ mMileageOnCarDataAvailableListener.onCarDataAvailable(mileage);
});
}
}
@@ -366,14 +366,15 @@
*/
@VisibleForTesting
static class EnergyLevelListener implements OnCarPropertyResponseListener {
- private final OnCarDataAvailableListener<EnergyLevel> mEnergyLevelOnCarDataListener;
+ private final OnCarDataAvailableListener<EnergyLevel>
+ mEnergyLevelOnCarDataAvailableListener;
private final Executor mExecutor;
private float mEvBatteryCapacity;
private float mFuelCapacity;
EnergyLevelListener(OnCarDataAvailableListener<EnergyLevel> listener, Executor executor,
float evBatteryCapacity, float fuelCapacity) {
- mEnergyLevelOnCarDataListener = listener;
+ mEnergyLevelOnCarDataAvailableListener = listener;
mExecutor = executor;
mEvBatteryCapacity = evBatteryCapacity;
mFuelCapacity = fuelCapacity;
@@ -438,7 +439,7 @@
energyIsLowValue).setRangeRemainingMeters(
rangeRemainingValue).setDistanceDisplayUnit(
distanceDisplayUnitValue).build();
- mEnergyLevelOnCarDataListener.onCarDataAvailable(energyLevel);
+ mEnergyLevelOnCarDataAvailableListener.onCarDataAvailable(energyLevel);
});
}
}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java
index 50a8995..cc23f92 100644
--- a/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java
@@ -23,8 +23,10 @@
import androidx.car.app.annotations.CarProtocol;
import androidx.car.app.annotations.RequiresCarApi;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/** Units such as speed and distance for car hardware measurements and display. */
@CarProtocol
@@ -42,6 +44,7 @@
MILE,
})
@Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE})
@RestrictTo(LIBRARY)
public @interface CarDistanceUnit {
}
@@ -73,6 +76,7 @@
MILES_PER_HOUR,
})
@Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE})
@RestrictTo(LIBRARY)
public @interface CarSpeedUnit {
}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java
index 621b0da..480eae7 100644
--- a/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java
@@ -15,6 +15,8 @@
*/
package androidx.car.app.hardware.info;
+import static androidx.car.app.hardware.common.CarUnit.CarDistanceUnit;
+
import static java.util.Objects.requireNonNull;
import androidx.annotation.Keep;
@@ -55,7 +57,7 @@
@Keep
@NonNull
- private final CarValue<Integer> mDistanceDisplayUnit;
+ private final CarValue<@CarDistanceUnit Integer> mDistanceDisplayUnit;
/** Returns the battery percentage remaining from the car hardware. */
@NonNull
@@ -85,6 +87,7 @@
}
// TODO(b/192106888): Remove when new values fully supported by Android Auto Host.
+
/**
* Returns the range remaining from the car hardware in meters.
*
@@ -102,7 +105,7 @@
* <p>See {@link CarUnit} for possible distance values.
*/
@NonNull
- public CarValue<Integer> getDistanceDisplayUnit() {
+ public CarValue<@CarDistanceUnit Integer> getDistanceDisplayUnit() {
return requireNonNull(mDistanceDisplayUnit);
}
@@ -171,7 +174,8 @@
CarValue<Float> mFuelPercent = CarValue.UNIMPLEMENTED_FLOAT;
CarValue<Boolean> mEnergyIsLow = CarValue.UNIMPLEMENTED_BOOLEAN;
CarValue<Float> mRangeRemainingMeters = CarValue.UNIMPLEMENTED_FLOAT;
- CarValue<Integer> mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+ CarValue<@CarDistanceUnit Integer> mDistanceDisplayUnit =
+ CarValue.UNIMPLEMENTED_INTEGER;
/** Sets the remaining batter percentage. */
@NonNull
@@ -236,7 +240,8 @@
* @throws NullPointerException if {@code distanceDisplayUnit} is {@code null}
*/
@NonNull
- public Builder setDistanceDisplayUnit(@NonNull CarValue<Integer> distanceDisplayUnit) {
+ public Builder setDistanceDisplayUnit(
+ @NonNull CarValue<@CarDistanceUnit Integer> distanceDisplayUnit) {
mDistanceDisplayUnit = requireNonNull(distanceDisplayUnit);
return this;
}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java
index 948800f..4453c32 100644
--- a/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java
@@ -28,8 +28,10 @@
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.hardware.common.CarValue;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.List;
import java.util.Objects;
@@ -59,8 +61,10 @@
EVCONNECTOR_TYPE_OTHER,
})
@Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE})
@RestrictTo(LIBRARY)
- public @interface EvConnectorType {}
+ public @interface EvConnectorType {
+ }
/** Unknown connector type. */
@EvConnectorType
@@ -137,65 +141,80 @@
FUEL_TYPE_OTHER,
})
@Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE})
@RestrictTo(LIBRARY)
- public @interface FuelType {}
+ public @interface FuelType {
+ }
/** Unknown fuel type */
- @FuelType public static final int FUEL_TYPE_UNKNOWN = 0;
+ @FuelType
+ public static final int FUEL_TYPE_UNKNOWN = 0;
/** Unleaded gasoline */
- @FuelType public static final int FUEL_TYPE_UNLEADED = 1;
+ @FuelType
+ public static final int FUEL_TYPE_UNLEADED = 1;
/** Leaded gasoline */
- @FuelType public static final int FUEL_TYPE_LEADED = 2;
+ @FuelType
+ public static final int FUEL_TYPE_LEADED = 2;
/** #1 Grade Diesel */
- @FuelType public static final int FUEL_TYPE_DIESEL_1 = 3;
+ @FuelType
+ public static final int FUEL_TYPE_DIESEL_1 = 3;
/** #2 Grade Diesel */
- @FuelType public static final int FUEL_TYPE_DIESEL_2 = 4;
+ @FuelType
+ public static final int FUEL_TYPE_DIESEL_2 = 4;
/** Biodiesel */
- @FuelType public static final int FUEL_TYPE_BIODIESEL = 5;
+ @FuelType
+ public static final int FUEL_TYPE_BIODIESEL = 5;
/** 85% ethanol/gasoline blend */
- @FuelType public static final int FUEL_TYPE_E85 = 6;
+ @FuelType
+ public static final int FUEL_TYPE_E85 = 6;
/** Liquified petroleum gas */
- @FuelType public static final int FUEL_TYPE_LPG = 7;
+ @FuelType
+ public static final int FUEL_TYPE_LPG = 7;
/** Compressed natural gas */
- @FuelType public static final int FUEL_TYPE_CNG = 8;
+ @FuelType
+ public static final int FUEL_TYPE_CNG = 8;
/** Liquified natural gas */
- @FuelType public static final int FUEL_TYPE_LNG = 9;
+ @FuelType
+ public static final int FUEL_TYPE_LNG = 9;
/** Electric */
- @FuelType public static final int FUEL_TYPE_ELECTRIC = 10;
+ @FuelType
+ public static final int FUEL_TYPE_ELECTRIC = 10;
/** Hydrogen fuel cell */
- @FuelType public static final int FUEL_TYPE_HYDROGEN = 11;
+ @FuelType
+ public static final int FUEL_TYPE_HYDROGEN = 11;
/** Fuel type to use when no other types apply. */
- @FuelType public static final int FUEL_TYPE_OTHER = 12;
+ @FuelType
+ public static final int FUEL_TYPE_OTHER = 12;
@Keep
@NonNull
- private final CarValue<List<Integer>> mEvConnectorTypes;
+ private final CarValue<List<@EvConnectorType Integer>> mEvConnectorTypes;
@Keep
@NonNull
- private final CarValue<List<Integer>> mFuelTypes;
+ private final CarValue<List<@FuelType Integer>> mFuelTypes;
/**
- * Returns an array of the available EV connectors.
+ * Returns an array of the available EV connectors.
*
- * <p>If a vehicle does not know the EV connector type it will return
- * {@link #EVCONNECTOR_TYPE_UNKNOWN} or {@link CarValue#STATUS_UNIMPLEMENTED}. If the value
- * is known but not in the current list {@link #EVCONNECTOR_TYPE_UNKNOWN} will be returned.
+ * <p>If a vehicle does not know the EV connector type it will return
+ * {@link #EVCONNECTOR_TYPE_UNKNOWN} or {@link CarValue#STATUS_UNIMPLEMENTED}. If the value
+ * is known but not in the current list {@link #EVCONNECTOR_TYPE_UNKNOWN} will be returned.
*/
@NonNull
- public CarValue<List<Integer>> getEvConnectorTypes() {
+ public CarValue<List<@EvConnectorType Integer>> getEvConnectorTypes() {
return requireNonNull(mEvConnectorTypes);
}
/**
- * Returns an array of the available fuel types.
+ * Returns an array of the available fuel types.
*
- * <p>If a vehicle does not know the fuel type it will return {@link #FUEL_TYPE_UNKNOWN} or
- * {@link CarValue#STATUS_UNIMPLEMENTED}. If the value is known but not in the current list
- * {@link #EVCONNECTOR_TYPE_UNKNOWN} will be returned.
+ * <p>If a vehicle does not know the fuel type it will return {@link #FUEL_TYPE_UNKNOWN} or
+ * {@link CarValue#STATUS_UNIMPLEMENTED}. If the value is known but not in the current list
+ * {@link #EVCONNECTOR_TYPE_UNKNOWN} will be returned.
*/
@NonNull
- public CarValue<List<Integer>> getFuelTypes() {
+ public CarValue<List<@FuelType Integer>> getFuelTypes() {
return requireNonNull(mFuelTypes);
}
@@ -237,8 +256,9 @@
/** A builder of {@link EnergyProfile}. */
public static final class Builder {
- CarValue<List<Integer>> mEvConnectorTypes = CarValue.UNIMPLEMENTED_INTEGER_LIST;
- CarValue<List<Integer>> mFuelTypes = CarValue.UNIMPLEMENTED_INTEGER_LIST;
+ CarValue<List<@EvConnectorType Integer>> mEvConnectorTypes =
+ CarValue.UNIMPLEMENTED_INTEGER_LIST;
+ CarValue<List<@FuelType Integer>> mFuelTypes = CarValue.UNIMPLEMENTED_INTEGER_LIST;
/**
* Sets the cars EV connector types.
@@ -246,7 +266,8 @@
* @throws NullPointerException if {@code evConnectorTypes} is {@code null}
*/
@NonNull
- public Builder setEvConnectorTypes(@NonNull CarValue<List<Integer>> evConnectorTypes) {
+ public Builder setEvConnectorTypes(
+ @NonNull CarValue<List<@EvConnectorType Integer>> evConnectorTypes) {
mEvConnectorTypes = requireNonNull(evConnectorTypes);
return this;
}
@@ -257,7 +278,7 @@
* @throws NullPointerException if {@code fuelTypes} is {@code null}
*/
@NonNull
- public Builder setFuelTypes(@NonNull CarValue<List<Integer>> fuelTypes) {
+ public Builder setFuelTypes(@NonNull CarValue<List<@FuelType Integer>> fuelTypes) {
mFuelTypes = requireNonNull(fuelTypes);
return this;
}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java
index aa4d440..68df7a9 100644
--- a/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java
@@ -15,6 +15,8 @@
*/
package androidx.car.app.hardware.info;
+import static androidx.car.app.hardware.common.CarUnit.CarDistanceUnit;
+
import static java.util.Objects.requireNonNull;
import androidx.annotation.Keep;
@@ -42,7 +44,7 @@
@Keep
@NonNull
- private final CarValue<Integer> mDistanceDisplayUnit;
+ private final CarValue<@CarDistanceUnit Integer> mDistanceDisplayUnit;
/** Returns the value of the odometer from the car hardware in meters. */
@NonNull
@@ -54,6 +56,7 @@
}
// TODO(b/192106888): Remove when new values fully supported by Android Auto Host.
+
/**
* Returns the value of the odometer from the car hardware in meters.
*
@@ -71,7 +74,7 @@
* <p>See {@link CarUnit} for possible distance values.
*/
@NonNull
- public CarValue<Integer> getDistanceDisplayUnit() {
+ public CarValue<@CarDistanceUnit Integer> getDistanceDisplayUnit() {
return requireNonNull(mDistanceDisplayUnit);
}
@@ -120,7 +123,8 @@
/** A builder of {@link Mileage}. */
public static final class Builder {
CarValue<Float> mOdometerMeters = CarValue.UNIMPLEMENTED_FLOAT;
- CarValue<Integer> mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+ CarValue<@CarDistanceUnit Integer> mDistanceDisplayUnit =
+ CarValue.UNIMPLEMENTED_INTEGER;
// TODO(b/192106888): Remove when new values fully supported by Android Auto Host.
@@ -156,7 +160,8 @@
* @throws NullPointerException if {@code mileageDisplayUnit} is {@code null}
*/
@NonNull
- public Builder setDistanceDisplayUnit(@NonNull CarValue<Integer> mileageDisplayUnit) {
+ public Builder setDistanceDisplayUnit(
+ @NonNull CarValue<@CarDistanceUnit Integer> mileageDisplayUnit) {
mDistanceDisplayUnit = requireNonNull(mileageDisplayUnit);
return this;
}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java
index 627b339..9290f84 100644
--- a/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java
@@ -15,6 +15,8 @@
*/
package androidx.car.app.hardware.info;
+import static androidx.car.app.hardware.common.CarUnit.CarSpeedUnit;
+
import static java.util.Objects.requireNonNull;
import androidx.annotation.Keep;
@@ -52,8 +54,8 @@
private final CarValue<Float> mDisplaySpeedMetersPerSecond;
@Keep
- @Nullable
- private final CarValue<Integer> mSpeedDisplayUnit;
+ @NonNull
+ private final CarValue<@CarSpeedUnit Integer> mSpeedDisplayUnit;
/**
* Returns the raw speed of the car in meters/second.
@@ -127,7 +129,7 @@
* <p>See {@link CarUnit} for valid speed units.
*/
@NonNull
- public CarValue<Integer> getSpeedDisplayUnit() {
+ public CarValue<@CarSpeedUnit Integer> getSpeedDisplayUnit() {
return requireNonNull(mSpeedDisplayUnit);
}
@@ -186,7 +188,7 @@
public static final class Builder {
CarValue<Float> mRawSpeedMetersPerSecond = CarValue.UNIMPLEMENTED_FLOAT;
CarValue<Float> mDisplaySpeedMetersPerSecond = CarValue.UNIMPLEMENTED_FLOAT;
- CarValue<Integer> mSpeedDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+ CarValue<@CarSpeedUnit Integer> mSpeedDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
// TODO(b/192106888): Remove when new values fully supported by Android Auto Host.
@@ -250,7 +252,8 @@
* @throws NullPointerException if {@code speedDisplayUnit} is {@code null}
*/
@NonNull
- public Builder setSpeedDisplayUnit(@NonNull CarValue<Integer> speedDisplayUnit) {
+ public Builder setSpeedDisplayUnit(
+ @NonNull CarValue<@CarSpeedUnit Integer> speedDisplayUnit) {
mSpeedDisplayUnit = requireNonNull(speedDisplayUnit);
return this;
}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/TollCard.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/TollCard.java
index bcd0e37..0feac80 100644
--- a/car/app/app/src/main/java/androidx/car/app/hardware/info/TollCard.java
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/TollCard.java
@@ -28,8 +28,10 @@
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.hardware.common.CarValue;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.Objects;
/**
@@ -51,20 +53,21 @@
TOLLCARD_STATE_NOT_INSERTED,
})
@Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE})
@RestrictTo(LIBRARY)
- public @interface State {
+ public @interface TollCardState {
}
/**
* Toll card state is unknown.
*/
- @State
+ @TollCardState
public static final int TOLLCARD_STATE_UNKNOWN = 0;
/**
* Toll card state is valid.
*/
- @State
+ @TollCardState
public static final int TOLLCARD_STATE_VALID = 1;
/**
@@ -73,7 +76,7 @@
* <p>On some vehicles this may be that the toll card is inserted but not valid while other
* vehicles might not have a toll card inserted.
*/
- @State
+ @TollCardState
public static final int TOLLCARD_STATE_INVALID = 2;
/**
@@ -81,16 +84,16 @@
*
* <p>Will be returned if the car hardware is able to detect that the card is not inserted.
*/
- @State
+ @TollCardState
public static final int TOLLCARD_STATE_NOT_INSERTED = 3;
@Keep
@NonNull
- private final CarValue<Integer> mCardState;
+ private final CarValue<@TollCardState Integer> mCardState;
/** Returns the toll card state if available. */
@NonNull
- public CarValue<Integer> getCardState() {
+ public CarValue<@TollCardState Integer> getCardState() {
return requireNonNull(mCardState);
}
@@ -129,7 +132,7 @@
/** A builder of {@link TollCard}. */
public static final class Builder {
- CarValue<Integer> mCardState = CarValue.UNIMPLEMENTED_INTEGER;
+ CarValue<@TollCardState Integer> mCardState = CarValue.UNIMPLEMENTED_INTEGER;
/**
* Sets the toll card state.
@@ -137,7 +140,7 @@
* @throws NullPointerException if {@code cardState} is {@code null}
*/
@NonNull
- public Builder setCardState(@NonNull CarValue<Integer> cardState) {
+ public Builder setCardState(@NonNull CarValue<@TollCardState Integer> cardState) {
mCardState = requireNonNull(cardState);
return this;
}
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
index 0b58d66..a95b950 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
@@ -86,14 +86,14 @@
* and touchpads, and calls the appropriate {@link SurfaceCallback} methods. Respond to the user
* action to enter or exit the pan mode via {@link Builder#setPanModeListener(PanModeListener)}.
*
- * If the app does not include this button in the map {@link ActionStrip}, the app will not
+ * <p>If the app does not include this button in the map {@link ActionStrip}, the app will not
* receive the user input for panning gestures from {@link SurfaceCallback} methods, and the host
* will exit any previously activated pan mode.
*
- * The host may hide the pan button in some head units in which the user does not need it. Also, the
- * host may hide other UI components in the template while the user is in the pan mode.
+ * <p>The host may hide the pan button in some head units in which the user does not need it.
+ * Also, the host may hide other UI components in the template while the user is in the pan mode.
*
- * Note that not all head units support touch gestures, and not all touch screens support
+ * <p>Note that not all head units support touch gestures, and not all touch screens support
* multi-touch gestures. Therefore, some {@link SurfaceCallback} methods may not be called in
* some cars. In order to support different head units, use the buttons in the map action strip
* to provide necessary functionality, such as the zoom-in and zoom-out buttons.
diff --git a/collection/collection2/build.gradle b/collection/collection2/build.gradle
index 62f7c17..833417e 100644
--- a/collection/collection2/build.gradle
+++ b/collection/collection2/build.gradle
@@ -14,27 +14,168 @@
* limitations under the License.
*/
+import androidx.build.LibraryGroups
+import me.champeau.gradle.japicmp.JapicmpTask
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.Publish
+import androidx.build.RunApiTasks
+
+buildscript {
+ dependencies {
+ classpath(libs.japicmpPlugin)
+ classpath(libs.atomicFu)
+ }
+}
+
plugins {
id("AndroidXPlugin")
id("org.jetbrains.kotlin.multiplatform")
+ id("me.champeau.gradle.japicmp")
}
+// This should be true when building from GitHub, and false when building
+// from AOSP. Use this to block out any features or code that we're not
+// ready to build yet in AOSP
+
+def githubBuild = project.properties['androidx.github.build'] ?: false
+
kotlin {
+
jvm {
withJava()
}
+ if (githubBuild) {
+ js {
+ nodejs()
+ }
+ macosX64 {
+ binaries {
+ framework {
+ baseName = "CollectionKMP"
+ }
+ }
+ }
+
+ linuxX64()
+ mingwX64()
+ macosX64()
+ iosX64()
+ iosArm64()
+ iosArm32()
+ }
sourceSets {
+ all {
+ languageSettings {
+ progressiveMode = true
+ useExperimentalAnnotation("kotlin.ExperimentalStdlibApi") // STOPSHIP
+ }
+ }
commonMain {
dependencies {
api(libs.kotlinStdlibCommon)
}
}
+ commonTest {
+ dependencies {
+ implementation(libs.kotlinTestCommon)
+ implementation(libs.kotlinTestAnnotationsCommon)
+ }
+ }
+
+ if (githubBuild) {
+ nonJvmMain {
+ // Used for JS and all native targets.
+ dependencies {
+ api("org.jetbrains.kotlinx:atomicfu:$atomicfu_version")
+ }
+ }
+
+ configure([macosX64Main, linuxX64Main, mingwX64Main, iosX64Main, iosArm32Main, iosArm64Main]) {
+ dependsOn nonJvmMain
+ }
+ }
+
+ nativeTest // Used for all native targets.
+ configure([macosX64Test, linuxX64Test, mingwX64Test, iosX64Test, iosArm32Test, iosArm64Test]) {
+ dependsOn nativeTest
+ }
+
+ if (githubBuild) {
+ jsMain {
+ dependsOn nonJvmMain
+ dependencies {
+ api(libs.kotlinStdlibJs)
+ }
+ }
+ jsTest {
+ dependencies {
+ implementation(libs.kotlinTestJs)
+ }
+ }
+ }
jvmMain {
dependencies {
api(libs.kotlinStdlib)
api("androidx.annotation:annotation:1.1.0")
}
}
+ jvmTest {
+ dependencies {
+ implementation(libs.kotlinTestJunit)
+ implementation(libs.truth)
+ }
+ }
}
-}
\ No newline at end of file
+}
+
+if (githubBuild) {
+ // Workaround for https://youtrack.jetbrains.com/issue/KT-31603
+ sourceSets.test.java.srcDir('../collection/src/test/java')
+}
+
+androidx {
+ name = "Android Support Library collections"
+ if (githubBuild) {
+ publish = Publish.SNAPSHOT_AND_RELEASE
+ } else {
+ publish = Publish.NONE
+ }
+ mavenGroup = LibraryGroups.COLLECTION
+ inceptionYear = "2020"
+ description = "Standalone efficient collections."
+ legacyDisableKotlinStrictApiMode = true // TODO: Re-enable this!
+ runApiTasks = new RunApiTasks.No("metalava issues prevent api checks on kmp: b/188171162")
+}
+
+repositories {
+ mavenCentral()
+}
+
+configurations {
+ jvmApiBaseline
+}
+
+dependencies {
+ jvmApiBaseline('androidx.collection:collection:1.1.0') {
+ transitive = false
+ force = true
+ }
+}
+
+def japicmpProvider = tasks.register("japicmp", JapicmpTask) { task ->
+ def jvmJar = tasks.getByName("jvmJar")
+ dependsOn(jvmJar)
+
+ task.oldClasspath = configurations.jvmApiBaseline
+ task.newClasspath = files(jvmJar.archivePath)
+ task.onlyBinaryIncompatibleModified = true
+ task.failOnModification = true
+ task.ignoreMissingClasses = true
+ task.includeSynthetic = true
+ task.txtOutputFile = file("$buildDir/reports/japi.txt")
+}
+
+// TODO(b/190741472): runErrorProne is currently broken.
+runErrorProne.enabled = false
diff --git a/compose/animation/animation-core/api/current.ignore b/compose/animation/animation-core/api/current.ignore
index 2650543..612e66b 100644
--- a/compose/animation/animation-core/api/current.ignore
+++ b/compose/animation/animation-core/api/current.ignore
@@ -1,13 +1,3 @@
// Baseline format: 1.0
-ChangedClass: androidx.compose.animation.core.Transition.Segment:
- Class androidx.compose.animation.core.Transition.Segment changed class/interface declaration
-
-
-RemovedClass: androidx.compose.animation.core.ManualFrameClock:
- Removed class androidx.compose.animation.core.ManualFrameClock
-RemovedClass: androidx.compose.animation.core.ManualFrameClockKt:
- Removed class androidx.compose.animation.core.ManualFrameClockKt
-
-
-RemovedMethod: androidx.compose.animation.core.Transition.Segment#Segment(S, S):
- Removed constructor androidx.compose.animation.core.Transition.Segment(S,S)
+RemovedInterface: androidx.compose.animation.core.InfiniteAnimationPolicy:
+ Removed class androidx.compose.animation.core.InfiniteAnimationPolicy
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index 2b5c962..e8cb77f 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -328,16 +328,6 @@
property public final int duration;
}
- public interface InfiniteAnimationPolicy extends kotlin.coroutines.CoroutineContext.Element {
- method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
- method public suspend <R> Object? onInfiniteOperation(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R> p);
- property public default kotlin.coroutines.CoroutineContext.Key<?> key;
- field public static final androidx.compose.animation.core.InfiniteAnimationPolicy.Key Key;
- }
-
- public static final class InfiniteAnimationPolicy.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.animation.core.InfiniteAnimationPolicy> {
- }
-
public final class InfiniteAnimationPolicyKt {
method public static suspend inline <R> Object? withInfiniteAnimationFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withInfiniteAnimationFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
diff --git a/compose/animation/animation-core/api/public_plus_experimental_current.txt b/compose/animation/animation-core/api/public_plus_experimental_current.txt
index e181d01..4dbae30 100644
--- a/compose/animation/animation-core/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation-core/api/public_plus_experimental_current.txt
@@ -331,16 +331,6 @@
property public final int duration;
}
- public interface InfiniteAnimationPolicy extends kotlin.coroutines.CoroutineContext.Element {
- method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
- method public suspend <R> Object? onInfiniteOperation(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R> p);
- property public default kotlin.coroutines.CoroutineContext.Key<?> key;
- field public static final androidx.compose.animation.core.InfiniteAnimationPolicy.Key Key;
- }
-
- public static final class InfiniteAnimationPolicy.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.animation.core.InfiniteAnimationPolicy> {
- }
-
public final class InfiniteAnimationPolicyKt {
method public static suspend inline <R> Object? withInfiniteAnimationFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withInfiniteAnimationFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
diff --git a/compose/animation/animation-core/api/restricted_current.ignore b/compose/animation/animation-core/api/restricted_current.ignore
index 6eba384..612e66b 100644
--- a/compose/animation/animation-core/api/restricted_current.ignore
+++ b/compose/animation/animation-core/api/restricted_current.ignore
@@ -1,23 +1,3 @@
// Baseline format: 1.0
-ChangedClass: androidx.compose.animation.core.Transition.Segment:
- Class androidx.compose.animation.core.Transition.Segment changed class/interface declaration
-
-
-RemovedClass: androidx.compose.animation.core.ManualFrameClock:
- Removed class androidx.compose.animation.core.ManualFrameClock
-RemovedClass: androidx.compose.animation.core.ManualFrameClockKt:
- Removed class androidx.compose.animation.core.ManualFrameClockKt
-
-
-RemovedMethod: androidx.compose.animation.core.Transition#addAnimation(androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>):
- Removed method androidx.compose.animation.core.Transition.addAnimation(androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>)
-RemovedMethod: androidx.compose.animation.core.Transition#addTransition(androidx.compose.animation.core.Transition<?>):
- Removed method androidx.compose.animation.core.Transition.addTransition(androidx.compose.animation.core.Transition<?>)
-RemovedMethod: androidx.compose.animation.core.Transition#removeAnimation(androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>):
- Removed method androidx.compose.animation.core.Transition.removeAnimation(androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>)
-RemovedMethod: androidx.compose.animation.core.Transition#removeTransition(androidx.compose.animation.core.Transition<?>):
- Removed method androidx.compose.animation.core.Transition.removeTransition(androidx.compose.animation.core.Transition<?>)
-RemovedMethod: androidx.compose.animation.core.Transition#updateTarget(S):
- Removed method androidx.compose.animation.core.Transition.updateTarget(S)
-RemovedMethod: androidx.compose.animation.core.Transition.Segment#Segment(S, S):
- Removed constructor androidx.compose.animation.core.Transition.Segment(S,S)
+RemovedInterface: androidx.compose.animation.core.InfiniteAnimationPolicy:
+ Removed class androidx.compose.animation.core.InfiniteAnimationPolicy
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index b96b878..71f9ce4 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -328,16 +328,6 @@
property public final int duration;
}
- public interface InfiniteAnimationPolicy extends kotlin.coroutines.CoroutineContext.Element {
- method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
- method public suspend <R> Object? onInfiniteOperation(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R> p);
- property public default kotlin.coroutines.CoroutineContext.Key<?> key;
- field public static final androidx.compose.animation.core.InfiniteAnimationPolicy.Key Key;
- }
-
- public static final class InfiniteAnimationPolicy.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.animation.core.InfiniteAnimationPolicy> {
- }
-
public final class InfiniteAnimationPolicyKt {
method public static suspend inline <R> Object? withInfiniteAnimationFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withInfiniteAnimationFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
diff --git a/compose/animation/animation-core/benchmark/build.gradle b/compose/animation/animation-core/benchmark/build.gradle
index 5cef145..2fd2831 100644
--- a/compose/animation/animation-core/benchmark/build.gradle
+++ b/compose/animation/animation-core/benchmark/build.gradle
@@ -25,6 +25,7 @@
dependencies {
kotlinPlugin project(":compose:compiler:compiler")
+ androidTestImplementation project(":compose:animation:animation-core")
androidTestImplementation project(":benchmark:benchmark-junit4")
androidTestImplementation project(":compose:runtime:runtime")
androidTestImplementation project(":compose:benchmark-utils")
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index 095df43..63cdf9e 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -40,6 +40,7 @@
api("androidx.annotation:annotation:1.1.0")
implementation(project(":compose:runtime:runtime"))
+ implementation(project(":compose:ui:ui"))
implementation(project(":compose:ui:ui-unit"))
implementation(project(":compose:ui:ui-util"))
implementation(libs.kotlinStdlib)
@@ -83,6 +84,7 @@
commonMain.dependencies {
implementation(project(":compose:runtime:runtime"))
+ implementation(project(":compose:ui:ui"))
implementation(project(":compose:ui:ui-unit"))
implementation(project(":compose:ui:ui-util"))
implementation(libs.kotlinStdlibCommon)
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteAnimationPolicy.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteAnimationPolicy.kt
index b0b4458..2cc7720 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteAnimationPolicy.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteAnimationPolicy.kt
@@ -18,34 +18,9 @@
import androidx.compose.runtime.withFrameMillis
import androidx.compose.runtime.withFrameNanos
+import androidx.compose.ui.platform.InfiniteAnimationPolicy
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext
-
-/**
- * Provides a policy that will be applied to animations that get their frame time from
- * [withInfiniteAnimationFrameNanos] or [withInfiniteAnimationFrameMillis]. This can be used to
- * intervene in infinite animations to make them finite, for example by cancelling such coroutines.
- *
- * By default no policy is installed, except in instrumented tests that use
- * [androidx.compose.ui.test.junit4.ComposeTestRule].
- */
-interface InfiniteAnimationPolicy : CoroutineContext.Element {
- /**
- * Call this to apply the policy on the given suspending [block]. Execution of the block is
- * determined by the policy implementation. For example, a test policy could decide not to
- * run the block, or trace its execution.
- *
- * The block is intended to be part of and will therefore be treated as an infinite animation,
- * one that after returning from [onInfiniteOperation] will call it again. If the block is
- * not part of an infinite animation, the policy will still be applied.
- */
- suspend fun <R> onInfiniteOperation(block: suspend () -> R): R
-
- override val key: CoroutineContext.Key<*> get() = Key
-
- companion object Key : CoroutineContext.Key<InfiniteAnimationPolicy>
-}
-
/**
* Like [withFrameNanos], but applies the [InfiniteAnimationPolicy] from the calling
* [CoroutineContext] if there is one.
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index e28e6c3..9c5c1ff6 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -120,6 +120,7 @@
publishing {
publications {
it.jvmOs("linux-x64", [libs.skikoLinuxX64])
+ it.jvmOs("linux-arm64", [libs.skikoLinuxArm64])
it.jvmOs("macos-x64", [libs.skikoMacOsX64])
it.jvmOs("macos-arm64", [libs.skikoMacOsArm64])
it.jvmOs("windows-x64", [libs.skikoWindowsX64])
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt
index 62430ab..9c0d71e 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt
@@ -32,13 +32,8 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Menu
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.animatedVectorResource
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
@@ -129,21 +124,6 @@
)
}
-@OptIn(ExperimentalComposeUiApi::class)
-@Composable
-private fun ResourcesSnippet7() {
- // Files in res/drawable folders. For example:
- // - res/drawable/animated_vector.xml
-
- // In your Compose code
- val image = animatedVectorResource(id = R.drawable.animated_vector)
- val atEnd by remember { mutableStateOf(false) }
- Icon(
- painter = image.painterFor(atEnd = atEnd),
- contentDescription = null // decorative element
- )
-}
-
@Composable
private fun ResourcesSnippet8() {
Icon(Icons.Rounded.Menu, contentDescription = "Localized description")
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
index ca5a68c..cac44a1 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
@@ -172,6 +172,18 @@
}
private object StateSnippet7 {
+ @Parcelize
+ data class City(val name: String, val country: String) : Parcelable
+
+ @Composable
+ fun CityScreen() {
+ var selectedCity = rememberSaveable {
+ mutableStateOf(City("Madrid", "Spain"))
+ }
+ }
+}
+
+private object StateSnippet8 {
data class City(val name: String, val country: String)
val CitySaver = run {
@@ -192,7 +204,7 @@
}
@Composable
-private fun StateSnippets8() {
+private fun StateSnippets9() {
data class City(val name: String, val country: String)
val CitySaver = listSaver<City, Any>(
@@ -237,3 +249,10 @@
_name.value = newName
}
}
+
+/**
+ * Add fake Parcelize and Parcelable to avoid adding AndroidX wide dependency on
+ * kotlin-parcelize just for snippets
+ */
+annotation class Parcelize
+interface Parcelable
\ No newline at end of file
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
index 1340a12..df95df9 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
@@ -293,7 +293,7 @@
) {
RadioButton(
selected = (text == selectedOption.name),
- onClick = { selectedOption = Option.valueOf(text) }
+ onClick = null
)
Text(
text = text,
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/NavigationRailDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/NavigationRailDemo.kt
index 37879d7..444854b 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/NavigationRailDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/NavigationRailDemo.kt
@@ -28,6 +28,7 @@
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Checkbox
import androidx.compose.material.ContentAlpha
@@ -137,9 +138,9 @@
.fillMaxWidth()
.padding(start = 16.dp)
.height(56.dp)
- .selectable(
- selected = compactNavRailState.value,
- onClick = { compactNavRailState.value = !compactNavRailState.value }
+ .toggleable(
+ value = compactNavRailState.value,
+ onValueChange = { compactNavRailState.value = !compactNavRailState.value }
),
verticalAlignment = Alignment.CenterVertically
) {
diff --git a/compose/ui/ui-test-junit4/build.gradle b/compose/ui/ui-test-junit4/build.gradle
index fb506d1..749cc40 100644
--- a/compose/ui/ui-test-junit4/build.gradle
+++ b/compose/ui/ui-test-junit4/build.gradle
@@ -43,7 +43,6 @@
api(libs.kotlinStdlib)
api(libs.kotlinStdlibCommon)
- implementation(project(":compose:animation:animation-core"))
implementation(project(":compose:runtime:runtime-saveable"))
implementation(project(":activity:activity-compose"))
implementation("androidx.annotation:annotation:1.1.0")
@@ -56,6 +55,7 @@
implementation(libs.kotlinCoroutinesCore)
implementation(libs.kotlinCoroutinesTest)
+ testImplementation(project(":compose:animation:animation-core"))
testImplementation(project(":compose:material:material"))
testImplementation(project(":compose:test-utils"))
testImplementation(libs.truth)
@@ -100,7 +100,6 @@
implementation project(":activity:activity-compose")
api("androidx.test.ext:junit:1.1.2")
- implementation(project(":compose:animation:animation-core"))
implementation(project(":compose:runtime:runtime-saveable"))
implementation("androidx.lifecycle:lifecycle-common:2.3.0")
implementation("androidx.lifecycle:lifecycle-runtime:2.3.0")
@@ -111,6 +110,7 @@
}
test.dependencies {
+ implementation(project(":compose:animation:animation-core"))
implementation(project(":compose:material:material"))
implementation(project(":compose:test-utils"))
implementation(libs.truth)
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.android.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.android.kt
index e3c4621..090bdb3 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.android.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.android.kt
@@ -21,11 +21,11 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.VisibleForTesting
-import androidx.compose.animation.core.InfiniteAnimationPolicy
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Recomposer
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.node.RootForTest
+import androidx.compose.ui.platform.InfiniteAnimationPolicy
import androidx.compose.ui.platform.ViewRootForTest
import androidx.compose.ui.platform.WindowRecomposerPolicy
import androidx.compose.ui.platform.textInputServiceFactory
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/InfiniteAnimationPolicyTest.kt b/compose/ui/ui-test-junit4/src/test/kotlin/androidx/compose/ui/test/junit4/InfiniteAnimationPolicyTest.kt
similarity index 90%
rename from compose/animation/animation-core/src/test/java/androidx/compose/animation/core/InfiniteAnimationPolicyTest.kt
rename to compose/ui/ui-test-junit4/src/test/kotlin/androidx/compose/ui/test/junit4/InfiniteAnimationPolicyTest.kt
index bddd29a..c505e085 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/InfiniteAnimationPolicyTest.kt
+++ b/compose/ui/ui-test-junit4/src/test/kotlin/androidx/compose/ui/test/junit4/InfiniteAnimationPolicyTest.kt
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-package androidx.compose.animation.core
+package androidx.compose.ui.test.junit4
+import androidx.compose.animation.core.withInfiniteAnimationFrameNanos
+import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
+import androidx.compose.ui.platform.InfiniteAnimationPolicy
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.runBlocking
diff --git a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/HotReloaderTest.kt b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/HotReloaderTest.kt
new file mode 100644
index 0000000..baa0d04
--- /dev/null
+++ b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/HotReloaderTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 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.ui.tooling
+
+import org.junit.Assert.assertNotNull
+import org.junit.Test
+
+class HotReloaderTest {
+ /**
+ * Ensure that the `HotReloader` interface used by the preview is not modified inadvertently.
+ *
+ * This test replicates the sequence of calls done from the preview to obtain the methods from
+ * `HotReloader`.
+ */
+ @Test
+ fun checkViaReflection() {
+ val hotReloader = HotReloaderTest::class.java.classLoader!!
+ .loadClass("androidx.compose.runtime.HotReloader")
+ val hotReloaderInstance = hotReloader.getDeclaredField("Companion").let {
+ it.isAccessible = true
+ it.get(null)
+ }
+ val saveStateAndDisposeMethod =
+ hotReloaderInstance.javaClass.getDeclaredMethod("saveStateAndDispose", Any::class.java)
+ .also {
+ it.isAccessible = true
+ }
+ val loadStateAndDisposeMethod =
+ hotReloaderInstance.javaClass.getDeclaredMethod("loadStateAndCompose", Any::class.java)
+ .also {
+ it.isAccessible = true
+ }
+ assertNotNull(saveStateAndDisposeMethod)
+ assertNotNull(loadStateAndDisposeMethod)
+ }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/api/current.ignore b/compose/ui/ui/api/current.ignore
index 91311ea..523801b2f4 100644
--- a/compose/ui/ui/api/current.ignore
+++ b/compose/ui/ui/api/current.ignore
@@ -1,75 +1,13 @@
// Baseline format: 1.0
-AddedAbstractMethod: androidx.compose.ui.hapticfeedback.HapticFeedback#performHapticFeedback-CdsT49E(int):
- Added method androidx.compose.ui.hapticfeedback.HapticFeedback.performHapticFeedback-CdsT49E(int)
-
-
-ChangedSuperclass: androidx.compose.ui.hapticfeedback.HapticFeedbackType:
- Class androidx.compose.ui.hapticfeedback.HapticFeedbackType superclass changed from java.lang.Enum to java.lang.Object
-ChangedSuperclass: androidx.compose.ui.semantics.LiveRegionMode:
- Class androidx.compose.ui.semantics.LiveRegionMode superclass changed from java.lang.Enum to java.lang.Object
-ChangedSuperclass: androidx.compose.ui.semantics.Role:
- Class androidx.compose.ui.semantics.Role superclass changed from java.lang.Enum to java.lang.Object
-
-
-ChangedType: androidx.compose.ui.semantics.SemanticsPropertiesKt#getLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver):
- Method androidx.compose.ui.semantics.SemanticsPropertiesKt.getLiveRegion has changed return type from androidx.compose.ui.semantics.LiveRegionMode to int
-ChangedType: androidx.compose.ui.semantics.SemanticsPropertiesKt#getRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver):
- Method androidx.compose.ui.semantics.SemanticsPropertiesKt.getRole has changed return type from androidx.compose.ui.semantics.Role to int
-
-
-InvalidNullConversion: androidx.compose.ui.semantics.SemanticsPropertiesKt#getLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver):
- Attempted to remove @NonNull annotation from method androidx.compose.ui.semantics.SemanticsPropertiesKt.getLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver)
-InvalidNullConversion: androidx.compose.ui.semantics.SemanticsPropertiesKt#getRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver):
- Attempted to remove @NonNull annotation from method androidx.compose.ui.semantics.SemanticsPropertiesKt.getRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver)
-
-
-ParameterNameChange: androidx.compose.ui.layout.Placeable#setMeasurementConstraints-BRTryo0(long) parameter #0:
- Attempted to change parameter name from p to value in method androidx.compose.ui.layout.Placeable.setMeasurementConstraints-BRTryo0
-
-
-RemovedClass: androidx.compose.ui.autofill.AndroidAutofillDebugUtils_androidKt:
- Removed class androidx.compose.ui.autofill.AndroidAutofillDebugUtils_androidKt
-RemovedClass: androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent:
- Removed class androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent
-
-
-RemovedField: androidx.compose.ui.hapticfeedback.HapticFeedbackType#LongPress:
- Removed enum constant androidx.compose.ui.hapticfeedback.HapticFeedbackType.LongPress
-RemovedField: androidx.compose.ui.hapticfeedback.HapticFeedbackType#TextHandleMove:
- Removed enum constant androidx.compose.ui.hapticfeedback.HapticFeedbackType.TextHandleMove
-RemovedField: androidx.compose.ui.semantics.LiveRegionMode#Assertive:
- Removed enum constant androidx.compose.ui.semantics.LiveRegionMode.Assertive
-RemovedField: androidx.compose.ui.semantics.LiveRegionMode#Polite:
- Removed enum constant androidx.compose.ui.semantics.LiveRegionMode.Polite
-RemovedField: androidx.compose.ui.semantics.Role#Button:
- Removed enum constant androidx.compose.ui.semantics.Role.Button
-RemovedField: androidx.compose.ui.semantics.Role#Checkbox:
- Removed enum constant androidx.compose.ui.semantics.Role.Checkbox
-RemovedField: androidx.compose.ui.semantics.Role#Image:
- Removed enum constant androidx.compose.ui.semantics.Role.Image
-RemovedField: androidx.compose.ui.semantics.Role#RadioButton:
- Removed enum constant androidx.compose.ui.semantics.Role.RadioButton
-RemovedField: androidx.compose.ui.semantics.Role#Switch:
- Removed enum constant androidx.compose.ui.semantics.Role.Switch
-RemovedField: androidx.compose.ui.semantics.Role#Tab:
- Removed enum constant androidx.compose.ui.semantics.Role.Tab
-
-
-RemovedMethod: androidx.compose.ui.focus.FocusDirection#constructor-impl(int):
- Removed method androidx.compose.ui.focus.FocusDirection.constructor-impl(int)
-RemovedMethod: androidx.compose.ui.focus.FocusDirection#getValue():
- Removed method androidx.compose.ui.focus.FocusDirection.getValue()
-RemovedMethod: androidx.compose.ui.hapticfeedback.HapticFeedback#performHapticFeedback(androidx.compose.ui.hapticfeedback.HapticFeedbackType):
- Removed method androidx.compose.ui.hapticfeedback.HapticFeedback.performHapticFeedback(androidx.compose.ui.hapticfeedback.HapticFeedbackType)
-RemovedMethod: androidx.compose.ui.input.key.KeyEventType#constructor-impl(int):
- Removed method androidx.compose.ui.input.key.KeyEventType.constructor-impl(int)
-RemovedMethod: androidx.compose.ui.input.key.KeyEventType#getValue():
- Removed method androidx.compose.ui.input.key.KeyEventType.getValue()
-RemovedMethod: androidx.compose.ui.input.pointer.PointerInteropFilter_androidKt#pointerInteropFilter(androidx.compose.ui.Modifier, androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent, kotlin.jvm.functions.Function1<? super android.view.MotionEvent,java.lang.Boolean>):
- Removed method androidx.compose.ui.input.pointer.PointerInteropFilter_androidKt.pointerInteropFilter(androidx.compose.ui.Modifier,androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent,kotlin.jvm.functions.Function1<? super android.view.MotionEvent,java.lang.Boolean>)
-RemovedMethod: androidx.compose.ui.input.pointer.PointerType#getValue():
- Removed method androidx.compose.ui.input.pointer.PointerType.getValue()
-RemovedMethod: androidx.compose.ui.semantics.SemanticsPropertiesKt#setLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.LiveRegionMode):
- Removed method androidx.compose.ui.semantics.SemanticsPropertiesKt.setLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver,androidx.compose.ui.semantics.LiveRegionMode)
-RemovedMethod: androidx.compose.ui.semantics.SemanticsPropertiesKt#setRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.Role):
- Removed method androidx.compose.ui.semantics.SemanticsPropertiesKt.setRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver,androidx.compose.ui.semantics.Role)
+RemovedClass: androidx.compose.ui.graphics.vector.AnimatorKt:
+ Removed class androidx.compose.ui.graphics.vector.AnimatorKt
+RemovedClass: androidx.compose.ui.graphics.vector.compat.XmlAnimatedVectorParser_androidKt:
+ Removed class androidx.compose.ui.graphics.vector.compat.XmlAnimatedVectorParser_androidKt
+RemovedClass: androidx.compose.ui.graphics.vector.compat.XmlAnimatorParser_androidKt:
+ Removed class androidx.compose.ui.graphics.vector.compat.XmlAnimatorParser_androidKt
+RemovedClass: androidx.compose.ui.graphics.vector.compat.XmlPullParserUtils_androidKt:
+ Removed class androidx.compose.ui.graphics.vector.compat.XmlPullParserUtils_androidKt
+RemovedClass: androidx.compose.ui.res.AnimatedVectorResources_androidKt:
+ Removed class androidx.compose.ui.res.AnimatedVectorResources_androidKt
+RemovedClass: androidx.compose.ui.res.AnimatorResources_androidKt:
+ Removed class androidx.compose.ui.res.AnimatorResources_androidKt
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 20df907..378529c 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -447,9 +447,6 @@
package androidx.compose.ui.graphics.vector {
- public final class AnimatorKt {
- }
-
@androidx.compose.runtime.Immutable public final class ImageVector {
method public float getDefaultHeight-D9Ej5fM();
method public float getDefaultWidth-D9Ej5fM();
@@ -604,15 +601,6 @@
package androidx.compose.ui.graphics.vector.compat {
- public final class XmlAnimatedVectorParser_androidKt {
- }
-
- public final class XmlAnimatorParser_androidKt {
- }
-
- public final class XmlPullParserUtils_androidKt {
- }
-
public final class XmlVectorParser_androidKt {
}
@@ -1970,6 +1958,16 @@
public final class DisposableSaveableStateRegistry_androidKt {
}
+ public interface InfiniteAnimationPolicy extends kotlin.coroutines.CoroutineContext.Element {
+ method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
+ method public suspend <R> Object? onInfiniteOperation(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R> p);
+ property public default kotlin.coroutines.CoroutineContext.Key<?> key;
+ field public static final androidx.compose.ui.platform.InfiniteAnimationPolicy.Key Key;
+ }
+
+ public static final class InfiniteAnimationPolicy.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.ui.platform.InfiniteAnimationPolicy> {
+ }
+
public interface InspectableValue {
method public default kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
method public default String? getNameFallback();
@@ -2148,12 +2146,6 @@
package androidx.compose.ui.res {
- public final class AnimatedVectorResources_androidKt {
- }
-
- public final class AnimatorResources_androidKt {
- }
-
public final class ColorResources_androidKt {
method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long colorResource(@ColorRes int id);
}
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index d6f5d92..f50d2c7 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -539,17 +539,6 @@
package androidx.compose.ui.graphics.vector {
- @androidx.compose.runtime.Immutable @androidx.compose.ui.ExperimentalComposeUiApi public final class AnimatedImageVector {
- method public androidx.compose.ui.graphics.vector.ImageVector getImageVector();
- method public int getTotalDuration();
- method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter painterFor(boolean atEnd);
- property public final androidx.compose.ui.graphics.vector.ImageVector imageVector;
- property public final int totalDuration;
- }
-
- public final class AnimatorKt {
- }
-
@androidx.compose.runtime.Immutable public final class ImageVector {
method public float getDefaultHeight-D9Ej5fM();
method public float getDefaultWidth-D9Ej5fM();
@@ -704,15 +693,6 @@
package androidx.compose.ui.graphics.vector.compat {
- public final class XmlAnimatedVectorParser_androidKt {
- }
-
- public final class XmlAnimatorParser_androidKt {
- }
-
- public final class XmlPullParserUtils_androidKt {
- }
-
public final class XmlVectorParser_androidKt {
}
@@ -2089,6 +2069,16 @@
public final class DisposableSaveableStateRegistry_androidKt {
}
+ public interface InfiniteAnimationPolicy extends kotlin.coroutines.CoroutineContext.Element {
+ method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
+ method public suspend <R> Object? onInfiniteOperation(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R> p);
+ property public default kotlin.coroutines.CoroutineContext.Key<?> key;
+ field public static final androidx.compose.ui.platform.InfiniteAnimationPolicy.Key Key;
+ }
+
+ public static final class InfiniteAnimationPolicy.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.ui.platform.InfiniteAnimationPolicy> {
+ }
+
public interface InspectableValue {
method public default kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
method public default String? getNameFallback();
@@ -2304,13 +2294,6 @@
package androidx.compose.ui.res {
- public final class AnimatedVectorResources_androidKt {
- method @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.graphics.vector.AnimatedImageVector animatedVectorResource(@DrawableRes int id);
- }
-
- public final class AnimatorResources_androidKt {
- }
-
public final class ColorResources_androidKt {
method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long colorResource(@ColorRes int id);
}
diff --git a/compose/ui/ui/api/restricted_current.ignore b/compose/ui/ui/api/restricted_current.ignore
index 91311ea..523801b2f4 100644
--- a/compose/ui/ui/api/restricted_current.ignore
+++ b/compose/ui/ui/api/restricted_current.ignore
@@ -1,75 +1,13 @@
// Baseline format: 1.0
-AddedAbstractMethod: androidx.compose.ui.hapticfeedback.HapticFeedback#performHapticFeedback-CdsT49E(int):
- Added method androidx.compose.ui.hapticfeedback.HapticFeedback.performHapticFeedback-CdsT49E(int)
-
-
-ChangedSuperclass: androidx.compose.ui.hapticfeedback.HapticFeedbackType:
- Class androidx.compose.ui.hapticfeedback.HapticFeedbackType superclass changed from java.lang.Enum to java.lang.Object
-ChangedSuperclass: androidx.compose.ui.semantics.LiveRegionMode:
- Class androidx.compose.ui.semantics.LiveRegionMode superclass changed from java.lang.Enum to java.lang.Object
-ChangedSuperclass: androidx.compose.ui.semantics.Role:
- Class androidx.compose.ui.semantics.Role superclass changed from java.lang.Enum to java.lang.Object
-
-
-ChangedType: androidx.compose.ui.semantics.SemanticsPropertiesKt#getLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver):
- Method androidx.compose.ui.semantics.SemanticsPropertiesKt.getLiveRegion has changed return type from androidx.compose.ui.semantics.LiveRegionMode to int
-ChangedType: androidx.compose.ui.semantics.SemanticsPropertiesKt#getRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver):
- Method androidx.compose.ui.semantics.SemanticsPropertiesKt.getRole has changed return type from androidx.compose.ui.semantics.Role to int
-
-
-InvalidNullConversion: androidx.compose.ui.semantics.SemanticsPropertiesKt#getLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver):
- Attempted to remove @NonNull annotation from method androidx.compose.ui.semantics.SemanticsPropertiesKt.getLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver)
-InvalidNullConversion: androidx.compose.ui.semantics.SemanticsPropertiesKt#getRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver):
- Attempted to remove @NonNull annotation from method androidx.compose.ui.semantics.SemanticsPropertiesKt.getRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver)
-
-
-ParameterNameChange: androidx.compose.ui.layout.Placeable#setMeasurementConstraints-BRTryo0(long) parameter #0:
- Attempted to change parameter name from p to value in method androidx.compose.ui.layout.Placeable.setMeasurementConstraints-BRTryo0
-
-
-RemovedClass: androidx.compose.ui.autofill.AndroidAutofillDebugUtils_androidKt:
- Removed class androidx.compose.ui.autofill.AndroidAutofillDebugUtils_androidKt
-RemovedClass: androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent:
- Removed class androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent
-
-
-RemovedField: androidx.compose.ui.hapticfeedback.HapticFeedbackType#LongPress:
- Removed enum constant androidx.compose.ui.hapticfeedback.HapticFeedbackType.LongPress
-RemovedField: androidx.compose.ui.hapticfeedback.HapticFeedbackType#TextHandleMove:
- Removed enum constant androidx.compose.ui.hapticfeedback.HapticFeedbackType.TextHandleMove
-RemovedField: androidx.compose.ui.semantics.LiveRegionMode#Assertive:
- Removed enum constant androidx.compose.ui.semantics.LiveRegionMode.Assertive
-RemovedField: androidx.compose.ui.semantics.LiveRegionMode#Polite:
- Removed enum constant androidx.compose.ui.semantics.LiveRegionMode.Polite
-RemovedField: androidx.compose.ui.semantics.Role#Button:
- Removed enum constant androidx.compose.ui.semantics.Role.Button
-RemovedField: androidx.compose.ui.semantics.Role#Checkbox:
- Removed enum constant androidx.compose.ui.semantics.Role.Checkbox
-RemovedField: androidx.compose.ui.semantics.Role#Image:
- Removed enum constant androidx.compose.ui.semantics.Role.Image
-RemovedField: androidx.compose.ui.semantics.Role#RadioButton:
- Removed enum constant androidx.compose.ui.semantics.Role.RadioButton
-RemovedField: androidx.compose.ui.semantics.Role#Switch:
- Removed enum constant androidx.compose.ui.semantics.Role.Switch
-RemovedField: androidx.compose.ui.semantics.Role#Tab:
- Removed enum constant androidx.compose.ui.semantics.Role.Tab
-
-
-RemovedMethod: androidx.compose.ui.focus.FocusDirection#constructor-impl(int):
- Removed method androidx.compose.ui.focus.FocusDirection.constructor-impl(int)
-RemovedMethod: androidx.compose.ui.focus.FocusDirection#getValue():
- Removed method androidx.compose.ui.focus.FocusDirection.getValue()
-RemovedMethod: androidx.compose.ui.hapticfeedback.HapticFeedback#performHapticFeedback(androidx.compose.ui.hapticfeedback.HapticFeedbackType):
- Removed method androidx.compose.ui.hapticfeedback.HapticFeedback.performHapticFeedback(androidx.compose.ui.hapticfeedback.HapticFeedbackType)
-RemovedMethod: androidx.compose.ui.input.key.KeyEventType#constructor-impl(int):
- Removed method androidx.compose.ui.input.key.KeyEventType.constructor-impl(int)
-RemovedMethod: androidx.compose.ui.input.key.KeyEventType#getValue():
- Removed method androidx.compose.ui.input.key.KeyEventType.getValue()
-RemovedMethod: androidx.compose.ui.input.pointer.PointerInteropFilter_androidKt#pointerInteropFilter(androidx.compose.ui.Modifier, androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent, kotlin.jvm.functions.Function1<? super android.view.MotionEvent,java.lang.Boolean>):
- Removed method androidx.compose.ui.input.pointer.PointerInteropFilter_androidKt.pointerInteropFilter(androidx.compose.ui.Modifier,androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent,kotlin.jvm.functions.Function1<? super android.view.MotionEvent,java.lang.Boolean>)
-RemovedMethod: androidx.compose.ui.input.pointer.PointerType#getValue():
- Removed method androidx.compose.ui.input.pointer.PointerType.getValue()
-RemovedMethod: androidx.compose.ui.semantics.SemanticsPropertiesKt#setLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.LiveRegionMode):
- Removed method androidx.compose.ui.semantics.SemanticsPropertiesKt.setLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver,androidx.compose.ui.semantics.LiveRegionMode)
-RemovedMethod: androidx.compose.ui.semantics.SemanticsPropertiesKt#setRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.Role):
- Removed method androidx.compose.ui.semantics.SemanticsPropertiesKt.setRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver,androidx.compose.ui.semantics.Role)
+RemovedClass: androidx.compose.ui.graphics.vector.AnimatorKt:
+ Removed class androidx.compose.ui.graphics.vector.AnimatorKt
+RemovedClass: androidx.compose.ui.graphics.vector.compat.XmlAnimatedVectorParser_androidKt:
+ Removed class androidx.compose.ui.graphics.vector.compat.XmlAnimatedVectorParser_androidKt
+RemovedClass: androidx.compose.ui.graphics.vector.compat.XmlAnimatorParser_androidKt:
+ Removed class androidx.compose.ui.graphics.vector.compat.XmlAnimatorParser_androidKt
+RemovedClass: androidx.compose.ui.graphics.vector.compat.XmlPullParserUtils_androidKt:
+ Removed class androidx.compose.ui.graphics.vector.compat.XmlPullParserUtils_androidKt
+RemovedClass: androidx.compose.ui.res.AnimatedVectorResources_androidKt:
+ Removed class androidx.compose.ui.res.AnimatedVectorResources_androidKt
+RemovedClass: androidx.compose.ui.res.AnimatorResources_androidKt:
+ Removed class androidx.compose.ui.res.AnimatorResources_androidKt
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 1626b87..d21b81a 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -447,9 +447,6 @@
package androidx.compose.ui.graphics.vector {
- public final class AnimatorKt {
- }
-
@androidx.compose.runtime.Immutable public final class ImageVector {
method public float getDefaultHeight-D9Ej5fM();
method public float getDefaultWidth-D9Ej5fM();
@@ -604,15 +601,6 @@
package androidx.compose.ui.graphics.vector.compat {
- public final class XmlAnimatedVectorParser_androidKt {
- }
-
- public final class XmlAnimatorParser_androidKt {
- }
-
- public final class XmlPullParserUtils_androidKt {
- }
-
public final class XmlVectorParser_androidKt {
}
@@ -2000,6 +1988,16 @@
public final class DisposableSaveableStateRegistry_androidKt {
}
+ public interface InfiniteAnimationPolicy extends kotlin.coroutines.CoroutineContext.Element {
+ method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
+ method public suspend <R> Object? onInfiniteOperation(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R> p);
+ property public default kotlin.coroutines.CoroutineContext.Key<?> key;
+ field public static final androidx.compose.ui.platform.InfiniteAnimationPolicy.Key Key;
+ }
+
+ public static final class InfiniteAnimationPolicy.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.ui.platform.InfiniteAnimationPolicy> {
+ }
+
public interface InspectableValue {
method public default kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
method public default String? getNameFallback();
@@ -2178,12 +2176,6 @@
package androidx.compose.ui.res {
- public final class AnimatedVectorResources_androidKt {
- }
-
- public final class AnimatorResources_androidKt {
- }
-
public final class ColorResources_androidKt {
method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long colorResource(@ColorRes int id);
}
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index d188d8c..e8887a6 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -40,9 +40,6 @@
implementation(libs.kotlinStdlibCommon)
implementation(libs.kotlinCoroutinesCore)
- // TODO: b/160602714 create a lower level module to avoid this dependency on the
- // animation group
- api(project(":compose:animation:animation-core"))
api(project(":compose:runtime:runtime-saveable"))
api(project(":compose:ui:ui-geometry"))
api(project(":compose:ui:ui-graphics"))
@@ -89,6 +86,7 @@
androidTestImplementation(libs.mockitoCore)
androidTestImplementation(libs.truth)
androidTestImplementation(libs.mockitoKotlin)
+ androidTestImplementation(project(":compose:animation:animation-core"))
androidTestImplementation(project(":compose:foundation:foundation"))
androidTestImplementation(project(":compose:foundation:foundation-layout"))
androidTestImplementation(project(":compose:test-utils"))
@@ -122,9 +120,6 @@
implementation(libs.kotlinStdlibCommon)
implementation(libs.kotlinCoroutinesCore)
- // TODO: b/160602714 create a lower level module to avoid this dependency on the
- // animation group
- api project(":compose:animation:animation-core")
api project(":compose:runtime:runtime-saveable")
api project(":compose:ui:ui-geometry")
api project(":compose:ui:ui-graphics")
@@ -189,6 +184,7 @@
implementation(libs.mockitoCore)
implementation(libs.truth)
implementation(libs.mockitoKotlin)
+ implementation(project(":compose:animation:animation-core"))
implementation(project(":compose:foundation:foundation"))
implementation(project(":compose:foundation:foundation-layout"))
implementation(project(":compose:test-utils"))
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/AnimatedVectorGraphicsDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/AnimatedVectorGraphicsDemo.kt
deleted file mode 100644
index 418eb4c..0000000
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/AnimatedVectorGraphicsDemo.kt
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright 2021 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.ui.demos
-
-import androidx.compose.animation.animateColor
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.animateFloat
-import androidx.compose.animation.core.keyframes
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.updateTransition
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.painter.Painter
-import androidx.compose.ui.graphics.vector.Group
-import androidx.compose.ui.graphics.vector.Path
-import androidx.compose.ui.graphics.vector.PathData
-import androidx.compose.ui.graphics.vector.rememberVectorPainter
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.res.animatedVectorResource
-import androidx.compose.ui.unit.dp
-
-@OptIn(ExperimentalComposeUiApi::class)
-@Composable
-fun AnimatedVectorGraphicsDemo() {
- Column(
- modifier = Modifier.fillMaxSize(),
- verticalArrangement = Arrangement.SpaceEvenly,
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- val image = animatedVectorResource(R.drawable.ic_hourglass_animated)
- var atEnd by remember { mutableStateOf(false) }
- Image(
- painter = image.painterFor(atEnd),
- contentDescription = "AnimatedImageVector",
- modifier = Modifier.size(200.dp).clickable {
- atEnd = !atEnd
- },
- contentScale = ContentScale.Crop
- )
-
- var toggle by remember { mutableStateOf(false) }
- Image(
- painter = createSampleVectorPainter(toggle),
- contentDescription = "Transition with vector graphics",
- modifier = Modifier.size(200.dp).clickable {
- toggle = !toggle
- },
- contentScale = ContentScale.Crop
- )
- }
-}
-
-@Composable
-fun createSampleVectorPainter(toggle: Boolean): Painter {
- return rememberVectorPainter(
- defaultWidth = 24.dp,
- defaultHeight = 24.dp,
- viewportWidth = 24f,
- viewportHeight = 24f,
- name = "sample"
- ) { _, _ ->
- val transition = updateTransition(toggle)
- val duration = 3000
- Path(
- pathData = PathData {
- horizontalLineTo(24f)
- verticalLineTo(24f)
- horizontalLineTo(0f)
- close()
- },
- fill = SolidColor(Color.Cyan)
- )
- val rotation by transition.animateFloat(
- transitionSpec = {
- if (targetState) {
- keyframes {
- durationMillis = duration
- 0f at 0
- 360f at duration with LinearEasing
- }
- } else {
- spring(
- dampingRatio = Spring.DampingRatioMediumBouncy,
- stiffness = Spring.StiffnessVeryLow
- )
- }
- }
- ) { state ->
- if (state) 360f else 0f
- }
- @Suppress("UnusedTransitionTargetStateParameter")
- val translationX by transition.animateFloat(
- transitionSpec = {
- if (targetState) {
- keyframes {
- durationMillis = duration
- -6f at 500
- 6f at 1500
- -6f at 2000
- 6f at 2500
- }
- } else {
- spring(
- dampingRatio = Spring.DampingRatioHighBouncy,
- stiffness = Spring.StiffnessLow
- )
- }
- }
- ) { 0f }
- @Suppress("UnusedTransitionTargetStateParameter")
- val translationY by transition.animateFloat(
- transitionSpec = {
- if (targetState) {
- keyframes {
- durationMillis = duration
- -6f at 1000
- 6f at 2000
- }
- } else {
- spring()
- }
- }
- ) { 0f }
- Group(
- name = "rectangle",
- rotation = rotation,
- translationX = translationX,
- translationY = translationY,
- pivotX = 12f,
- pivotY = 12f
- ) {
- val fillColor by transition.animateColor(
- transitionSpec = {
- if (targetState) {
- keyframes {
- durationMillis = duration
- Color.Red at 0
- Color.Blue at duration with LinearEasing
- }
- } else {
- spring()
- }
- }
- ) { state ->
- if (state) Color.Blue else Color.Red
- }
- Path(
- pathData = PathData {
- moveTo(8f, 8f)
- lineTo(16f, 8f)
- lineTo(16f, 16f)
- lineTo(8f, 16f)
- close()
- },
- fill = SolidColor(fillColor)
- )
- }
- }
-}
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
index 5e56360..a26c7ff 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
@@ -128,7 +128,6 @@
"Graphics",
listOf(
ComposableDemo("VectorGraphicsDemo") { VectorGraphicsDemo() },
- ComposableDemo("AnimatedVectorGraphicsDemo") { AnimatedVectorGraphicsDemo() },
ComposableDemo("DeclarativeGraphicsDemo") { DeclarativeGraphicsDemo() }
)
)
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/res/drawable/ic_hourglass_animated.xml b/compose/ui/ui/integration-tests/ui-demos/src/main/res/drawable/ic_hourglass_animated.xml
deleted file mode 100644
index ceb023f..0000000
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/res/drawable/ic_hourglass_animated.xml
+++ /dev/null
@@ -1,155 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright 2021 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.
--->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <target android:name="hourglass_frame">
- <aapt:attr name="android:animation">
- <objectAnimator
- android:duration="333"
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:propertyName="rotation"
- android:valueFrom="0"
- android:valueTo="180" />
- </aapt:attr>
- </target>
- <target android:name="fill_outlines">
- <aapt:attr name="android:animation">
- <objectAnimator
- android:duration="333"
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:propertyName="rotation"
- android:valueFrom="0"
- android:valueTo="180" />
- </aapt:attr>
- </target>
- <target android:name="mask_sand">
- <aapt:attr name="android:animation">
- <objectAnimator
- android:duration="1000"
- android:interpolator="@android:interpolator/accelerate_decelerate"
- android:propertyName="pathData"
- android:startOffset="333"
- android:valueFrom="M 24 13.3999938965 c 0 0.0 -24 0.0 -24 0.0
- c 0 0.0 0 10.6000061035 0 10.6000061035 c 0 0 24 0 24 0
- c 0 0 0 -10.6000061035 0 -10.6000061035 Z"
- android:valueTo="M 24 0.00173950195312 c 0 0.0 -24 0.0 -24 0.0
- c 0 0.0 0 10.6982574463 0 10.6982574463 c 0 0.0 24 0.0 24 0.0
- c 0 0.0 0 -10.6982574463 0 -10.6982574463 Z"
- android:valueType="pathType" />
- </aapt:attr>
- </target>
- <aapt:attr name="android:drawable">
- <vector
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
- <group
- android:name="hourglass_frame"
- android:scaleX="0.75"
- android:scaleY="0.75"
- android:translateX="12"
- android:translateY="12">
- <group
- android:name="hourglass_frame_pivot"
- android:translateX="-12"
- android:translateY="-12">
- <group
- android:name="group_bottom"
- android:translateX="12"
- android:translateY="6.5">
- <path
- android:name="path_bottom"
- android:fillColor="#FF777777"
- android:pathData="M 6.52099609375 -3.89300537109
- c 0.0 0.0 -6.52099609375 6.87901306152 -6.52099609375 6.87901306152
- c 0 0.0 -6.52099609375 -6.87901306152 -6.52099609375 -6.87901306152
- c 0.0 0.0 13.0419921875 0.0 13.0419921875 0.0 Z M 9.99800109863 -6.5
- c 0.0 0.0 -19.9960021973 0.0 -19.9960021973 0.0
- c -0.890991210938 0.0 -1.33700561523 1.07699584961 -0.707000732422
- 1.70700073242
- c 0.0 0.0 10.7050018311 11.2929992676 10.7050018311 11.2929992676
- c 0 0.0 10.7050018311 -11.2929992676 10.7050018311 -11.2929992676
- c 0.630004882812 -0.630004882812 0.183990478516 -1.70700073242
- -0.707000732422 -1.70700073242 Z" />
- </group>
- <group
- android:name="group_top"
- android:translateX="12"
- android:translateY="17.5">
- <path
- android:name="path_top"
- android:fillColor="#FF777777"
- android:pathData="M 0 -2.98600769043
- c 0 0.0 6.52099609375 6.87901306152 6.52099609375 6.87901306152
- c 0.0 0.0 -13.0419921875 0.0 -13.0419921875 0.0
- c 0.0 0.0 6.52099609375 -6.87901306152 6.52099609375 -6.87901306152
- Z M 0 -6.5
- c 0 0.0 -10.7050018311 11.2929992676 -10.7050018311 11.2929992676
- c -0.630004882812 0.630004882812 -0.184005737305 1.70700073242
- 0.707000732422 1.70700073242
- c 0.0 0.0 19.9960021973 0.0 19.9960021973 0.0
- c 0.890991210938 0.0 1.33699035645 -1.07699584961 0.707000732422
- -1.70700073242
- c 0.0 0.0 -10.7050018311 -11.2929992676 -10.7050018311
- -11.2929992676 Z" />
- </group>
- </group>
- </group>
- <group
- android:name="fill_outlines"
- android:scaleX="0.75"
- android:scaleY="0.75"
- android:translateX="12"
- android:translateY="12">
- <group
- android:name="fill_outlines_pivot"
- android:translateX="-12"
- android:translateY="-12">
- <clip-path
- android:name="mask_sand"
- android:pathData="M 24 13.3999938965 c 0 0.0 -24 0.0 -24 0.0
- c 0 0.0 0 10.6000061035 0 10.6000061035 c 0 0 24 0 24 0
- c 0 0 0 -10.6000061035 0 -10.6000061035 Z" />
- <group
- android:name="group_sand"
- android:translateX="12"
- android:translateY="12">
- <path
- android:name="path_sand"
- android:fillColor="#FF777777"
- android:pathData="M 10.7100067139 10.2900085449
- c 0.629989624023 0.629989624023 0.179992675781 1.70999145508
- -0.710006713867 1.70999145508
- c 0 0 -20 0 -20 0
- c -0.889999389648 0 -1.33999633789 -1.08000183105 -0.710006713867
- -1.70999145508
- c 0.0 0.0 9.76000976562 -10.2900085449 9.76000976563 -10.2900085449
- c 0.0 0 -9.76000976562 -10.2899932861 -9.76000976563 -10.2899932861
- c -0.629989624023 -0.630004882812 -0.179992675781 -1.71000671387
- 0.710006713867 -1.71000671387
- c 0 0 20 0 20 0
- c 0.889999389648 0 1.33999633789 1.08000183105 0.710006713867
- 1.71000671387
- c 0.0 0.0 -9.76000976562 10.2899932861 -9.76000976563 10.2899932861
- c 0.0 0 9.76000976562 10.2900085449 9.76000976563 10.2900085449
- Z" />
- </group>
- </group>
- </group>
- </vector>
- </aapt:attr>
-</animated-vector>
diff --git a/compose/ui/ui/samples/build.gradle b/compose/ui/ui/samples/build.gradle
index cf62e93..5376a2c 100644
--- a/compose/ui/ui/samples/build.gradle
+++ b/compose/ui/ui/samples/build.gradle
@@ -31,6 +31,7 @@
compileOnly(project(":annotation:annotation-sampled"))
+ implementation(project(":compose:animation:animation-core"))
implementation(project(":compose:foundation:foundation-layout"))
implementation(project(":compose:material:material"))
implementation(project(":compose:runtime:runtime"))
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/AnimatedVectorSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/AnimatedVectorSample.kt
deleted file mode 100644
index ecacbd2..0000000
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/AnimatedVectorSample.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2021 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.ui.samples
-
-import androidx.annotation.DrawableRes
-import androidx.annotation.Sampled
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.animatedVectorResource
-import androidx.compose.ui.unit.dp
-
-@Sampled
-@Composable
-fun AnimatedVectorSample() {
-
- @OptIn(ExperimentalComposeUiApi::class)
- @Composable
- fun AnimatedVector(@DrawableRes drawableId: Int) {
- val image = animatedVectorResource(drawableId)
- var atEnd by remember { mutableStateOf(false) }
- Image(
- painter = image.painterFor(atEnd),
- contentDescription = "Your content description",
- modifier = Modifier.size(64.dp).clickable {
- atEnd = !atEnd
- }
- )
- }
-}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/AnimatedImageVectorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/AnimatedImageVectorTest.kt
deleted file mode 100644
index 3d11dc0..0000000
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/AnimatedImageVectorTest.kt
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright 2021 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.ui.graphics.vector
-
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.RepeatMode
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import com.google.common.truth.Truth.assertThat
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalComposeUiApi::class)
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-class AnimatedImageVectorTest {
-
- @get:Rule
- val composeTestRule = createComposeRule()
-
- private val pathStart: PathBuilder.() -> Unit = {
- horizontalLineTo(50f)
- verticalLineTo(50f)
- horizontalLineTo(0f)
- close()
- }
-
- private val pathEnd: PathBuilder.() -> Unit = {
- horizontalLineTo(10f)
- verticalLineTo(10f)
- horizontalLineTo(0f)
- close()
- }
-
- private val topLeftRed = ImageVector.Builder(
- name = "image",
- defaultWidth = 100.dp,
- defaultHeight = 100.dp,
- viewportWidth = 100f,
- viewportHeight = 100f
- ).apply {
- group(
- name = "group",
- pivotX = 50f,
- pivotY = 50f
- ) {
- // Top left is red.
- path(name = "path", fill = SolidColor(Color.Red), pathBuilder = pathStart)
- }
- }.build()
-
- private val groupRotation = AnimatedVectorTarget(
- name = "group",
- animator = ObjectAnimator(
- duration = 1000,
- startDelay = 0,
- repeatCount = 0,
- repeatMode = RepeatMode.Restart,
- holders = listOf(
- PropertyValuesHolderFloat(
- propertyName = "rotation",
- animatorKeyframes = listOf(
- Keyframe(0f, 0f, LinearEasing),
- Keyframe(1f, 360f, LinearEasing)
- )
- )
- )
- )
- )
-
- private val pathData = AnimatedVectorTarget(
- name = "path",
- animator = ObjectAnimator(
- duration = 1000,
- startDelay = 0,
- repeatCount = 0,
- repeatMode = RepeatMode.Restart,
- holders = listOf(
- PropertyValuesHolderPath(
- propertyName = "pathData",
- animatorKeyframes = listOf(
- Keyframe(0f, PathData(pathStart), LinearEasing),
- Keyframe(1f, PathData(pathEnd), LinearEasing)
- )
- )
- )
- )
- )
-
- private val delta = 0.001f
-
- @Test
- fun rotation() {
- composeTestRule.mainClock.autoAdvance = false
- val image = AnimatedImageVector(
- imageVector = topLeftRed,
- targets = listOf(groupRotation)
- )
- var override: VectorOverride? = null
- var state by mutableStateOf(false)
- composeTestRule.setContent {
- image.painterFor(state) { _, map ->
- override = map["group"]
- }
- }
- val impossibleValue = 1234f
- assertThat(override!!.obtainRotation(impossibleValue)).isWithin(delta).of(0f)
- assertThat(override!!.obtainTranslateX(impossibleValue)).isWithin(delta).of(impossibleValue)
- composeTestRule.runOnUiThread { state = true }
- // TODO(yaraki): Use deterministic animation testing framework to test intermediate values.
- composeTestRule.mainClock.advanceTimeBy(1500)
- composeTestRule.waitForIdle()
- assertThat(override!!.obtainRotation(impossibleValue)).isWithin(delta).of(360f)
- }
-
- @Test
- fun pathData() {
- composeTestRule.mainClock.autoAdvance = false
- val image = AnimatedImageVector(
- imageVector = topLeftRed,
- targets = listOf(pathData)
- )
- var override: VectorOverride? = null
- var state by mutableStateOf(false)
- composeTestRule.setContent {
- image.painterFor(state) { _, map ->
- override = map["path"]
- }
- }
- assertThat(override!!.obtainPathData(emptyList())).isEqualTo(PathData(pathStart))
- composeTestRule.runOnUiThread { state = true }
- // TODO(yaraki): Use deterministic animation testing framework to test intermediate values.
- composeTestRule.mainClock.advanceTimeBy(1500)
- composeTestRule.waitForIdle()
- assertThat(override!!.obtainPathData(emptyList())).isEqualTo(PathData(pathEnd))
- }
-}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatedVectorParserTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatedVectorParserTest.kt
deleted file mode 100644
index 723310c..0000000
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatedVectorParserTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2021 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.ui.graphics.vector.compat
-
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.graphics.vector.AnimatorSet
-import androidx.compose.ui.graphics.vector.ObjectAnimator
-import androidx.compose.ui.graphics.vector.PropertyValuesHolderColor
-import androidx.compose.ui.res.loadAnimatedVectorResource
-import androidx.compose.ui.test.R
-import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalComposeUiApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class XmlAnimatedVectorParserTest {
-
- @Test
- fun load() {
- val context = InstrumentationRegistry.getInstrumentation().targetContext
- val resources = context.resources
- val avd = loadAnimatedVectorResource(
- context.theme,
- resources,
- R.drawable.avd_complex
- )
-
- val delta = 0.001f
- assertThat(avd.imageVector.defaultWidth).isEqualTo(24.dp)
- assertThat(avd.imageVector.defaultHeight).isEqualTo(24.dp)
- assertThat(avd.imageVector.viewportWidth).isWithin(delta).of(24f)
- assertThat(avd.imageVector.viewportHeight).isWithin(delta).of(24f)
-
- assertThat(avd.targets).hasSize(1)
-
- avd.targets[0].let { target ->
- assertThat(target.name).isEqualTo("background")
- assertThat(target.animator).isInstanceOf(AnimatorSet::class.java)
- (target.animator as AnimatorSet).let { set ->
- assertThat(set.animators).hasSize(1)
- (set.animators[0] as ObjectAnimator).let { a ->
- assertThat(a.duration).isEqualTo(123)
- assertThat(a.holders).hasSize(1)
- (a.holders[0] as PropertyValuesHolderColor).let { holder ->
- assertThat(holder.propertyName).isEqualTo("fillColor")
- }
- }
- }
- }
- }
-}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatorParserTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatorParserTest.kt
deleted file mode 100644
index ab2b56f8..0000000
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatorParserTest.kt
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright 2021 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.ui.graphics.vector.compat
-
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.RepeatMode
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.vector.AnimatorSet
-import androidx.compose.ui.graphics.vector.ObjectAnimator
-import androidx.compose.ui.graphics.vector.Ordering
-import androidx.compose.ui.graphics.vector.PropertyValuesHolder2D
-import androidx.compose.ui.graphics.vector.PropertyValuesHolderColor
-import androidx.compose.ui.graphics.vector.PropertyValuesHolderFloat
-import androidx.compose.ui.graphics.vector.PropertyValuesHolderInt
-import androidx.compose.ui.graphics.vector.PropertyValuesHolderPath
-import androidx.compose.ui.res.AccelerateEasing
-import androidx.compose.ui.res.DecelerateEasing
-import androidx.compose.ui.res.loadAnimatorResource
-import androidx.compose.ui.test.R
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class XmlAnimatorParserTest {
-
- @Test
- fun objectAnimator1D() {
- val context = InstrumentationRegistry.getInstrumentation().targetContext
- val resources = context.resources
- val delta = 0.001f
- val a = loadAnimatorResource(
- context.theme,
- resources,
- R.animator.object_animator_1d
- )
- assertThat(a).isInstanceOf(ObjectAnimator::class.java)
- val oa = a as ObjectAnimator
- assertThat(oa.duration).isEqualTo(333)
- assertThat(oa.repeatCount).isEqualTo(1)
- assertThat(oa.repeatMode).isEqualTo(RepeatMode.Reverse)
- assertThat(oa.startDelay).isEqualTo(50)
- assertThat(oa.holders).hasSize(1)
- assertThat(oa.totalDuration).isEqualTo(716)
- val holder = oa.holders[0] as PropertyValuesHolderFloat
- assertThat(holder.propertyName).isEqualTo("translateX")
- assertThat(holder.animatorKeyframes).hasSize(2)
- assertThat(holder.animatorKeyframes[0].value).isWithin(delta).of(0f)
- assertThat(holder.animatorKeyframes[1].value).isWithin(delta).of(100f)
- }
-
- @Test
- fun objectAnimator2D() {
- val context = InstrumentationRegistry.getInstrumentation().targetContext
- val resources = context.resources
- val a = loadAnimatorResource(
- context.theme,
- resources,
- R.animator.object_animator_2d
- )
- assertThat(a).isInstanceOf(ObjectAnimator::class.java)
- val oa = a as ObjectAnimator
- assertThat(oa.duration).isEqualTo(333)
- assertThat(oa.holders).hasSize(1)
- assertThat(oa.totalDuration).isEqualTo(333)
- val holder = oa.holders[0] as PropertyValuesHolder2D
- assertThat(holder.xPropertyName).isEqualTo("translateX")
- assertThat(holder.yPropertyName).isEqualTo("translateY")
- assertThat(holder.pathData).hasSize(3)
- assertThat(holder.interpolator).isEqualTo(LinearEasing)
- }
-
- @Test
- fun propertyValuesHolders() {
- val context = InstrumentationRegistry.getInstrumentation().targetContext
- val resources = context.resources
- val delta = 0.001f
- val a = loadAnimatorResource(
- context.theme,
- resources,
- R.animator.property_values_holders
- )
- assertThat(a).isInstanceOf(ObjectAnimator::class.java)
- val oa = a as ObjectAnimator
- assertThat(oa.duration).isEqualTo(333)
- assertThat(oa.repeatCount).isEqualTo(1)
- assertThat(oa.repeatMode).isEqualTo(RepeatMode.Reverse)
- assertThat(oa.startDelay).isEqualTo(50)
- assertThat(oa.holders).hasSize(5)
- assertThat(oa.totalDuration).isEqualTo(716)
- (oa.holders[0] as PropertyValuesHolderFloat).let { holder ->
- assertThat(holder.propertyName).isEqualTo("translateX")
- assertThat(holder.animatorKeyframes).hasSize(2)
- assertThat(holder.animatorKeyframes[0].value).isWithin(delta).of(0f)
- assertThat(holder.animatorKeyframes[1].value).isWithin(delta).of(100f)
- }
- (oa.holders[1] as PropertyValuesHolderFloat).let { holder ->
- assertThat(holder.propertyName).isEqualTo("translateY")
- assertThat(holder.animatorKeyframes).hasSize(4)
- holder.animatorKeyframes[0].let { keyframe ->
- assertThat(keyframe.fraction).isWithin(delta).of(0f)
- assertThat(keyframe.value).isWithin(delta).of(0f)
- }
- holder.animatorKeyframes[1].let { keyframe ->
- assertThat(keyframe.fraction).isWithin(delta).of(0.3f)
- assertThat(keyframe.value).isWithin(delta).of(150f)
- assertThat(keyframe.interpolator).isEqualTo(DecelerateEasing)
- }
- holder.animatorKeyframes[2].let { keyframe ->
- assertThat(keyframe.fraction).isWithin(delta).of(0.6f)
- assertThat(keyframe.value).isWithin(delta).of(50f)
- assertThat(keyframe.interpolator).isEqualTo(AccelerateEasing)
- }
- holder.animatorKeyframes[3].let { keyframe ->
- assertThat(keyframe.fraction).isWithin(delta).of(1f)
- assertThat(keyframe.value).isWithin(delta).of(200f)
- }
- }
- (oa.holders[2] as PropertyValuesHolderColor).let { holder ->
- assertThat(holder.propertyName).isEqualTo("colorProperty")
- assertThat(holder.animatorKeyframes).hasSize(2)
- assertThat(holder.animatorKeyframes[0].value).isEqualTo(Color.Red)
- assertThat(holder.animatorKeyframes[1].value).isEqualTo(Color.Blue)
- }
- (oa.holders[3] as PropertyValuesHolderInt).let { holder ->
- assertThat(holder.propertyName).isEqualTo("intProperty")
- assertThat(holder.animatorKeyframes).hasSize(2)
- assertThat(holder.animatorKeyframes[0].value).isEqualTo(500)
- assertThat(holder.animatorKeyframes[1].value).isEqualTo(1000)
- }
- (oa.holders[4] as PropertyValuesHolderPath).let { holder ->
- assertThat(holder.propertyName).isEqualTo("pathProperty")
- assertThat(holder.animatorKeyframes).hasSize(2)
- assertThat(holder.animatorKeyframes[0].value).hasSize(3)
- assertThat(holder.animatorKeyframes[1].value).hasSize(3)
- }
- }
-
- @Test
- fun set() {
- val context = InstrumentationRegistry.getInstrumentation().targetContext
- val resources = context.resources
- val anim = loadAnimatorResource(
- context.theme,
- resources,
- R.animator.set
- )
- assertThat(anim).isInstanceOf(AnimatorSet::class.java)
- val set = anim as AnimatorSet
- assertThat(set.ordering).isEqualTo(Ordering.Together)
- assertThat(set.animators).hasSize(2)
- assertThat(set.totalDuration).isEqualTo(300)
- (set.animators[0] as ObjectAnimator).let { oa ->
- assertThat(oa.duration).isEqualTo(300)
- assertThat(oa.repeatCount).isEqualTo(0)
- assertThat(oa.startDelay).isEqualTo(0)
- assertThat(oa.holders).hasSize(1)
- (oa.holders[0] as PropertyValuesHolderFloat).let { holder ->
- assertThat(holder.propertyName).isEqualTo("floatProperty")
- }
- }
- (set.animators[1] as AnimatorSet).let { s ->
- assertThat(s.ordering).isEqualTo(Ordering.Sequentially)
- assertThat(s.animators).hasSize(2)
- (s.animators[0] as ObjectAnimator).let { oa ->
- assertThat(oa.holders).hasSize(1)
- (oa.holders[0] as PropertyValuesHolderInt).let { holder ->
- assertThat(holder.propertyName).isEqualTo("intProperty")
- }
- }
- (s.animators[1] as ObjectAnimator).let { oa ->
- assertThat(oa.holders).hasSize(1)
- (oa.holders[0] as PropertyValuesHolderColor).let { holder ->
- assertThat(holder.propertyName).isEqualTo("colorProperty")
- }
- }
- }
- }
-}
diff --git a/compose/ui/ui/src/androidAndroidTest/res/animator/complex_background.xml b/compose/ui/ui/src/androidAndroidTest/res/animator/complex_background.xml
deleted file mode 100644
index 3de4e5b..0000000
--- a/compose/ui/ui/src/androidAndroidTest/res/animator/complex_background.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:duration="123"
- android:propertyName="fillColor"
- android:valueFrom="#777777"
- android:valueTo="#999999"
- android:valueType="colorType" />
-</set>
diff --git a/compose/ui/ui/src/androidAndroidTest/res/animator/object_animator_1d.xml b/compose/ui/ui/src/androidAndroidTest/res/animator/object_animator_1d.xml
deleted file mode 100644
index ab5497f..0000000
--- a/compose/ui/ui/src/androidAndroidTest/res/animator/object_animator_1d.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:duration="333"
- android:interpolator="@android:anim/linear_interpolator"
- android:propertyName="translateX"
- android:repeatCount="1"
- android:repeatMode="reverse"
- android:startOffset="50"
- android:valueFrom="0"
- android:valueTo="100"
- android:valueType="floatType" />
diff --git a/compose/ui/ui/src/androidAndroidTest/res/animator/object_animator_2d.xml b/compose/ui/ui/src/androidAndroidTest/res/animator/object_animator_2d.xml
deleted file mode 100644
index 01af795..0000000
--- a/compose/ui/ui/src/androidAndroidTest/res/animator/object_animator_2d.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:duration="333"
- android:interpolator="@android:anim/linear_interpolator"
- android:pathData="L30,40 L70,60 L100,100"
- android:propertyXName="translateX"
- android:propertyYName="translateY" />
diff --git a/compose/ui/ui/src/androidAndroidTest/res/animator/property_values_holders.xml b/compose/ui/ui/src/androidAndroidTest/res/animator/property_values_holders.xml
deleted file mode 100644
index d3e86d6..0000000
--- a/compose/ui/ui/src/androidAndroidTest/res/animator/property_values_holders.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2020 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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:duration="333"
- android:interpolator="@android:anim/linear_interpolator"
- android:repeatCount="1"
- android:repeatMode="reverse"
- android:startOffset="50">
- <propertyValuesHolder
- android:propertyName="translateX"
- android:valueFrom="0"
- android:valueTo="100"
- android:valueType="floatType" />
- <propertyValuesHolder
- android:propertyName="translateY"
- android:valueFrom="0"
- android:valueTo="200">
- <keyframe
- android:fraction="0.3"
- android:interpolator="@android:anim/decelerate_interpolator"
- android:value="150" />
- <keyframe
- android:fraction="0.6"
- android:interpolator="@android:anim/accelerate_interpolator"
- android:value="50" />
- </propertyValuesHolder>
- <propertyValuesHolder
- android:propertyName="colorProperty"
- android:valueFrom="#ff0000"
- android:valueTo="#0000ff"
- android:valueType="colorType" />
- <propertyValuesHolder
- android:propertyName="intProperty"
- android:valueFrom="500"
- android:valueTo="1000"
- android:valueType="intType" />
- <propertyValuesHolder
- android:propertyName="pathProperty"
- android:valueFrom="L24,0 L24,24 Z"
- android:valueTo="L24,24 L0,24 Z"
- android:valueType="pathType" />
-</objectAnimator>
diff --git a/compose/ui/ui/src/androidAndroidTest/res/animator/set.xml b/compose/ui/ui/src/androidAndroidTest/res/animator/set.xml
deleted file mode 100644
index 4567f01..0000000
--- a/compose/ui/ui/src/androidAndroidTest/res/animator/set.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2020 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:duration="300"
- android:propertyName="floatProperty"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- <set android:ordering="sequentially">
- <objectAnimator
- android:duration="100"
- android:propertyName="intProperty"
- android:valueFrom="100"
- android:valueTo="200"
- android:valueType="intType" />
- <objectAnimator
- android:duration="200"
- android:propertyName="colorProperty"
- android:valueFrom="#ff0000"
- android:valueTo="#0000ff"
- android:valueType="colorType" />
- </set>
-</set>
diff --git a/compose/ui/ui/src/androidAndroidTest/res/drawable/avd_complex.xml b/compose/ui/ui/src/androidAndroidTest/res/drawable/avd_complex.xml
deleted file mode 100644
index 18ff685..0000000
--- a/compose/ui/ui/src/androidAndroidTest/res/drawable/avd_complex.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 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.
--->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/vd_complex">
-
- <target
- android:name="background"
- android:animation="@animator/complex_background" />
-
-</animated-vector>
diff --git a/compose/ui/ui/src/androidAndroidTest/res/drawable/vd_complex.xml b/compose/ui/ui/src/androidAndroidTest/res/drawable/vd_complex.xml
deleted file mode 100644
index 3a96ae2..0000000
--- a/compose/ui/ui/src/androidAndroidTest/res/drawable/vd_complex.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:name="background"
- android:fillColor="#777777"
- android:pathData="L24,0 24,24 0,24Z" />
-</vector>
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatedVectorParser.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatedVectorParser.android.kt
deleted file mode 100644
index db3ae9a..0000000
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatedVectorParser.android.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2021 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.ui.graphics.vector.compat
-
-import android.content.res.Resources
-import android.util.AttributeSet
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.graphics.vector.AnimatedVectorTarget
-import androidx.compose.ui.graphics.vector.AnimatedImageVector
-import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.res.loadAnimatorResource
-import androidx.compose.ui.res.vectorResource
-import org.xmlpull.v1.XmlPullParser
-
-private const val TagAnimatedVector = "animated-vector"
-private const val TagAnimatedVectorTarget = "target"
-
-private fun parseAnimatedVectorTarget(
- res: Resources,
- theme: Resources.Theme?,
- attrs: AttributeSet
-): AnimatedVectorTarget {
- return attrs.attrs(
- res, theme, AndroidVectorResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET
- ) { a ->
- AnimatedVectorTarget(
- a.getString(
- AndroidVectorResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET_NAME
- ) ?: "",
- loadAnimatorResource(
- theme,
- res,
- a.getResourceId(
- AndroidVectorResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET_ANIMATION,
- 0
- )
- )
- )
- }
-}
-
-@ExperimentalComposeUiApi
-internal fun XmlPullParser.parseAnimatedImageVector(
- res: Resources,
- theme: Resources.Theme?,
- attrs: AttributeSet
-): AnimatedImageVector {
- return attrs.attrs(res, theme, AndroidVectorResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE) { a ->
- val drawableId = a.getResourceId(
- AndroidVectorResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_DRAWABLE,
- 0
- )
- val targets = mutableListOf<AnimatedVectorTarget>()
- forEachChildOf(TagAnimatedVector) {
- if (eventType == XmlPullParser.START_TAG && name == TagAnimatedVectorTarget) {
- targets.add(parseAnimatedVectorTarget(res, theme, attrs))
- }
- }
- AnimatedImageVector(
- ImageVector.vectorResource(theme, res, drawableId),
- targets
- )
- }
-}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatorParser.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatorParser.android.kt
deleted file mode 100644
index f2126d7..0000000
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlAnimatorParser.android.kt
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * Copyright 2021 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.ui.graphics.vector.compat
-
-import android.content.res.Resources
-import android.content.res.TypedArray
-import android.util.AttributeSet
-import android.util.TypedValue
-import android.view.animation.PathInterpolator
-import androidx.compose.animation.core.CubicBezierEasing
-import androidx.compose.animation.core.Easing
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.RepeatMode
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.vector.Animator
-import androidx.compose.ui.graphics.vector.AnimatorSet
-import androidx.compose.ui.graphics.vector.Keyframe
-import androidx.compose.ui.graphics.vector.ObjectAnimator
-import androidx.compose.ui.graphics.vector.Ordering
-import androidx.compose.ui.graphics.vector.PathNode
-import androidx.compose.ui.graphics.vector.PropertyValuesHolder
-import androidx.compose.ui.graphics.vector.PropertyValuesHolder1D
-import androidx.compose.ui.graphics.vector.PropertyValuesHolder2D
-import androidx.compose.ui.graphics.vector.PropertyValuesHolderColor
-import androidx.compose.ui.graphics.vector.PropertyValuesHolderFloat
-import androidx.compose.ui.graphics.vector.PropertyValuesHolderInt
-import androidx.compose.ui.graphics.vector.PropertyValuesHolderPath
-import androidx.compose.ui.graphics.vector.addPathNodes
-import androidx.compose.ui.res.AccelerateDecelerateEasing
-import androidx.compose.ui.res.AccelerateEasing
-import androidx.compose.ui.res.AnticipateEasing
-import androidx.compose.ui.res.AnticipateOvershootEasing
-import androidx.compose.ui.res.BounceEasing
-import androidx.compose.ui.res.CycleEasing
-import androidx.compose.ui.res.DecelerateEasing
-import androidx.compose.ui.res.OvershootEasing
-import androidx.compose.ui.res.loadInterpolatorResource
-import androidx.compose.ui.res.toEasing
-import androidx.core.graphics.PathParser
-import org.xmlpull.v1.XmlPullParser
-
-internal const val TagSet = "set"
-internal const val TagObjectAnimator = "objectAnimator"
-private const val TagPropertyValuesHolder = "propertyValuesHolder"
-private const val TagKeyframe = "keyframe"
-
-private const val ValueTypeFloat = 0
-private const val ValueTypeInt = 1
-private const val ValueTypePath = 2
-private const val ValueTypeColor = 3
-private const val ValueTypeUndefined = 4
-
-private const val RepeatModeReverse = 2
-
-private enum class ValueType {
- Float,
- Int,
- Color,
- Path
-}
-
-private val FallbackValueType = ValueType.Float
-
-private fun TypedArray.getInterpolator(
- res: Resources,
- theme: Resources.Theme?,
- index: Int,
- defaultValue: Easing
-): Easing {
- val id = getResourceId(index, 0)
- return if (id == 0) {
- defaultValue
- } else {
- loadInterpolatorResource(theme, res, id)
- }
-}
-
-private fun parseKeyframe(
- res: Resources,
- theme: Resources.Theme?,
- attrs: AttributeSet,
- holderValueType: ValueType?,
- defaultInterpolator: Easing
-): Pair<Keyframe<Any>, ValueType> {
- return attrs.attrs(res, theme, AndroidVectorResources.STYLEABLE_KEYFRAME) { a ->
- val inferredValueType =
- // The type is specified in <propertyValuesHolder>.
- holderValueType
- ?: inferValueType( // Identify the type from our attribute values.
- a.getInt(
- AndroidVectorResources.STYLEABLE_KEYFRAME_VALUE_TYPE,
- ValueTypeUndefined
- ),
- a.peekValue(AndroidVectorResources.STYLEABLE_KEYFRAME_VALUE).type
- )
- // We didn't have any clue until the end.
- ?: FallbackValueType
- a.getKeyframe(
- a.getFloat(AndroidVectorResources.STYLEABLE_KEYFRAME_FRACTION, 0f),
- a.getInterpolator(
- res,
- theme,
- AndroidVectorResources.STYLEABLE_KEYFRAME_INTERPOLATOR,
- defaultInterpolator
- ),
- inferredValueType,
- AndroidVectorResources.STYLEABLE_KEYFRAME_VALUE
- ) to inferredValueType // Report back the type to <propertyValuesHolder>.
- }
-}
-
-/**
- * Extracts a [Keyframe] value from this [TypedArray]. This [TypedArray] can come from either
- * `<propertyValuesHolder>` or `<keyframe>`
- */
-private fun TypedArray.getKeyframe(
- fraction: Float,
- interpolator: Easing,
- valueType: ValueType,
- valueIndex: Int
-): Keyframe<Any> {
- return when (valueType) {
- ValueType.Float -> Keyframe(
- fraction,
- getFloat(valueIndex, 0f),
- interpolator
- )
- ValueType.Int -> Keyframe(
- fraction,
- getInt(valueIndex, 0),
- interpolator
- )
- ValueType.Color -> Keyframe(
- fraction,
- Color(getColor(valueIndex, 0)),
- interpolator
- )
- ValueType.Path -> Keyframe(
- fraction,
- addPathNodes(getString(valueIndex)),
- interpolator
- )
- }
-}
-
-private fun XmlPullParser.parsePropertyValuesHolder(
- res: Resources,
- theme: Resources.Theme?,
- attrs: AttributeSet,
- interpolator: Easing
-): PropertyValuesHolder<*> {
- return attrs.attrs(res, theme, AndroidVectorResources.STYLEABLE_PROPERTY_VALUES_HOLDER) { a ->
- a.getPropertyValuesHolder1D(
- a.getString(
- AndroidVectorResources.STYLEABLE_PROPERTY_VALUES_HOLDER_PROPERTY_NAME
- )!!,
- AndroidVectorResources.STYLEABLE_PROPERTY_VALUES_HOLDER_VALUE_TYPE,
- AndroidVectorResources.STYLEABLE_PROPERTY_VALUES_HOLDER_VALUE_FROM,
- AndroidVectorResources.STYLEABLE_PROPERTY_VALUES_HOLDER_VALUE_TO,
- interpolator
- ) { valueType, keyframes ->
- var vt: ValueType? = null
- forEachChildOf(TagPropertyValuesHolder) {
- if (eventType == XmlPullParser.START_TAG && name == TagKeyframe) {
- val (keyframe, keyframeValueType) =
- parseKeyframe(res, theme, attrs, valueType, interpolator)
- if (vt == null) vt = keyframeValueType
- keyframes.add(keyframe)
- }
- }
- // This determines the final ValueType of the PropertyValuesHolder.
- vt ?: valueType ?: FallbackValueType
- }
- }
-}
-
-/**
- * Infers a [ValueType] from various information from XML.
- *
- * @param valueType The `valueType` attribute specified in the XML.
- * @param typedValueTypes [TypedValue.type] values taken from multiple [TypedValue]s.
- * @return A [ValueType] identified by the information so far, or `null` if it is uncertain.
- */
-private fun inferValueType(valueType: Int, vararg typedValueTypes: Int): ValueType? {
- return when (valueType) {
- ValueTypeFloat -> ValueType.Float
- ValueTypeInt -> ValueType.Int
- ValueTypeColor -> ValueType.Color
- ValueTypePath -> ValueType.Path
- else ->
- if (
- typedValueTypes
- .all {
- it in TypedValue.TYPE_FIRST_COLOR_INT..TypedValue.TYPE_LAST_COLOR_INT
- }
- ) {
- ValueType.Color
- } else {
- null
- }
- }
-}
-
-/**
- * Extracts attribute values related to [PropertyValuesHolder]. This [TypedArray] can be taken from
- * either `<objectAnimator>` or `<propertyValuesHolder>`.
- *
- * @param parseKeyframes The caller should parse `<keyframe>`s inside of this
- * `<propertyValuesHolder>` and store them in the `keyframes` [MutableList]. The lambda receives
- * a [ValueType] if it has been identified so far. The lambda has to return [ValueType] in case it
- * is first identified while parsing keyframes.
- */
-private fun TypedArray.getPropertyValuesHolder1D(
- propertyName: String,
- valueTypeIndex: Int,
- valueFromIndex: Int,
- valueToIndex: Int,
- interpolator: Easing,
- parseKeyframes: (
- valueType: ValueType?,
- keyframes: MutableList<Keyframe<Any>>
- ) -> ValueType = { vt, _ -> vt ?: FallbackValueType }
-): PropertyValuesHolder1D<*> {
- val valueType = getInt(
- valueTypeIndex,
- ValueTypeUndefined
- )
-
- val valueFrom = peekValue(valueFromIndex)
- val hasFrom = valueFrom != null
- val typeFrom = valueFrom?.type ?: ValueTypeUndefined
-
- val valueTo = peekValue(valueToIndex)
- val hasTo = valueTo != null
- val typeTo = valueTo?.type ?: ValueTypeUndefined
-
- var inferredValueType =
- inferValueType(
- valueType,
- typeFrom,
- typeTo
- )
- val keyframes = mutableListOf<Keyframe<Any>>()
- if (inferredValueType == null && (hasFrom || hasTo)) {
- inferredValueType =
- ValueType.Float
- }
- if (hasFrom) {
- keyframes.add(getKeyframe(0f, interpolator, inferredValueType!!, valueFromIndex))
- }
- if (hasTo) {
- keyframes.add(getKeyframe(1f, interpolator, inferredValueType!!, valueToIndex))
- }
- inferredValueType = parseKeyframes(inferredValueType, keyframes)
- keyframes.sortBy { it.fraction }
- @Suppress("UNCHECKED_CAST")
- return when (inferredValueType) {
- ValueType.Float -> PropertyValuesHolderFloat(
- propertyName,
- keyframes as List<Keyframe<Float>>
- )
- ValueType.Int -> PropertyValuesHolderInt(
- propertyName,
- keyframes as List<Keyframe<Int>>
- )
- ValueType.Color -> PropertyValuesHolderColor(
- propertyName,
- keyframes as List<Keyframe<Color>>
- )
- ValueType.Path -> PropertyValuesHolderPath(
- propertyName,
- keyframes as List<Keyframe<List<PathNode>>>
- )
- }
-}
-
-private fun convertRepeatMode(repeatMode: Int) = when (repeatMode) {
- RepeatModeReverse -> RepeatMode.Reverse
- else -> RepeatMode.Restart
-}
-
-internal fun XmlPullParser.parseObjectAnimator(
- res: Resources,
- theme: Resources.Theme?,
- attrs: AttributeSet
-): ObjectAnimator {
- return attrs.attrs(res, theme, AndroidVectorResources.STYLEABLE_ANIMATOR) { a ->
- attrs.attrs(res, theme, AndroidVectorResources.STYLEABLE_PROPERTY_ANIMATOR) { oa ->
- val interpolator = a.getInterpolator(
- res,
- theme,
- AndroidVectorResources.STYLEABLE_ANIMATOR_INTERPOLATOR,
- AccelerateDecelerateEasing
- )
- val holders = mutableListOf<PropertyValuesHolder<*>>()
- // 2D; This <objectAnimator> has `propertyXName`, `propertyYName`, and `pathData`.
- oa.getString(
- AndroidVectorResources.STYLEABLE_PROPERTY_ANIMATOR_PATH_DATA
- )?.let { pathData ->
- holders.add(
- PropertyValuesHolder2D(
- oa.getString(
- AndroidVectorResources.STYLEABLE_PROPERTY_ANIMATOR_PROPERTY_X_NAME
- )!!,
- oa.getString(
- AndroidVectorResources.STYLEABLE_PROPERTY_ANIMATOR_PROPERTY_Y_NAME
- )!!,
- addPathNodes(pathData),
- interpolator
- )
- )
- }
- // 1D; This <objectAnimator> has `propertyName`, `valueFrom`, and `valueTo`.
- oa.getString(
- AndroidVectorResources.STYLEABLE_PROPERTY_ANIMATOR_PROPERTY_NAME
- )?.let { propertyName ->
- holders.add(
- a.getPropertyValuesHolder1D(
- propertyName,
- AndroidVectorResources.STYLEABLE_ANIMATOR_VALUE_TYPE,
- AndroidVectorResources.STYLEABLE_ANIMATOR_VALUE_FROM,
- AndroidVectorResources.STYLEABLE_ANIMATOR_VALUE_TO,
- interpolator
- )
- )
- }
- // This <objectAnimator> has <propertyValuesHolder> inside.
- forEachChildOf(TagObjectAnimator) {
- if (eventType == XmlPullParser.START_TAG && name == TagPropertyValuesHolder) {
- holders.add(parsePropertyValuesHolder(res, theme, attrs, interpolator))
- }
- }
-
- ObjectAnimator(
- duration = a.getInt(AndroidVectorResources.STYLEABLE_ANIMATOR_DURATION, 300),
- startDelay = a.getInt(AndroidVectorResources.STYLEABLE_ANIMATOR_START_OFFSET, 0),
- repeatCount = a.getInt(AndroidVectorResources.STYLEABLE_ANIMATOR_REPEAT_COUNT, 0),
- repeatMode = convertRepeatMode(
- a.getInt(AndroidVectorResources.STYLEABLE_ANIMATOR_REPEAT_MODE, 0)
- ),
- holders = holders
- )
- }
- }
-}
-
-internal fun XmlPullParser.parseAnimatorSet(
- res: Resources,
- theme: Resources.Theme?,
- attrs: AttributeSet
-): AnimatorSet {
- return attrs.attrs(res, theme, AndroidVectorResources.STYLEABLE_ANIMATOR_SET) { a ->
- val ordering = a.getInt(AndroidVectorResources.STYLEABLE_ANIMATOR_SET_ORDERING, 0)
- val animators = mutableListOf<Animator>()
- forEachChildOf(TagSet) {
- if (eventType == XmlPullParser.START_TAG) {
- when (name) {
- TagSet -> animators.add(parseAnimatorSet(res, theme, attrs))
- TagObjectAnimator -> animators.add(parseObjectAnimator(res, theme, attrs))
- }
- }
- }
- AnimatorSet(
- animators,
- if (ordering != 0) Ordering.Sequentially else Ordering.Together
- )
- }
-}
-
-internal fun XmlPullParser.parseInterpolator(
- res: Resources,
- theme: Resources.Theme?,
- attrs: AttributeSet
-): Easing {
- return when (name) {
- "linearInterpolator" -> LinearEasing
- "accelerateInterpolator" ->
- attrs.attrs(
- res, theme, AndroidVectorResources.STYLEABLE_ACCELERATE_INTERPOLATOR
- ) { a ->
- val factor = a.getFloat(
- AndroidVectorResources.STYLEABLE_ACCELERATE_INTERPOLATOR_FACTOR, 1.0f
- )
- if (factor == 1.0f) AccelerateEasing else AccelerateEasing(factor)
- }
- "decelerateInterpolator" ->
- attrs.attrs(
- res, theme, AndroidVectorResources.STYLEABLE_DECELERATE_INTERPOLATOR
- ) { a ->
- val factor = a.getFloat(
- AndroidVectorResources.STYLEABLE_DECELERATE_INTERPOLATOR_FACTOR, 1.0f
- )
- if (factor == 1.0f) DecelerateEasing else DecelerateEasing(factor)
- }
- "accelerateDecelerateInterpolator" -> AccelerateDecelerateEasing
- "cycleInterpolator" ->
- attrs.attrs(
- res, theme, AndroidVectorResources.STYLEABLE_CYCLE_INTERPOLATOR
- ) { a ->
- CycleEasing(
- a.getFloat(
- AndroidVectorResources.STYLEABLE_CYCLE_INTERPOLATOR_CYCLES, 1.0f
- )
- )
- }
- "anticipateInterpolator" ->
- attrs.attrs(
- res, theme, AndroidVectorResources.STYLEABLE_ANTICIPATEOVERSHOOT_INTERPOLATOR
- ) { a ->
- AnticipateEasing(
- a.getFloat(
- AndroidVectorResources.STYLEABLE_ANTICIPATEOVERSHOOT_INTERPOLATOR_TENSION,
- 2.0f
- )
- )
- }
- "overshootInterpolator" ->
- attrs.attrs(
- res, theme, AndroidVectorResources.STYLEABLE_OVERSHOOT_INTERPOLATOR
- ) { a ->
- OvershootEasing(
- a.getFloat(
- AndroidVectorResources.STYLEABLE_OVERSHOOT_INTERPOLATOR_TENSION, 2.0f
- )
- )
- }
- "anticipateOvershootInterpolator" ->
- attrs.attrs(
- res, theme, AndroidVectorResources.STYLEABLE_ANTICIPATEOVERSHOOT_INTERPOLATOR
- ) { a ->
- AnticipateOvershootEasing(
- a.getFloat(
- AndroidVectorResources.STYLEABLE_ANTICIPATEOVERSHOOT_INTERPOLATOR_TENSION,
- 2.0f
- ),
- a.getFloat(
- AndroidVectorResources
- .STYLEABLE_ANTICIPATEOVERSHOOT_INTERPOLATOR_EXTRA_TENSION,
- 1.5f
- )
- )
- }
- "bounceInterpolator" -> BounceEasing
- "pathInterpolator" ->
- attrs.attrs(
- res, theme, AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR
- ) { a ->
- val pathData =
- a.getString(AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR_PATH_DATA)
- if (pathData != null) {
- PathInterpolator(PathParser.createPathFromPathData(pathData)).toEasing()
- } else if (
- !a.hasValue(AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_X_2) ||
- !a.hasValue(AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_Y_2)
- ) {
- PathInterpolator(
- a.getFloat(
- AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_X_1,
- 0f
- ),
- a.getFloat(
- AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_Y_1,
- 0f
- )
- ).toEasing()
- } else {
- CubicBezierEasing(
- a.getFloat(
- AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_X_1,
- 0f
- ),
- a.getFloat(
- AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_Y_1,
- 0f
- ),
- a.getFloat(
- AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_X_2,
- 1f
- ),
- a.getFloat(
- AndroidVectorResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_Y_2,
- 1f
- )
- )
- }
- }
- else -> throw RuntimeException("Unknown interpolator: $name")
- }
-}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlPullParserUtils.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlPullParserUtils.android.kt
deleted file mode 100644
index e04d0635..0000000
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/graphics/vector/compat/XmlPullParserUtils.android.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2021 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.ui.graphics.vector.compat
-
-import android.content.res.Resources
-import android.content.res.TypedArray
-import android.util.AttributeSet
-import org.xmlpull.v1.XmlPullParser
-
-/**
- * Assuming that we are at the [XmlPullParser.START_TAG start] of the specified [tag], iterates
- * though the events until we see a corresponding [XmlPullParser.END_TAG].
- */
-internal inline fun XmlPullParser.forEachChildOf(
- tag: String,
- action: XmlPullParser.() -> Unit
-) {
- next()
- while (!isAtEnd()) {
- if (eventType == XmlPullParser.END_TAG && name == tag) {
- break
- }
- action()
- next()
- }
-}
-
-internal inline fun <T> AttributeSet.attrs(
- res: Resources,
- theme: Resources.Theme?,
- styleable: IntArray,
- body: (a: TypedArray) -> T
-): T {
- val a = theme?.obtainStyledAttributes(this, styleable, 0, 0)
- ?: res.obtainAttributes(this, styleable)
- try {
- return body(a)
- } finally {
- a.recycle()
- }
-}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/AnimatedVectorResources.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/AnimatedVectorResources.android.kt
deleted file mode 100644
index a9ca5b5..0000000
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/AnimatedVectorResources.android.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2021 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.ui.res
-
-import android.content.res.Resources
-import android.util.Xml
-import androidx.annotation.DrawableRes
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.graphics.vector.AnimatedImageVector
-import androidx.compose.ui.graphics.vector.compat.parseAnimatedImageVector
-import androidx.compose.ui.graphics.vector.compat.seekToStartTag
-import androidx.compose.ui.platform.LocalContext
-import org.xmlpull.v1.XmlPullParserException
-
-/**
- * Load an [AnimatedImageVector] from an Android resource id.
- *
- * Note: This API is transient and will be likely removed for encouraging async resource loading.
- *
- * @param id the resource identifier
- * @return an animated vector drawable resource.
- *
- * @sample androidx.compose.ui.samples.AnimatedVectorSample
- */
-@ExperimentalComposeUiApi
-@Composable
-fun animatedVectorResource(@DrawableRes id: Int): AnimatedImageVector {
- val context = LocalContext.current
- val res = context.resources
- val theme = context.theme
- return remember(id) {
- loadAnimatedVectorResource(theme, res, id)
- }
-}
-
-@ExperimentalComposeUiApi
-@Throws(XmlPullParserException::class)
-internal fun loadAnimatedVectorResource(
- theme: Resources.Theme? = null,
- res: Resources,
- resId: Int
-): AnimatedImageVector {
- val parser = res.getXml(resId).seekToStartTag()
- val attrs = Xml.asAttributeSet(parser)
- return parser.parseAnimatedImageVector(res, theme, attrs)
-}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/AnimatorResources.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/AnimatorResources.android.kt
deleted file mode 100644
index 81b572c..0000000
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/AnimatorResources.android.kt
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2021 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.ui.res
-
-import android.animation.TimeInterpolator
-import android.content.res.Resources
-import android.util.Xml
-import android.view.animation.AnticipateOvershootInterpolator
-import android.view.animation.BounceInterpolator
-import androidx.compose.animation.core.Easing
-import androidx.compose.animation.core.FastOutLinearInEasing
-import androidx.compose.animation.core.FastOutSlowInEasing
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.LinearOutSlowInEasing
-import androidx.compose.ui.graphics.vector.Animator
-import androidx.compose.ui.graphics.vector.compat.AndroidVectorResources
-import androidx.compose.ui.graphics.vector.compat.TagObjectAnimator
-import androidx.compose.ui.graphics.vector.compat.TagSet
-import androidx.compose.ui.graphics.vector.compat.parseAnimatorSet
-import androidx.compose.ui.graphics.vector.compat.parseInterpolator
-import androidx.compose.ui.graphics.vector.compat.parseObjectAnimator
-import androidx.compose.ui.graphics.vector.compat.seekToStartTag
-import org.xmlpull.v1.XmlPullParserException
-import kotlin.math.PI
-import kotlin.math.cos
-import kotlin.math.pow
-import kotlin.math.sin
-
-/**
- * Synchronously loads an [Animator] resource.
- */
-@Throws(XmlPullParserException::class)
-internal fun loadAnimatorResource(
- theme: Resources.Theme? = null,
- res: Resources,
- resId: Int
-): Animator {
- val parser = res.getXml(resId)
- val attrs = Xml.asAttributeSet(parser)
-
- parser.seekToStartTag()
- return when (parser.name) {
- TagSet -> {
- parser.parseAnimatorSet(res, theme, attrs)
- }
- TagObjectAnimator -> {
- parser.parseObjectAnimator(res, theme, attrs)
- }
- else -> {
- throw XmlPullParserException("Unknown tag: ${parser.name}")
- }
- }
-}
-
-internal fun TimeInterpolator.toEasing() = Easing { x -> getInterpolation(x) }
-
-internal val AccelerateDecelerateEasing = Easing { x ->
- ((cos((x + 1) * Math.PI) / 2.0f) + 0.5f).toFloat()
-}
-
-internal val AccelerateEasing = Easing { x -> x * x }
-internal fun AccelerateEasing(factor: Float) = Easing { x -> x.pow(factor * 2) }
-
-internal fun AnticipateEasing(tension: Float) =
- Easing { x -> x * x * ((tension + 1) * x - tension) }
-
-internal fun AnticipateOvershootEasing(tension: Float, extraTension: Float): Easing =
- AnticipateOvershootInterpolator(tension, extraTension).toEasing()
-
-internal val BounceEasing: Easing = BounceInterpolator().toEasing()
-
-internal fun CycleEasing(cycle: Float) = Easing { x -> sin(2 * cycle * PI * x).toFloat() }
-
-internal val DecelerateEasing = Easing { x -> 1.0f - (1.0f - x) * (1.0f - x) }
-internal fun DecelerateEasing(factor: Float) = Easing { x -> 1.0f - (1.0f - x).pow(2 * factor) }
-
-internal fun OvershootEasing(tension: Float) =
- Easing { x -> (x - 1f).let { t -> t * t * ((tension + 1f) * t + tension) + 1f } }
-
-private val builtinInterpolators = hashMapOf(
- android.R.anim.linear_interpolator to LinearEasing,
- android.R.interpolator.fast_out_linear_in to FastOutLinearInEasing,
- android.R.interpolator.fast_out_slow_in to FastOutSlowInEasing,
- android.R.interpolator.linear to LinearEasing,
- android.R.interpolator.linear_out_slow_in to LinearOutSlowInEasing,
- AndroidVectorResources.FAST_OUT_LINEAR_IN to FastOutLinearInEasing,
- AndroidVectorResources.FAST_OUT_SLOW_IN to FastOutSlowInEasing,
- AndroidVectorResources.LINEAR_OUT_SLOW_IN to LinearOutSlowInEasing
-)
-
-/**
- * Synchronously loads an interpolator resource as an [Easing].
- */
-@Throws(XmlPullParserException::class)
-internal fun loadInterpolatorResource(
- theme: Resources.Theme? = null,
- res: Resources,
- resId: Int
-): Easing {
- return builtinInterpolators[resId]
- ?: res.getXml(resId).run {
- seekToStartTag().parseInterpolator(res, theme, Xml.asAttributeSet(this))
- }
-}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/AnimatedImageVector.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/AnimatedImageVector.kt
deleted file mode 100644
index bb6f2a9..0000000
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/AnimatedImageVector.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2021 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.ui.graphics.vector
-
-import androidx.compose.animation.core.updateTransition
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.fastAssociate
-import androidx.compose.ui.graphics.painter.Painter
-import androidx.compose.ui.util.fastMaxBy
-
-/**
- * Animated vector graphics object that is generated as a result of
- * [androidx.compose.ui.res.loadAnimatedVectorResource].
- * It can be composed and rendered by [painterFor].
- *
- * @param imageVector The [ImageVector] to be animated. This is represented with the
- * `android:drawable` parameter of an `<animated-vector>` element.
- */
-@ExperimentalComposeUiApi
-@Immutable
-class AnimatedImageVector internal constructor(
- val imageVector: ImageVector,
- // The list of [AnimatedVectorTarget]s that specify animations for each of the elements in the
- // drawable. This is represented with `<target>` elements in `<animated-vector>`. This list is
- // expected to be *immutable*.
- internal val targets: List<AnimatedVectorTarget>
-) {
-
- /**
- * The total duration of all the animations in this image, including start delays and repeats.
- */
- val totalDuration = targets.fastMaxBy {
- it.animator.totalDuration
- }?.animator?.totalDuration ?: 0
-
- /**
- * Creates and remembers a [Painter] to render this [AnimatedImageVector]. It renders the image
- * either at the start or the end of all the animations depending on the [atEnd]. Changes to
- * [atEnd] are animated.
- *
- * @param atEnd Whether the animated vector should be rendered at the end of all its animations.
- *
- * @sample androidx.compose.ui.samples.AnimatedVectorSample
- */
- @Composable
- fun painterFor(atEnd: Boolean): Painter {
- return painterFor(atEnd) { group, overrides ->
- RenderVectorGroup(group, overrides)
- }
- }
-
- @Composable
- internal fun painterFor(
- atEnd: Boolean,
- render: @Composable (VectorGroup, Map<String, VectorOverride>) -> Unit
- ): Painter {
- return rememberVectorPainter(
- defaultWidth = imageVector.defaultWidth,
- defaultHeight = imageVector.defaultHeight,
- viewportWidth = imageVector.viewportWidth,
- viewportHeight = imageVector.viewportHeight,
- name = imageVector.name,
- tintColor = imageVector.tintColor,
- tintBlendMode = imageVector.tintBlendMode,
- ) { _, _ ->
- val transition = updateTransition(atEnd)
- render(
- imageVector.root,
- targets.fastAssociate { target ->
- target.name to target.animator.createVectorOverride(transition, totalDuration)
- }
- )
- }
- }
-}
-
-/**
- * Definition of animation to one of the elements in a [AnimatedImageVector].
- */
-@Immutable
-internal class AnimatedVectorTarget(
- val name: String,
- val animator: Animator
-)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Animator.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Animator.kt
deleted file mode 100644
index 5bc0e89..0000000
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Animator.kt
+++ /dev/null
@@ -1,673 +0,0 @@
-/*
- * Copyright 2021 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.ui.graphics.vector
-
-import androidx.compose.animation.core.AnimationVector4D
-import androidx.compose.animation.core.Easing
-import androidx.compose.animation.core.FiniteAnimationSpec
-import androidx.compose.animation.core.KeyframesSpec
-import androidx.compose.animation.core.RepeatMode
-import androidx.compose.animation.core.Transition
-import androidx.compose.animation.core.TwoWayConverter
-import androidx.compose.animation.core.animateFloat
-import androidx.compose.animation.core.animateValue
-import androidx.compose.animation.core.keyframes
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.remember
-import androidx.compose.ui.fastZip
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.colorspace.ColorSpace
-import androidx.compose.ui.graphics.colorspace.ColorSpaces
-import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMaxBy
-import androidx.compose.ui.util.fastSumBy
-import androidx.compose.ui.util.lerp
-
-internal sealed class Animator {
- abstract val totalDuration: Int
-
- @Composable
- fun createVectorOverride(
- transition: Transition<Boolean>,
- overallDuration: Int
- ): VectorOverride {
- return StateVectorOverride().also { override ->
- Configure(transition, override, overallDuration, 0)
- }
- }
-
- @Composable
- abstract fun Configure(
- transition: Transition<Boolean>,
- override: StateVectorOverride,
- overallDuration: Int,
- parentDelay: Int
- )
-}
-
-internal data class ObjectAnimator(
- val duration: Int,
- val startDelay: Int,
- val repeatCount: Int,
- val repeatMode: RepeatMode,
- val holders: List<PropertyValuesHolder<*>>
-) : Animator() {
-
- override val totalDuration = if (repeatCount == Int.MAX_VALUE) {
- Int.MAX_VALUE
- } else {
- startDelay + duration * (repeatCount + 1)
- }
-
- @Composable
- override fun Configure(
- transition: Transition<Boolean>,
- override: StateVectorOverride,
- overallDuration: Int,
- parentDelay: Int
- ) {
- holders.fastForEach { holder ->
- holder.AnimateIn(
- override,
- transition,
- overallDuration,
- duration,
- parentDelay + startDelay
- )
- }
- }
-}
-
-internal data class AnimatorSet(
- val animators: List<Animator>,
- val ordering: Ordering
-) : Animator() {
-
- override val totalDuration = when (ordering) {
- Ordering.Together -> animators.fastMaxBy { it.totalDuration }?.totalDuration ?: 0
- Ordering.Sequentially -> animators.fastSumBy { it.totalDuration }
- }
-
- @Composable
- override fun Configure(
- transition: Transition<Boolean>,
- override: StateVectorOverride,
- overallDuration: Int,
- parentDelay: Int
- ) {
- when (ordering) {
- Ordering.Together -> {
- animators.fastForEach { animator ->
- animator.Configure(transition, override, overallDuration, parentDelay)
- }
- }
- Ordering.Sequentially -> {
- var accumulatedDelay = parentDelay
- animators.fastForEach { animator ->
- animator.Configure(transition, override, overallDuration, accumulatedDelay)
- accumulatedDelay += animator.totalDuration
- }
- }
- }
- }
-}
-
-internal sealed class PropertyValuesHolder<T> {
-
- @Composable
- abstract fun AnimateIn(
- override: StateVectorOverride,
- transition: Transition<Boolean>,
- overallDuration: Int,
- duration: Int,
- delay: Int
- )
-}
-
-internal data class PropertyValuesHolder2D(
- val xPropertyName: String,
- val yPropertyName: String,
- val pathData: List<PathNode>,
- val interpolator: Easing
-) : PropertyValuesHolder<Pair<Float, Float>>() {
-
- @Composable
- override fun AnimateIn(
- override: StateVectorOverride,
- transition: Transition<Boolean>,
- overallDuration: Int,
- duration: Int,
- delay: Int
- ) {
- // TODO(b/178978971): Implement path animation.
- }
-}
-
-internal sealed class PropertyValuesHolder1D<T>(
- val propertyName: String
-) : PropertyValuesHolder<T>() {
-
- abstract val animatorKeyframes: List<Keyframe<T>>
-
- protected val targetValueByState: @Composable (Boolean) -> T = { atEnd ->
- if (atEnd) {
- animatorKeyframes.last().value
- } else {
- animatorKeyframes.first().value
- }
- }
-
- protected fun <R> createTransitionSpec(
- overallDuration: Int,
- duration: Int,
- delay: Int,
- addKeyframe: KeyframesSpec.KeyframesSpecConfig<R>.(
- keyframe: Keyframe<T>,
- time: Int,
- easing: Easing
- ) -> Unit
- ): @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<R> {
- return {
- if (targetState) { // at end
- keyframes {
- durationMillis = duration
- delayMillis = delay
- animatorKeyframes.fastForEach { keyframe ->
- val time = (duration * keyframe.fraction).toInt()
- addKeyframe(keyframe, time, keyframe.interpolator)
- }
- }
- } else {
- keyframes {
- durationMillis = duration
- delayMillis = overallDuration - duration - delay
- animatorKeyframes.fastForEach { keyframe ->
- val time = (duration * (1 - keyframe.fraction)).toInt()
- addKeyframe(keyframe, time, keyframe.interpolator.transpose())
- }
- }
- }
- }
- }
-}
-
-internal class PropertyValuesHolderFloat(
- propertyName: String,
- override val animatorKeyframes: List<Keyframe<Float>>
-) : PropertyValuesHolder1D<Float>(propertyName) {
-
- @Composable
- override fun AnimateIn(
- override: StateVectorOverride,
- transition: Transition<Boolean>,
- overallDuration: Int,
- duration: Int,
- delay: Int
- ) {
- val state = transition.animateFloat(
- transitionSpec = createTransitionSpec(
- overallDuration,
- duration,
- delay
- ) { keyframe, time, easing ->
- keyframe.value at time with easing
- },
- label = propertyName,
- targetValueByState = targetValueByState
- )
- when (propertyName) {
- "rotation" -> override.rotationState = state
- "pivotX" -> override.pivotXState = state
- "pivotY" -> override.pivotYState = state
- "scaleX" -> override.scaleXState = state
- "scaleY" -> override.scaleYState = state
- "translateX" -> override.translateXState = state
- "translateY" -> override.translateYState = state
- "fillAlpha" -> override.fillAlphaState = state
- "strokeWidth" -> override.strokeWidthState = state
- "strokeAlpha" -> override.strokeAlphaState = state
- "trimPathStart" -> override.trimPathStartState = state
- "trimPathEnd" -> override.trimPathEndState = state
- "trimPathOffset" -> override.trimPathOffsetState = state
- else -> throw IllegalStateException("Unknown propertyName: $propertyName")
- }
- }
-}
-
-internal class PropertyValuesHolderInt(
- propertyName: String,
- override val animatorKeyframes: List<Keyframe<Int>>
-) : PropertyValuesHolder1D<Int>(propertyName) {
-
- @Composable
- override fun AnimateIn(
- override: StateVectorOverride,
- transition: Transition<Boolean>,
- overallDuration: Int,
- duration: Int,
- delay: Int
- ) {
- // AnimatedVectorDrawable does not have an Int property; Ignore.
- }
-}
-
-internal class PropertyValuesHolderColor(
- propertyName: String,
- override val animatorKeyframes: List<Keyframe<Color>>
-) : PropertyValuesHolder1D<Color>(propertyName) {
-
- @Composable
- override fun AnimateIn(
- override: StateVectorOverride,
- transition: Transition<Boolean>,
- overallDuration: Int,
- duration: Int,
- delay: Int
- ) {
- val state = transition.animateColor(
- transitionSpec = createTransitionSpec(
- overallDuration,
- duration,
- delay
- ) { keyframe, time, easing ->
- keyframe.value at time with easing
- },
- label = propertyName,
- targetValueByState = targetValueByState
- )
- when (propertyName) {
- "fillColor" -> override.fillColorState = state
- "strokeColor" -> override.strokeColorState = state
- else -> throw IllegalStateException("Unknown propertyName: $propertyName")
- }
- }
-}
-
-internal class PropertyValuesHolderPath(
- propertyName: String,
- override val animatorKeyframes: List<Keyframe<List<PathNode>>>
-) : PropertyValuesHolder1D<List<PathNode>>(propertyName) {
-
- @Composable
- override fun AnimateIn(
- override: StateVectorOverride,
- transition: Transition<Boolean>,
- overallDuration: Int,
- duration: Int,
- delay: Int
- ) {
- if (propertyName == "pathData") {
- val state = transition.animateFloat(
- transitionSpec = createTransitionSpec(
- overallDuration,
- duration,
- delay
- ) { keyframe, time, easing ->
- keyframe.fraction at time with easing
- }
- ) { atEnd ->
- if (atEnd) {
- animatorKeyframes.last().fraction
- } else {
- animatorKeyframes.first().fraction
- }
- }
- override.pathDataStatePair = state to this
- } else {
- throw IllegalStateException("Unknown propertyName: $propertyName")
- }
- }
-
- fun interpolate(fraction: Float): List<PathNode> {
- val index = (animatorKeyframes.indexOfFirst { it.fraction > fraction } - 1).coerceAtLeast(0)
- val easing = animatorKeyframes[index + 1].interpolator
- val innerFraction = easing.transform(
- (
- (fraction - animatorKeyframes[index].fraction) /
- (animatorKeyframes[index + 1].fraction - animatorKeyframes[index].fraction)
- )
- .coerceIn(0f, 1f)
- )
- return lerp(
- animatorKeyframes[index].value,
- animatorKeyframes[index + 1].value,
- innerFraction
- )
- }
-}
-
-internal data class Keyframe<T>(
- val fraction: Float,
- val value: T,
- val interpolator: Easing
-)
-
-internal enum class Ordering {
- Together,
- Sequentially
-}
-
-internal class StateVectorOverride : VectorOverride {
-
- var rotationState: State<Float>? = null
- var pivotXState: State<Float>? = null
- var pivotYState: State<Float>? = null
- var pivotState: State<Offset>? = null
- var scaleXState: State<Float>? = null
- var scaleYState: State<Float>? = null
- var scaleState: State<Offset>? = null
- var translateXState: State<Float>? = null
- var translateYState: State<Float>? = null
- var translateState: State<Offset>? = null
-
- // PathData is special because we have to animate its float fraction and interpolate the path.
- var pathDataStatePair: Pair<State<Float>, PropertyValuesHolderPath>? = null
- var fillColorState: State<Color>? = null
- var strokeColorState: State<Color>? = null
- var strokeWidthState: State<Float>? = null
- var strokeAlphaState: State<Float>? = null
- var fillAlphaState: State<Float>? = null
- var trimPathStartState: State<Float>? = null
- var trimPathEndState: State<Float>? = null
- var trimPathOffsetState: State<Float>? = null
-
- override fun obtainRotation(rotation: Float): Float {
- return rotationState?.value ?: rotation
- }
-
- override fun obtainPivotX(pivotX: Float): Float {
- return pivotXState?.value ?: pivotState?.value?.x ?: pivotX
- }
-
- override fun obtainPivotY(pivotY: Float): Float {
- return pivotYState?.value ?: pivotState?.value?.y ?: pivotY
- }
-
- override fun obtainScaleX(scaleX: Float): Float {
- return scaleXState?.value ?: scaleState?.value?.x ?: scaleX
- }
-
- override fun obtainScaleY(scaleY: Float): Float {
- return scaleYState?.value ?: scaleState?.value?.y ?: scaleY
- }
-
- override fun obtainTranslateX(translateX: Float): Float {
- return translateXState?.value ?: translateState?.value?.x ?: translateX
- }
-
- override fun obtainTranslateY(translateY: Float): Float {
- return translateYState?.value ?: translateState?.value?.y ?: translateY
- }
-
- override fun obtainPathData(pathData: List<PathNode>): List<PathNode> {
- return pathDataStatePair.let { pair ->
- if (pair != null) {
- val (state, holder) = pair
- holder.interpolate(state.value)
- } else {
- pathData
- }
- }
- }
-
- override fun obtainFill(fill: Brush?): Brush? {
- return fillColorState.let { state ->
- if (state != null) {
- SolidColor(state.value)
- } else {
- fill
- }
- }
- }
-
- override fun obtainFillAlpha(fillAlpha: Float): Float {
- return fillAlphaState?.value ?: fillAlpha
- }
-
- override fun obtainStroke(stroke: Brush?): Brush? {
- return strokeColorState.let { state ->
- if (state != null) {
- SolidColor(state.value)
- } else {
- stroke
- }
- }
- }
-
- override fun obtainStrokeWidth(strokeWidth: Float): Float {
- return strokeWidthState?.value ?: strokeWidth
- }
-
- override fun obtainStrokeAlpha(strokeAlpha: Float): Float {
- return strokeAlphaState?.value ?: strokeAlpha
- }
-
- override fun obtainTrimPathStart(trimPathStart: Float): Float {
- return trimPathStartState?.value ?: trimPathStart
- }
-
- override fun obtainTrimPathEnd(trimPathEnd: Float): Float {
- return trimPathEndState?.value ?: trimPathEnd
- }
-
- override fun obtainTrimPathOffset(trimPathOffset: Float): Float {
- return trimPathOffsetState?.value ?: trimPathOffset
- }
-}
-
-@Composable
-private inline fun <S> Transition<S>.animateColor(
- noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<Color>,
- label: String = "ColorAnimation",
- targetValueByState: @Composable (state: S) -> Color
-): State<Color> {
- val colorSpace = targetValueByState(targetState).colorSpace
- val typeConverter = remember(colorSpace) {
- ColorToVector(colorSpace)
- }
- return animateValue(typeConverter, transitionSpec, label, targetValueByState)
-}
-
-private val ColorToVector: (colorSpace: ColorSpace) -> TwoWayConverter<Color, AnimationVector4D> =
- { colorSpace ->
- TwoWayConverter(
- convertToVector = {
- val linearColor = it.convert(ColorSpaces.LinearExtendedSrgb)
- AnimationVector4D(
- linearColor.alpha, linearColor.red, linearColor.green, linearColor.blue
- )
- },
- convertFromVector = {
- Color(
- alpha = it.v1.coerceIn(0.0f, 1.0f),
- red = it.v2.coerceIn(0.0f, 1.0f),
- green = it.v3.coerceIn(0.0f, 1.0f),
- blue = it.v4.coerceIn(0.0f, 1.0f),
- colorSpace = ColorSpaces.LinearExtendedSrgb
- ).convert(colorSpace)
- }
- )
- }
-
-private fun Easing.transpose(): Easing {
- return Easing { x -> 1 - this.transform(1 - x) }
-}
-
-private fun lerp(start: List<PathNode>, stop: List<PathNode>, fraction: Float): List<PathNode> {
- return start.fastZip(stop) { a, b -> lerp(a, b, fraction) }
-}
-
-/**
- * Linearly interpolate between [start] and [stop] with [fraction] fraction between them.
- */
-private fun lerp(start: PathNode, stop: PathNode, fraction: Float): PathNode {
- return when (start) {
- is PathNode.RelativeMoveTo -> {
- require(stop is PathNode.RelativeMoveTo)
- PathNode.RelativeMoveTo(
- lerp(start.dx, stop.dx, fraction),
- lerp(start.dy, stop.dy, fraction)
- )
- }
- is PathNode.MoveTo -> {
- require(stop is PathNode.MoveTo)
- PathNode.MoveTo(
- lerp(start.x, stop.x, fraction),
- lerp(start.y, stop.y, fraction)
- )
- }
- is PathNode.RelativeLineTo -> {
- require(stop is PathNode.RelativeLineTo)
- PathNode.RelativeLineTo(
- lerp(start.dx, stop.dx, fraction),
- lerp(start.dy, stop.dy, fraction)
- )
- }
- is PathNode.LineTo -> {
- require(stop is PathNode.LineTo)
- PathNode.LineTo(
- lerp(start.x, stop.x, fraction),
- lerp(start.y, stop.y, fraction)
- )
- }
- is PathNode.RelativeHorizontalTo -> {
- require(stop is PathNode.RelativeHorizontalTo)
- PathNode.RelativeHorizontalTo(
- lerp(start.dx, stop.dx, fraction)
- )
- }
- is PathNode.HorizontalTo -> {
- require(stop is PathNode.HorizontalTo)
- PathNode.HorizontalTo(
- lerp(start.x, stop.x, fraction)
- )
- }
- is PathNode.RelativeVerticalTo -> {
- require(stop is PathNode.RelativeVerticalTo)
- PathNode.RelativeVerticalTo(
- lerp(start.dy, stop.dy, fraction)
- )
- }
- is PathNode.VerticalTo -> {
- require(stop is PathNode.VerticalTo)
- PathNode.VerticalTo(
- lerp(start.y, stop.y, fraction)
- )
- }
- is PathNode.RelativeCurveTo -> {
- require(stop is PathNode.RelativeCurveTo)
- PathNode.RelativeCurveTo(
- lerp(start.dx1, stop.dx1, fraction),
- lerp(start.dy1, stop.dy1, fraction),
- lerp(start.dx2, stop.dx2, fraction),
- lerp(start.dy2, stop.dy2, fraction),
- lerp(start.dx3, stop.dx3, fraction),
- lerp(start.dy3, stop.dy3, fraction)
- )
- }
- is PathNode.CurveTo -> {
- require(stop is PathNode.CurveTo)
- PathNode.CurveTo(
- lerp(start.x1, stop.x1, fraction),
- lerp(start.y1, stop.y1, fraction),
- lerp(start.x2, stop.x2, fraction),
- lerp(start.y2, stop.y2, fraction),
- lerp(start.x3, stop.x3, fraction),
- lerp(start.y3, stop.y3, fraction)
- )
- }
- is PathNode.RelativeReflectiveCurveTo -> {
- require(stop is PathNode.RelativeReflectiveCurveTo)
- PathNode.RelativeReflectiveCurveTo(
- lerp(start.dx1, stop.dx1, fraction),
- lerp(start.dy1, stop.dy1, fraction),
- lerp(start.dx2, stop.dx2, fraction),
- lerp(start.dy2, stop.dy2, fraction)
- )
- }
- is PathNode.ReflectiveCurveTo -> {
- require(stop is PathNode.ReflectiveCurveTo)
- PathNode.ReflectiveCurveTo(
- lerp(start.x1, stop.x1, fraction),
- lerp(start.y1, stop.y1, fraction),
- lerp(start.x2, stop.x2, fraction),
- lerp(start.y2, stop.y2, fraction)
- )
- }
- is PathNode.RelativeQuadTo -> {
- require(stop is PathNode.RelativeQuadTo)
- PathNode.RelativeQuadTo(
- lerp(start.dx1, stop.dx1, fraction),
- lerp(start.dy1, stop.dy1, fraction),
- lerp(start.dx2, stop.dx2, fraction),
- lerp(start.dy2, stop.dy2, fraction)
- )
- }
- is PathNode.QuadTo -> {
- require(stop is PathNode.QuadTo)
- PathNode.QuadTo(
- lerp(start.x1, stop.x1, fraction),
- lerp(start.y1, stop.y1, fraction),
- lerp(start.x2, stop.x2, fraction),
- lerp(start.y2, stop.y2, fraction)
- )
- }
- is PathNode.RelativeReflectiveQuadTo -> {
- require(stop is PathNode.RelativeReflectiveQuadTo)
- PathNode.RelativeReflectiveQuadTo(
- lerp(start.dx, stop.dx, fraction),
- lerp(start.dy, stop.dy, fraction)
- )
- }
- is PathNode.ReflectiveQuadTo -> {
- require(stop is PathNode.ReflectiveQuadTo)
- PathNode.ReflectiveQuadTo(
- lerp(start.x, stop.x, fraction),
- lerp(start.y, stop.y, fraction)
- )
- }
- is PathNode.RelativeArcTo -> {
- require(stop is PathNode.RelativeArcTo)
- PathNode.RelativeArcTo(
- lerp(start.horizontalEllipseRadius, stop.horizontalEllipseRadius, fraction),
- lerp(start.verticalEllipseRadius, stop.verticalEllipseRadius, fraction),
- lerp(start.theta, stop.theta, fraction),
- start.isMoreThanHalf,
- start.isPositiveArc,
- lerp(start.arcStartDx, stop.arcStartDx, fraction),
- lerp(start.arcStartDy, stop.arcStartDy, fraction)
- )
- }
- is PathNode.ArcTo -> {
- require(stop is PathNode.ArcTo)
- PathNode.ArcTo(
- lerp(start.horizontalEllipseRadius, stop.horizontalEllipseRadius, fraction),
- lerp(start.verticalEllipseRadius, stop.verticalEllipseRadius, fraction),
- lerp(start.theta, stop.theta, fraction),
- start.isMoreThanHalf,
- start.isPositiveArc,
- lerp(start.arcStartX, stop.arcStartX, fraction),
- lerp(start.arcStartY, stop.arcStartY, fraction)
- )
- }
- PathNode.Close -> PathNode.Close
- }
-}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/InfiniteAnimationPolicy.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/InfiniteAnimationPolicy.kt
new file mode 100644
index 0000000..58d703b
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/InfiniteAnimationPolicy.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021 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.ui.platform
+
+import kotlin.coroutines.CoroutineContext
+
+/**
+ * Provides a policy that will be applied to animations that get their frame time from
+ * [withInfiniteAnimationFrameNanos][androidx.compose.animation.core.withInfiniteAnimationFrameNanos]
+ * or
+ * [withInfiniteAnimationFrameMillis][androidx.compose.animation.core.withInfiniteAnimationFrameMillis]
+ * This can be used to intervene in infinite animations to make them finite, for example by
+ * cancelling such coroutines.
+ *
+ * By default no policy is installed, except in instrumented tests that use
+ * [androidx.compose.ui.test.junit4.ComposeTestRule].
+ */
+interface InfiniteAnimationPolicy : CoroutineContext.Element {
+ /**
+ * Call this to apply the policy on the given suspending [block]. Execution of the block is
+ * determined by the policy implementation. For example, a test policy could decide not to
+ * run the block, or trace its execution.
+ *
+ * The block is intended to be part of and will therefore be treated as an infinite animation,
+ * one that after returning from [onInfiniteOperation] will call it again. If the block is
+ * not part of an infinite animation, the policy will still be applied.
+ */
+ suspend fun <R> onInfiniteOperation(block: suspend () -> R): R
+
+ override val key: CoroutineContext.Key<*> get() = Key
+
+ companion object Key : CoroutineContext.Key<InfiniteAnimationPolicy>
+}
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Application.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Application.desktop.kt
index 6e2522f..6c5c915 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Application.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Application.desktop.kt
@@ -16,7 +16,6 @@
package androidx.compose.ui.window
-import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.desktop.AppManager
import androidx.compose.desktop.AppWindow
import androidx.compose.desktop.ComposePanel
@@ -144,7 +143,7 @@
* [LaunchedEffect]) or that have child composition created inside [Window], [Dialog], or [Tray].
*
* Don't use any animation in this function
- * (for example, [withFrameNanos] or [animateFloatAsState]),
+ * (for example, [withFrameNanos] or [androidx.compose.animation.core.animateFloatAsState]),
* because underlying [MonotonicFrameClock] hasn't synchronized with any display, and produces
* frames as fast as possible.
*
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 1ffec8b..2213c47 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -151,6 +151,20 @@
updated local\.properties
# > Configure project :compose:test\-utils
The following Kotlin source sets were configured but not added to any Kotlin compilation:
+\* iosArm[0-9]+Main
+\* iosArm[0-9]+Test
+\* iosX[0-9]+Main
+\* iosX[0-9]+Test
+\* jsMain
+\* jsTest
+\* linuxX[0-9]+Main
+\* linuxX[0-9]+Test
+\* macosX[0-9]+Main
+\* macosX[0-9]+Test
+\* mingwX[0-9]+Main
+\* mingwX[0-9]+Test
+\* nativeTest
+\* nonJvmMain
\* androidAndroidTestRelease
\* androidTestFixtures
\* androidTestFixturesDebug
@@ -159,6 +173,10 @@
\* test
You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'\.
See https://kotlinlang\.org/docs/reference/building\-mpp\-with\-gradle\.html\#connecting\-source\-sets
+Some Kotlin/Native targets cannot be built on this macos_x[0-9]+ machine and are disabled:
+\* In project ':collection:collection[0-9]+':
+\* target 'mingwX[0-9]+' \(can be built with a mingw_x[0-9]+ host\)
+To hide this message, add 'kotlin\.native\.ignoreDisabledTargets=true' to the Gradle properties\.
\* jvmMain
# > Task :listTaskOutputs
Wrote \$DIST_DIR/task_outputs\.txt
diff --git a/development/diagnose-build-failure/diagnose-build-failure.sh b/development/diagnose-build-failure/diagnose-build-failure.sh
index fa03471..e0b1f88 100755
--- a/development/diagnose-build-failure/diagnose-build-failure.sh
+++ b/development/diagnose-build-failure/diagnose-build-failure.sh
@@ -8,7 +8,7 @@
echo " diagnose-build-failure.sh"
echo
echo "SYNOPSIS"
- echo " ./development/diagnose-build-failure/diagnose-build-failure.sh [--message <message>] '<tasks>'"
+ echo " ./development/diagnose-build-failure/diagnose-build-failure.sh [--message <message>] [--timeout <seconds> ] '<tasks>'"
echo
echo "DESCRIPTION"
echo " Attempts to identify why "'`'"./gradlew <tasks>"'`'" fails"
@@ -34,6 +34,7 @@
}
expectedMessage=""
+timeoutSeconds=""
while true; do
if [ "$#" -lt 1 ]; then
usage
@@ -45,12 +46,33 @@
shift
continue
fi
+ if [ "$arg" == "--timeout" ]; then
+ timeoutSeconds="$1"
+ shift
+ continue
+ fi
+
gradleArgs="$arg"
break
done
if [ "$gradleArgs" == "" ]; then
usage
fi
+if [ "$timeoutSeconds" == "" ]; then
+ timeoutArg=""
+else
+ timeoutArg="--timeout $timeoutSeconds"
+fi
+# split Gradle arguments into options and tasks
+gradleOptions=""
+gradleTasks=""
+for arg in $gradleArgs; do
+ if [[ "$arg" == "-*" ]]; then
+ gradleOptions="$gradleOptions $arg"
+ else
+ gradleTasks="$gradleTasks $arg"
+ fi
+done
if [ "$#" -gt 0 ]; then
echo "Unrecognized argument: $1"
@@ -99,6 +121,7 @@
fi
}
+# echos a shell command for running the build in the current directory
function getBuildCommand() {
if [ "$expectedMessage" == "" ]; then
testCommand="$*"
@@ -108,6 +131,37 @@
echo "$testCommand"
}
+# Echos a shell command for testing the state in the current directory
+# Status can be inverted by the '--invert' flag
+# The dir of the state being tested is $testDir
+# The dir of the source code is $workingDir
+function getTestStateCommand() {
+ successStatus=0
+ failureStatus=1
+ if [[ "$1" == "--invert" ]]; then
+ successStatus=1
+ failureStatus=0
+ shift
+ fi
+
+ setupCommand="testDir=\$(pwd)
+$scriptPath/impl/restore-state.sh . $workingDir --move && cd $workingDir
+"
+ buildCommand="$*"
+ cleanupCommand="$scriptPath/impl/backup-state.sh \$testDir $workingDir --move >/dev/null"
+
+ fullFiltererCommand="$setupCommand
+if $buildCommand >/dev/null 2>/dev/null; then
+ $cleanupCommand
+ exit $successStatus
+else
+ $cleanupCommand
+ exit $failureStatus
+fi"
+
+ echo "$fullFiltererCommand"
+}
+
function runBuild() {
testCommand="$(getBuildCommand $*)"
cd "$workingDir"
@@ -235,25 +289,61 @@
echo "After restoring the saved state, the build failed. This confirms that this script is successfully saving and restoring the relevant state"
fi
+# Ask diff-filterer.py to run a binary search to determine the minimum set of tasks that must be passed to reproduce this error
+# (it's possible that the caller passed more tasks than needed, particularly if the caller is a script)
+requiredTasksDir="$tempDir/requiredTasks"
+function determineMinimalSetOfRequiredTasks() {
+ echo Calculating the list of tasks to run
+ allTasksLog="$tempDir/tasks.log"
+ restoreState "$successState"
+ rm -f "$allTasksLog"
+ bash -c "cd $workingDir && ./gradlew --no-daemon --dry-run $gradleArgs > $allTasksLog 2>&1" || true
+
+ # process output and split into files
+ taskListFile="$tempDir/tasks.list"
+ cat "$allTasksLog" | grep '^:' | sed 's/ .*//' > "$taskListFile"
+ requiredTasksWork="$tempDir/requiredTasksWork"
+ rm -rf "$requiredTasksWork"
+ cp -r "$tempDir/prev" "$requiredTasksWork"
+ mkdir -p "$requiredTasksWork/tasks"
+ bash -c "cd $requiredTasksWork/tasks && split -l 1 '$taskListFile'"
+
+ rm -rf "$requiredTasksDir"
+ # Build the command for passing to diff-filterer.
+ # We call xargs because the full set of tasks might be too long for the shell, and xargs will
+ # split into multiple gradlew invocations if needed.
+ # We also cd into the tasks/ dir before calling 'cat' to avoid reaching its argument length limit.
+ # note that the variable "$testDir" gets set by $getTestStateCommand
+ buildCommand="$(getBuildCommand "rm -f log && (cd \$testDir/tasks && cat *) | xargs --no-run-if-empty ./gradlew $gradleOptions")"
+
+ # command for moving state, running build, and moving state back
+ fullFiltererCommand="$(getTestStateCommand --invert $buildCommand)"
+
+ if $supportRoot/development/file-utils/diff-filterer.py $timeoutArg --work-path "$tempDir" "$requiredTasksWork" "$tempDir/prev" "$fullFiltererCommand"; then
+ echo diff-filterer successfully identified a minimal set of required tasks. Saving into $requiredTasksDir
+ cp -r "$tempDir/bestResults/tasks" "$requiredTasksDir"
+ else
+ echo diff-filterer was unable to identify a minimal set of tasks required to reproduce the error
+ exit 1
+ fi
+}
+determineMinimalSetOfRequiredTasks
+# update variables
+gradleTasks="$(cat $requiredTasksDir/*)"
+gradleArgs="$gradleOptions $gradleTasks"
+
# Now ask diff-filterer.py to run a binary search to determine what the relevant differences are between "$tempDir/prev" and "$tempDir/clean"
echo
echo "Binary-searching the contents of the two output directories until the relevant differences are identified."
echo "This may take a while."
echo
-setupCommand="work=\$(pwd)
-$scriptPath/impl/restore-state.sh . $workingDir --move && cd $workingDir
-"
+
+# command for running a build
buildCommand="$(getBuildCommand "./gradlew --no-daemon $gradleArgs")"
-cleanupCommand="$scriptPath/impl/backup-state.sh \$work $workingDir --move >/dev/null"
-fullFiltererCommand="$setupCommand
-if $buildCommand; then
- $cleanupCommand
- exit 0
-else
- $cleanupCommand
- exit 1
-fi"
-if $supportRoot/development/file-utils/diff-filterer.py --assume-input-states-are-correct --work-path $tempDir $successState $tempDir/prev "$fullFiltererCommand"; then
+# command for moving state, running build, and moving state back
+fullFiltererCommand="$(getTestStateCommand $buildCommand)"
+
+if $supportRoot/development/file-utils/diff-filterer.py $timeoutArg --assume-input-states-are-correct --work-path $tempDir $successState $tempDir/prev "$fullFiltererCommand"; then
echo
echo "There should be something wrong with the above file state"
echo "Hopefully the output from diff-filterer.py above is enough information for you to figure out what is wrong"
diff --git a/development/file-utils/diff-filterer.py b/development/file-utils/diff-filterer.py
index f555a58..3bc3ccf 100755
--- a/development/file-utils/diff-filterer.py
+++ b/development/file-utils/diff-filterer.py
@@ -20,7 +20,7 @@
from collections import OrderedDict
def usage():
- print("""Usage: diff-filterer.py [--assume-no-side-effects] [--assume-input-states-are-correct] [--try-fail] [--work-path <workpath>] [--num-jobs <count>] [--debug] <passingPath> <failingPath> <shellCommand>
+ print("""Usage: diff-filterer.py [--assume-no-side-effects] [--assume-input-states-are-correct] [--work-path <workpath>] [--num-jobs <count>] [--timeout <seconds>] [--debug] <passingPath> <failingPath> <shellCommand>
diff-filterer.py attempts to transform (a copy of) the contents of <passingPath> into the contents of <failingPath> subject to the constraint that when <shellCommand> is run in that directory, it returns 0
@@ -29,15 +29,15 @@
Assume that the given shell command does not make any (relevant) changes to the given directory, and therefore don't wipe and repopulate the directory before each invocation of the command
--assume-input-states-are-correct
Assume that <shellCommand> passes in <passingPath> and fails in <failingPath> rather than re-verifying this
- --try-fail
- Invert the success/fail status of <shellCommand> and swap <passingPath> and <failingPath>
- That is, instead of trying to transform <passingPath> into <failingPath>, try to transform <failingPath> into <passingPath>
--work-path <filepath>
File path to use as the work directory for testing the shell command
This file path will be overwritten and modified as needed for testing purposes, and will also be the working directory of the shell command when it is run
--num-jobs <count>
The maximum number of concurrent executions of <shellCommand> to spawn at once
Specify 'auto' to have diff-filterer.py dynamically adjust the number of jobs based on system load
+ --timeout <seconds>
+ Approximate maximum amount of time to run. If diff-filterer.py expects that running a test would exceed this timeout, then it will skip running the test, terminate early, and report what it did find.
+ diff-filterer.py doesn't terminate any child processes that have already started, so it is still possible that diff-filterer.py might exceed this timeout by the amount of time required to run one test.
--debug
Enable some debug checks in diff-filterer.py
""")
@@ -568,7 +568,7 @@
# Runner class that determines which diffs between two directories cause the given shell command to fail
class DiffRunner(object):
- def __init__(self, failingPath, passingPath, shellCommand, workPath, assumeNoSideEffects, assumeInputStatesAreCorrect, tryFail, maxNumJobsAtOnce):
+ def __init__(self, failingPath, passingPath, shellCommand, workPath, assumeNoSideEffects, assumeInputStatesAreCorrect, maxNumJobsAtOnce, timeoutSeconds):
# some simple params
self.workPath = os.path.abspath(workPath)
self.bestState_path = fileIo.join(self.workPath, "bestResults")
@@ -580,7 +580,7 @@
self.originalFailingPath = os.path.abspath(failingPath)
self.assumeNoSideEffects = assumeNoSideEffects
self.assumeInputStatesAreCorrect = assumeInputStatesAreCorrect
- self.tryFail = tryFail
+ self.timeoutSeconds = timeoutSeconds
# lists of all the files under the two dirs
print("Finding files in " + passingPath)
@@ -641,7 +641,7 @@
sys.exit(1)
self.targetState = self.targetState.withoutDuplicatesFrom(testState)
self.resetTo_state = self.resetTo_state.withConflictsFrom(testState).withoutDuplicatesFrom(testState)
- delta = self.full_resetTo_state.expandedWithEmptyEntriesFor(testState).withConflictsFrom(testState, True)
+ delta = self.full_resetTo_state.expandedWithEmptyEntriesFor(testState).withConflictsFrom(testState, True).withoutDuplicatesFrom(self.full_resetTo_state)
delta.apply(self.bestState_path)
self.full_resetTo_state = self.full_resetTo_state.expandedWithEmptyEntriesFor(delta).withConflictsFrom(delta)
if debug:
@@ -701,8 +701,9 @@
probablyAcceptableStates = []
numCompletedTests = 2 # Already tested initial passing state and initial failing state
numJobsAtFirstSuccessAfterMerge = None
+ timedOut = False
# continue until all files fail and no jobs are running
- while numFailuresSinceLastSplitOrSuccess < self.resetTo_state.size() or len(activeTestStatesById) > 0:
+ while (numFailuresSinceLastSplitOrSuccess < self.resetTo_state.size() and not timedOut) or len(activeTestStatesById) > 0:
# display status message
now = datetime.datetime.now()
elapsedDuration = now - start
@@ -717,6 +718,14 @@
estimatedRemainingDuration = datetime.timedelta(seconds = elapsedDuration.total_seconds() * float(estimatedNumTestsRemaining) / float(numCompletedTests))
message = "Elapsed duration: " + str(elapsedDuration) + ". Waiting for " + str(len(activeTestStatesById)) + " active subprocesses (" + str(len(availableTestStates) + len(activeTestStatesById)) + " total available jobs). " + str(self.resetTo_state.size()) + " changes left to test, should take about " + str(estimatedNumTestsRemaining) + " tests, about " + str(estimatedRemainingDuration)
print(message)
+ if self.timeoutSeconds is not None:
+ # what fraction of the time is left
+ remainingTimeFraction = 1.0 - (elapsedDuration.total_seconds() / self.timeoutSeconds)
+ # how many jobs there will be if we add another one
+ possibleNumPendingJobs = len(activeTestStatesById) + 1
+ if possibleNumPendingJobs / (numCompletedTests + possibleNumPendingJobs) > remainingTimeFraction:
+ # adding one more job would be likely to cause us to exceed our time limit
+ timedOut = True
if len(activeTestStatesById) > 0:
# wait for a response from a worker
@@ -853,34 +862,40 @@
targetNumJobs = 1
print("System idle = " + str(systemIdleFraction) + ", current num jobs = " + str(len(activeTestStatesById) + 1) + ", target num jobs = " + str(targetNumJobs))
- while len(activeTestStatesById) < targetNumJobs and len(activeTestStatesById) < self.resetTo_state.size() and len(availableTestStates) > 0:
- # find next pending job
- box = availableTestStates[0]
- # find next unused job id
- jobId = 0
- while jobId in activeTestStatesById:
- jobId += 1
- # start job
- workingDir = self.getWorkPath(jobId)
- cacheDir = self.getFilesCachePath(jobId)
- if jobId in workerStatesById:
- workerPreviousState = workerStatesById[jobId]
- else:
- workerPreviousState = FilesState()
- runJobInOtherProcess(self.testScript_path, workingDir, cacheDir, workerPreviousState, self.assumeNoSideEffects, self.full_resetTo_state, box, queue, jobId)
- activeTestStatesById[jobId] = box
- availableTestStates = availableTestStates[1:]
+ if timedOut:
+ print("Timeout reached, not starting new jobs")
+ else:
+ while len(activeTestStatesById) < targetNumJobs and len(activeTestStatesById) < self.resetTo_state.size() and len(availableTestStates) > 0:
+ # find next pending job
+ box = availableTestStates[0]
+ # find next unused job id
+ jobId = 0
+ while jobId in activeTestStatesById:
+ jobId += 1
+ # start job
+ workingDir = self.getWorkPath(jobId)
+ cacheDir = self.getFilesCachePath(jobId)
+ if jobId in workerStatesById:
+ workerPreviousState = workerStatesById[jobId]
+ else:
+ workerPreviousState = FilesState()
+ runJobInOtherProcess(self.testScript_path, workingDir, cacheDir, workerPreviousState, self.assumeNoSideEffects, self.full_resetTo_state, box, queue, jobId)
+ activeTestStatesById[jobId] = box
+ availableTestStates = availableTestStates[1:]
- print("double-checking results")
- wasSuccessful = True
- if not self.runnerTest(filesStateFromTree(self.bestState_path))[0]:
- message = "Error: expected best state at " + self.bestState_path + " did not pass the second time. Could the test be non-deterministic?"
- if self.assumeNoSideEffects:
- message += " (it may help to remove the --assume-no-side-effects flag)"
- if self.assumeInputStatesAreCorrect:
- message += " (it may help to remove the --assume-input-states-are-correct flag)"
- print(message)
+ if timedOut:
wasSuccessful = False
+ else:
+ print("double-checking results")
+ wasSuccessful = True
+ if not self.runnerTest(filesStateFromTree(self.bestState_path))[0]:
+ message = "Error: expected best state at " + self.bestState_path + " did not pass the second time. Could the test be non-deterministic?"
+ if self.assumeNoSideEffects:
+ message += " (it may help to remove the --assume-no-side-effects flag)"
+ if self.assumeInputStatesAreCorrect:
+ message += " (it may help to remove the --assume-input-states-are-correct flag)"
+ print(message)
+ wasSuccessful = False
self.cleanupTempDirs()
@@ -892,13 +907,15 @@
print("Done trying to transform the contents of passing path:\n " + self.originalPassingPath + "\ninto the contents of failing path:\n " + self.originalFailingPath)
print("Of " + str(self.originalNumDifferences) + " differences, could not accept: " + filesDescription)
print("The final accepted state can be seen at " + self.bestState_path)
+ if timedOut:
+ print("Note that these results might not be optimal due to reaching the timeout of " + str(self.timeoutSeconds) + " seconds")
return wasSuccessful
def main(args):
assumeNoSideEffects = False
assumeInputStatesAreCorrect = False
- tryFail = False
workPath = "/tmp/diff-filterer"
+ timeoutSeconds = None
maxNumJobsAtOnce = 1
while len(args) > 0:
arg = args[0]
@@ -910,10 +927,6 @@
assumeInputStatesAreCorrect = True
args = args[1:]
continue
- if arg == "--try-fail":
- tryFail = True
- args = args[1:]
- continue
if arg == "--work-path":
if len(args) < 2:
usage()
@@ -930,6 +943,13 @@
maxNumJobsAtOnce = int(val)
args = args[2:]
continue
+ if arg == "--timeout":
+ if len(args) < 2:
+ usage()
+ val = args[1]
+ timeoutSeconds = float(val)
+ args = args[2:]
+ continue
if arg == "--debug":
global debug
debug = True
@@ -945,17 +965,13 @@
failingPath = args[1]
shellCommand = args[2]
startTime = datetime.datetime.now()
- if tryFail:
- temp = passingPath
- passingPath = failingPath
- failingPath = temp
if not os.path.exists(passingPath):
print("Specified passing path " + passingPath + " does not exist")
sys.exit(1)
if not os.path.exists(failingPath):
print("Specified failing path " + failingPath + " does not exist")
sys.exit(1)
- success = DiffRunner(failingPath, passingPath, shellCommand, workPath, assumeNoSideEffects, assumeInputStatesAreCorrect, tryFail, maxNumJobsAtOnce).run()
+ success = DiffRunner(failingPath, passingPath, shellCommand, workPath, assumeNoSideEffects, assumeInputStatesAreCorrect, maxNumJobsAtOnce, timeoutSeconds).run()
endTime = datetime.datetime.now()
duration = endTime - startTime
if success:
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index f1bde8e..799e6c6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -47,6 +47,7 @@
androidLintChecks = { module = "com.android.tools.lint:lint-checks", version.ref = "androidLint" }
androidLintTests = { module = "com.android.tools.lint:lint-tests", version.ref = "androidLint" }
autoCommon = { module = "com.google.auto:auto-common", version = "0.11" }
+atomicFu = { module = "org.jetbrains.kotlinx:atomicfu-gradle-plugin", version = "0.14.4" }
autoServiceAnnotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoService" }
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
autoValue = { module = "com.google.auto.value:auto-value", version.ref = "autoValue" }
@@ -77,6 +78,7 @@
hiltCompiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
hiltCore = { module = "com.google.dagger:hilt-core", version.ref = "hilt" }
intellijAnnotations = { module = "com.intellij:annotations", version = "12.0" }
+japicmpPlugin = { module = "me.champeau.gradle:japicmp-gradle-plugin", version = "0.2.8" }
javapoet = { module = "com.squareup:javapoet", version = "1.13.0" }
jsqlparser = { module = "com.github.jsqlparser:jsqlparser", version = "3.1" }
jsr250 = { module = "javax.annotation:javax.annotation-api", version = "1.2" }
@@ -146,9 +148,10 @@
skikoMacOsX64 = { module = "org.jetbrains.skiko:skiko-jvm-runtime-macos-x64", version.ref = "skiko" }
skikoWindowsX64 = { module = "org.jetbrains.skiko:skiko-jvm-runtime-windows-x64", version.ref = "skiko" }
skikoLinuxX64 = { module = "org.jetbrains.skiko:skiko-jvm-runtime-linux-x64", version.ref = "skiko" }
+skikoLinuxArm64 = { module = "org.jetbrains.skiko:skiko-jvm-runtime-linux-arm64", version.ref = "skiko" }
sqldelightAndroid = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelightCoroutinesExt = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
-sqliteJdbc = { module = "org.xerial:sqlite-jdbc", version = "3.34.0" }
+sqliteJdbc = { module = "org.xerial:sqlite-jdbc", version = "3.36.0" }
testCore = { module = "androidx.test:core", version.ref = "androidxTest" }
testExtJunit = { module = "androidx.test.ext:junit", version.ref = "androidxTestExt" }
testExtJunitKtx = { module = "androidx.test.ext:junit-ktx", version.ref = "androidxTestExt" }
diff --git a/mediarouter/mediarouter/api/current.txt b/mediarouter/mediarouter/api/current.txt
index 96cefe9..08b4819 100644
--- a/mediarouter/mediarouter/api/current.txt
+++ b/mediarouter/mediarouter/api/current.txt
@@ -456,6 +456,7 @@
method public boolean isTransferToLocalEnabled();
field public static final int DIALOG_TYPE_DEFAULT = 1; // 0x1
field public static final int DIALOG_TYPE_DYNAMIC_GROUP = 2; // 0x2
+ field public static final String EXTRAS_KEY_DISABLE_GROUP_VOLUME_UX = "androidx.mediarouter.media.MediaRouterParams.DISABLE_GROUP_VOLUME_UX";
}
public static final class MediaRouterParams.Builder {
diff --git a/mediarouter/mediarouter/api/public_plus_experimental_current.txt b/mediarouter/mediarouter/api/public_plus_experimental_current.txt
index 96cefe9..08b4819 100644
--- a/mediarouter/mediarouter/api/public_plus_experimental_current.txt
+++ b/mediarouter/mediarouter/api/public_plus_experimental_current.txt
@@ -456,6 +456,7 @@
method public boolean isTransferToLocalEnabled();
field public static final int DIALOG_TYPE_DEFAULT = 1; // 0x1
field public static final int DIALOG_TYPE_DYNAMIC_GROUP = 2; // 0x2
+ field public static final String EXTRAS_KEY_DISABLE_GROUP_VOLUME_UX = "androidx.mediarouter.media.MediaRouterParams.DISABLE_GROUP_VOLUME_UX";
}
public static final class MediaRouterParams.Builder {
diff --git a/mediarouter/mediarouter/api/restricted_current.txt b/mediarouter/mediarouter/api/restricted_current.txt
index 96cefe9..08b4819 100644
--- a/mediarouter/mediarouter/api/restricted_current.txt
+++ b/mediarouter/mediarouter/api/restricted_current.txt
@@ -456,6 +456,7 @@
method public boolean isTransferToLocalEnabled();
field public static final int DIALOG_TYPE_DEFAULT = 1; // 0x1
field public static final int DIALOG_TYPE_DYNAMIC_GROUP = 2; // 0x2
+ field public static final String EXTRAS_KEY_DISABLE_GROUP_VOLUME_UX = "androidx.mediarouter.media.MediaRouterParams.DISABLE_GROUP_VOLUME_UX";
}
public static final class MediaRouterParams.Builder {
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteControllerDialog.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteControllerDialog.java
index b5d7e8f..1817674 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteControllerDialog.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteControllerDialog.java
@@ -73,6 +73,7 @@
import androidx.mediarouter.R;
import androidx.mediarouter.media.MediaRouteSelector;
import androidx.mediarouter.media.MediaRouter;
+import androidx.mediarouter.media.MediaRouterParams;
import androidx.palette.graphics.Palette;
import java.io.BufferedInputStream;
@@ -140,10 +141,13 @@
private TextView mRouteNameTextView;
private boolean mVolumeControlEnabled = true;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ final boolean mDisableGroupVolumeUX;
// Layout for media controllers including play/pause button and the main volume slider.
private LinearLayout mMediaMainControlLayout;
private RelativeLayout mPlaybackControlLayout;
- private LinearLayout mVolumeControlLayout;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ LinearLayout mVolumeControlLayout;
private View mDividerView;
OverlayListView mVolumeGroupList;
@@ -208,6 +212,10 @@
mControllerCallback = new MediaControllerCallback();
mRouter = MediaRouter.getInstance(mContext);
+ MediaRouterParams params = mRouter.getRouterParams();
+ Bundle extras = (params != null) ? params.getExtras() : null;
+ mDisableGroupVolumeUX = (extras != null
+ && extras.getBoolean(MediaRouterParams.EXTRAS_KEY_DISABLE_GROUP_VOLUME_UX));
mCallback = new MediaRouterCallback();
mRoute = mRouter.getSelectedRoute();
setMediaSession(mRouter.getMediaSessionToken());
@@ -486,7 +494,9 @@
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
- mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
+ if (!mDisableGroupVolumeUX || !mIsGroupExpanded) {
+ mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
+ }
return true;
}
return super.onKeyDown(keyCode, event);
@@ -725,7 +735,17 @@
}
private void updateVolumeControlLayout() {
- if (isVolumeControlAvailable(mRoute)) {
+ if (mDisableGroupVolumeUX && mRoute.isGroup()) {
+ mVolumeControlLayout.setVisibility(View.GONE);
+ mIsGroupExpanded = true;
+ mVolumeGroupList.setVisibility(View.VISIBLE);
+ loadInterpolator();
+ updateLayoutHeight(false);
+ return;
+ }
+ if ((mIsGroupExpanded && mDisableGroupVolumeUX) || !isVolumeControlAvailable(mRoute)) {
+ mVolumeControlLayout.setVisibility(View.GONE);
+ } else {
if (mVolumeControlLayout.getVisibility() == View.GONE) {
mVolumeControlLayout.setVisibility(View.VISIBLE);
mVolumeSlider.setMax(mRoute.getVolumeMax());
@@ -733,8 +753,6 @@
mGroupExpandCollapseButton.setVisibility(mRoute.isGroup()
? View.VISIBLE : View.GONE);
}
- } else {
- mVolumeControlLayout.setVisibility(View.GONE);
}
}
@@ -1086,7 +1104,8 @@
}
void updateArtIconIfNeeded() {
- if (mCustomControlView != null || !isIconChanged()) {
+ if (mCustomControlView != null || !isIconChanged()
+ || (mRoute.isGroup() && mDisableGroupVolumeUX)) {
return;
}
if (mFetchArtTask != null) {
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDynamicControllerDialog.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDynamicControllerDialog.java
index ac9d6da..dd343f5 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDynamicControllerDialog.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDynamicControllerDialog.java
@@ -70,6 +70,7 @@
import androidx.mediarouter.media.MediaRouteProvider;
import androidx.mediarouter.media.MediaRouteSelector;
import androidx.mediarouter.media.MediaRouter;
+import androidx.mediarouter.media.MediaRouterParams;
import androidx.palette.graphics.Palette;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -210,6 +211,7 @@
Bitmap mArtIconLoadedBitmap;
@SuppressWarnings("WeakerAccess") /* synthetic access */
int mArtIconBackgroundColor;
+ final boolean mDisableGroupVolumeUX;
public MediaRouteDynamicControllerDialog(@NonNull Context context) {
this(context, 0);
@@ -221,6 +223,10 @@
mContext = getContext();
mRouter = MediaRouter.getInstance(mContext);
+ MediaRouterParams params = mRouter.getRouterParams();
+ Bundle extras = (params != null) ? params.getExtras() : null;
+ mDisableGroupVolumeUX = (extras != null
+ && extras.getBoolean(MediaRouterParams.EXTRAS_KEY_DISABLE_GROUP_VOLUME_UX));
mCallback = new MediaRouterCallback();
mSelectedRoute = mRouter.getSelectedRoute();
mControllerCallback = new MediaControllerCallback();
@@ -790,7 +796,7 @@
}
boolean isGroupVolumeNeeded() {
- return mSelectedRoute.getMemberRoutes().size() > 1;
+ return !mDisableGroupVolumeUX && mSelectedRoute.getMemberRoutes().size() > 1;
}
void animateLayoutHeight(final View view, int targetHeight) {
@@ -843,7 +849,7 @@
boolean wasShown = isGroupVolumeNeeded();
// Group volume is shown when two or more members are in the selected route.
- boolean shouldShow = memberCount >= 2;
+ boolean shouldShow = !mDisableGroupVolumeUX && memberCount >= 2;
if (wasShown != shouldShow) {
RecyclerView.ViewHolder viewHolder =
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
index 2e7ff569..35155ca 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
@@ -1511,6 +1511,12 @@
*/
@PlaybackVolume
public int getVolumeHandling() {
+ Bundle extras = (sGlobal.getRouterParams() == null)
+ ? null : sGlobal.getRouterParams().getExtras();
+ if (isGroup() && extras != null
+ && extras.getBoolean(MediaRouterParams.EXTRAS_KEY_DISABLE_GROUP_VOLUME_UX)) {
+ return PLAYBACK_VOLUME_FIXED;
+ }
return mVolumeHandling;
}
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterParams.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterParams.java
index 824d1ca..86aca7f 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterParams.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterParams.java
@@ -56,6 +56,14 @@
public @interface DialogType {}
/**
+ * Bundle key used for disabling group volume UX.
+ *
+ * <p>TYPE: boolean
+ */
+ public static final String EXTRAS_KEY_DISABLE_GROUP_VOLUME_UX =
+ "androidx.mediarouter.media.MediaRouterParams.DISABLE_GROUP_VOLUME_UX";
+
+ /**
* Bundle key used for setting the cast icon fixed regardless of its connection state.
*
* <p>TYPE: boolean
@@ -65,13 +73,6 @@
public static final String EXTRAS_KEY_FIXED_CAST_ICON =
"androidx.mediarouter.media.MediaRouterParams.FIXED_CAST_ICON";
- /**
- * @hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- public static final String EXTRAS_KEY_TEST_PRIVATE_UI =
- "androidx.mediarouter.media.MediaRouterParams.TEST_PRIVATE_UI";
-
@DialogType
final int mDialogType;
final boolean mOutputSwitcherEnabled;
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/verifier/DatabaseVerifier.kt b/room/room-compiler/src/main/kotlin/androidx/room/verifier/DatabaseVerifier.kt
index b435de1..d7fd403 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/verifier/DatabaseVerifier.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/verifier/DatabaseVerifier.kt
@@ -26,6 +26,7 @@
import androidx.room.vo.Warning
import columnInfo
import org.sqlite.JDBC
+import org.sqlite.SQLiteJDBCLoader
import java.io.File
import java.sql.Connection
import java.sql.SQLException
@@ -67,7 +68,7 @@
// multiple library versions, process isolation and multiple class loaders by using
// UUID named library files.
synchronized(System::class.java) {
- NativeSQLiteLoader.load() // extract and loads native library
+ SQLiteJDBCLoader.initialize() // extract and loads native library
JDBC.isValidURL(CONNECTION_URL) // call to register driver
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/verifier/NativeSQLiteLoader.kt b/room/room-compiler/src/main/kotlin/androidx/room/verifier/NativeSQLiteLoader.kt
deleted file mode 100644
index 677411c..0000000
--- a/room/room-compiler/src/main/kotlin/androidx/room/verifier/NativeSQLiteLoader.kt
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2021 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.verifier
-
-import org.sqlite.SQLiteJDBCLoader
-import org.sqlite.util.OSInfo
-import java.io.File
-import java.io.IOException
-import java.io.InputStream
-import java.util.UUID
-
-/**
- * A custom sqlite-jdbc native library extractor and loader.
- *
- * This class is used instead of [SQLiteJDBCLoader.initialize] since it workarounds current issues
- * in the loading strategy, specifically: https://github.com/xerial/sqlite-jdbc/pull/578.
- */
-internal object NativeSQLiteLoader {
-
- private var loaded = false
-
- private val tempDir: File by lazy {
- File(System.getProperty("org.sqlite.tmpdir", System.getProperty("java.io.tmpdir")))
- }
-
- private val version: String by lazy { SQLiteJDBCLoader.getVersion() }
-
- @JvmStatic
- fun load() = synchronized(loaded) {
- if (loaded) return
- try {
- // Cleanup target temporary folder for a new extraction.
- cleanupTempFolder()
- // Extract and load native library.
- loadNativeLibrary()
- // Reflect into original loader and mark library as extracted.
- SQLiteJDBCLoader::class.java.getDeclaredField("extracted")
- .apply { trySetAccessible() }
- .set(null, true)
- } catch (ex: Exception) {
- // Fallback to main library if our attempt failed, do print error juuust in case, so if
- // there is an error with our approach we get to know, instead of fully swallowing it.
- RuntimeException("Failed to load native SQLite library, will try again though.", ex)
- .printStackTrace()
- SQLiteJDBCLoader.initialize()
- }
- loaded = true
- }
-
- private fun cleanupTempFolder() {
- tempDir.listFiles { file ->
- file.name.startsWith("sqlite-$version") && !file.name.endsWith(".lck")
- }?.forEach { libFile ->
- val lckFile = File(libFile.absolutePath + ".lck")
- if (!lckFile.exists()) {
- libFile.delete()
- }
- }
- }
-
- // Load the OS-dependent library from the Jar file.
- private fun loadNativeLibrary() {
- val packagePath =
- SQLiteJDBCLoader::class.java.getPackage().name.replace(".", "/")
- val nativeLibraryPath =
- "/$packagePath/native/${OSInfo.getNativeLibFolderPathForCurrentOS()}"
- val nativeLibraryName = let {
- val libName = System.mapLibraryName("sqlitejdbc")
- .apply { replace("dylib", "jnilib") }
- if (hasResource("$nativeLibraryPath/$libName")) {
- return@let libName
- }
- if (OSInfo.getOSName() == "Mac") {
- // Fix for openjdk7 for Mac
- val altLibName = "libsqlitejdbc.jnilib"
- if (hasResource("$nativeLibraryPath/$altLibName")) {
- return@let altLibName
- }
- }
- error(
- "No native library is found for os.name=${OSInfo.getOSName()} and " +
- "os.arch=${OSInfo.getArchName()}. path=$nativeLibraryPath"
- )
- }
-
- val extractedNativeLibraryFile = try {
- extractNativeLibrary(nativeLibraryPath, nativeLibraryName, tempDir.absolutePath)
- } catch (ex: IOException) {
- throw RuntimeException("Couldn't extract native SQLite library.", ex)
- }
- try {
- @Suppress("UnsafeDynamicallyLoadedCode") // Loading an from an absolute path.
- System.load(extractedNativeLibraryFile.absolutePath)
- } catch (ex: UnsatisfiedLinkError) {
- throw RuntimeException("Couldn't load native SQLite library.", ex)
- }
- }
-
- private fun extractNativeLibrary(
- libraryPath: String,
- libraryName: String,
- targetDirPath: String
- ): File {
- val libraryFilePath = "$libraryPath/$libraryName"
- // Include arch name in temporary filename in order to avoid conflicts when multiple JVMs
- // with different architectures are running.
- val outputLibraryFile = File(
- targetDirPath,
- "sqlite-$version-${UUID.randomUUID()}-$libraryName"
- ).apply { deleteOnExit() }
- val outputLibraryLckFile = File(
- targetDirPath,
- "${outputLibraryFile.name}.lck"
- ).apply { deleteOnExit() }
- if (!outputLibraryLckFile.exists()) {
- outputLibraryLckFile.outputStream().close()
- }
- getResourceAsStream(libraryFilePath).use { inputStream ->
- outputLibraryFile.outputStream().use { outputStream ->
- inputStream.copyTo(outputStream)
- }
- }
- // Set executable flag (x) to enable loading the library.
- outputLibraryFile.setReadable(true)
- outputLibraryFile.setExecutable(true)
- return outputLibraryFile
- }
-
- private fun hasResource(path: String) = SQLiteJDBCLoader::class.java.getResource(path) != null
-
- // Replacement of java.lang.Class#getResourceAsStream(String) to disable sharing the resource
- // stream in multiple class loaders and specifically to avoid
- // https://bugs.openjdk.java.net/browse/JDK-8205976
- private fun getResourceAsStream(name: String): InputStream {
- // Remove leading '/' since all our resource paths include a leading directory
- // See: https://github.com/openjdk/jdk/blob/jdk-11+0/src/java.base/share/classes/java/lang/Class.java#L2573
- val resolvedName = name.drop(1)
- val url = SQLiteJDBCLoader::class.java.classLoader.getResource(resolvedName)
- ?: throw IOException("Resource '$resolvedName' could not be found.")
- return url.openConnection().apply {
- defaultUseCaches = false
- }.getInputStream()
- }
-}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/verifier/NativeSQLiteLoaderTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/verifier/NativeSQLiteLoaderTest.kt
deleted file mode 100644
index 8cf547b..0000000
--- a/room/room-compiler/src/test/kotlin/androidx/room/verifier/NativeSQLiteLoaderTest.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2021 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.verifier
-
-import com.google.common.truth.Truth.assertThat
-import org.junit.Assert.fail
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import java.io.File
-import java.net.URL
-import java.net.URLClassLoader
-import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.atomic.AtomicInteger
-import java.util.jar.JarEntry
-import java.util.jar.JarOutputStream
-
-@RunWith(JUnit4::class)
-class NativeSQLiteLoaderTest {
-
- @Test
- fun multipleClassLoader() {
- // Get current classpath
- val stringUrls = System.getProperty("java.class.path")!!
- .split(System.getProperty("path.separator")!!).toTypedArray()
- // Find classes under test.
- val targetDirName = "room-compiler/build/classes/kotlin/main"
- val classesDirPath = stringUrls.first { it.contains(targetDirName) }
- // Create a JAR file out the classes and resources
- val jarFile = File.createTempFile("jar-for-test-", ".jar")
- createJar(classesDirPath, jarFile)
- val jarUrl = URL("file://${jarFile.absolutePath}")
- // Find Kotlin stdlibs (will need them to load class under test)
- val kotlinStdbLibUrls = stringUrls
- .filter { it.contains("kotlin-stdlib") && it.endsWith(".jar") }
- .map { URL("file://$it") }
- // Also find sqlite-jdbc since it is a hard dep of NativeSQLiteLoader
- val sqliteJdbcJarUrl = stringUrls
- .filter { it.contains("sqlite-jdbc") && it.endsWith(".jar") }
- .map { URL("file://$it") }
- // Spawn a few threads and have them all in parallel load the native lib
- val completedThreads = AtomicInteger(0)
- val numOfThreads = 8
- val pool = Executors.newFixedThreadPool(numOfThreads)
- val loadedClasses = arrayOfNulls<Class<*>>(numOfThreads)
- for (i in 1..numOfThreads) {
- pool.execute {
- try {
- Thread.sleep((i * 10).toLong())
- // Create an isolated class loader, it should load *different* instances
- // of NativeSQLiteLoader.class
- val classLoader = URLClassLoader(
- (kotlinStdbLibUrls + sqliteJdbcJarUrl + jarUrl).toTypedArray(),
- ClassLoader.getSystemClassLoader().parent
- )
- val clazz =
- classLoader.loadClass("androidx.room.verifier.NativeSQLiteLoader")
- clazz.getDeclaredMethod("load").invoke(null)
- classLoader.close()
- loadedClasses[i - 1] = clazz
- } catch (e: Throwable) {
- e.printStackTrace()
- fail(e.message)
- }
- completedThreads.incrementAndGet()
- }
- }
- // Verify all threads completed
- pool.shutdown()
- pool.awaitTermination(3, TimeUnit.SECONDS)
- assertThat(completedThreads.get()).isEqualTo(numOfThreads)
- // Verify all loaded classes are different from each other
- loadedClasses.forEachIndexed { i, clazz1 ->
- loadedClasses.forEachIndexed { j, clazz2 ->
- if (i == j) {
- assertThat(clazz1).isEqualTo(clazz2)
- } else {
- assertThat(clazz1).isNotEqualTo(clazz2)
- }
- }
- }
- }
-
- private fun createJar(inputDir: String, outputFile: File) {
- JarOutputStream(outputFile.outputStream()).use {
- addJarEntry(File(inputDir), inputDir, it)
- }
- }
-
- private fun addJarEntry(source: File, changeDir: String, target: JarOutputStream) {
- if (source.isDirectory) {
- var name = source.path.replace("\\", "/")
- if (name.isNotEmpty()) {
- if (!name.endsWith("/")) {
- name += "/"
- }
- val entry = JarEntry(name.substring(changeDir.length + 1))
- entry.time = source.lastModified()
- target.putNextEntry(entry)
- target.closeEntry()
- }
- source.listFiles()!!.forEach { nestedFile ->
- addJarEntry(nestedFile, changeDir, target)
- }
- } else if (source.isFile) {
- val entry = JarEntry(
- source.path.replace("\\", "/").substring(changeDir.length + 1)
- )
- entry.time = source.lastModified()
- target.putNextEntry(entry)
- source.inputStream().use { inputStream ->
- inputStream.copyTo(target)
- }
- target.closeEntry()
- }
- }
-}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 51b6560..f413473 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -32,7 +32,12 @@
} else if (os.startsWith("win")) {
currentOsArtifact = "skiko-jvm-runtime-windows-x64"
} else if (os.startsWith("linux") ) {
- currentOsArtifact = "skiko-jvm-runtime-linux-x64"
+ def arch = System.getProperty("os.arch")
+ if (arch == "aarch64") {
+ currentOsArtifact = "skiko-jvm-runtime-linux-arm64"
+ } else {
+ currentOsArtifact = "skiko-jvm-runtime-linux-x64"
+ }
} else {
throw GradleException("Unsupported operating system $os")
}
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index a997c56..e6a93d3 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -490,10 +490,17 @@
}
private var inOnSetStyle = false
+ internal var initComplete = false
private val ambientObserver = Observer<Boolean> {
- scheduleDraw()
- watchFaceHostApi.invalidate()
+ TraceEvent("WatchFaceImpl.ambientObserver").use {
+ // It's not safe to draw until initComplete because the ComplicationSlotManager init
+ // may not have completed.
+ if (initComplete) {
+ onDraw()
+ }
+ scheduleDraw()
+ }
}
private val interruptionFilterObserver = Observer<Int> {
@@ -515,11 +522,16 @@
// Update time zone in case it changed while we weren't visible.
calendar.timeZone = TimeZone.getDefault()
watchFaceHostApi.invalidate()
+
+ // It's not safe to draw until initComplete because the ComplicationSlotManager init
+ // may not have completed.
+ if (initComplete) {
+ onDraw()
+ }
+ scheduleDraw()
} else {
unregisterReceivers()
}
-
- scheduleDraw()
}
}
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 4b1fcb7..9d34d27a 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -1451,6 +1451,7 @@
initStyleAndComplicationsDone.await()
deferredWatchFaceImpl.complete(watchFaceImpl)
asyncWatchFaceConstructionPending = false
+ watchFaceImpl.initComplete = true
// For interactive instances we want to expedite the first frame to get something
// rendered as soon as its possible to do so. NB in tests we may not always want