Merge "[NavigationBar][NavigationRail] Update tokens." into androidx-main
diff --git a/activity/activity-compose/src/androidTest/java/androidx/activity/compose/PredictiveBackHandlerTest.kt b/activity/activity-compose/src/androidTest/java/androidx/activity/compose/PredictiveBackHandlerTest.kt
index 1ba51b4..a8e1bee 100644
--- a/activity/activity-compose/src/androidTest/java/androidx/activity/compose/PredictiveBackHandlerTest.kt
+++ b/activity/activity-compose/src/androidTest/java/androidx/activity/compose/PredictiveBackHandlerTest.kt
@@ -172,6 +172,71 @@
}
}
+ @Test
+ fun testPredictiveBackHandlerDisabledBeforeStart() {
+ val result = mutableListOf<String>()
+ var count by mutableStateOf(2)
+ lateinit var dispatcherOwner: TestOnBackPressedDispatcherOwner
+ lateinit var dispatcher: OnBackPressedDispatcher
+ var started = false
+
+ rule.setContent {
+ dispatcherOwner =
+ TestOnBackPressedDispatcherOwner(LocalLifecycleOwner.current.lifecycle)
+ CompositionLocalProvider(LocalOnBackPressedDispatcherOwner provides dispatcherOwner) {
+ PredictiveBackHandler(count > 1) { progress ->
+ if (count <= 1) {
+ started = true
+ }
+ progress.collect()
+ result += "onBack"
+ }
+ dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
+ }
+ }
+
+ // Changing the count right before starting the gesture is received in the
+ // onBackStackStarted callback
+ count = 1
+ dispatcher.startGestureBack()
+
+ rule.runOnIdle { assertThat(started).isTrue() }
+ dispatcher.api34Complete()
+ rule.runOnIdle { assertThat(result).isEqualTo(listOf("onBack")) }
+ }
+
+ fun testPredictiveBackHandlerDisabledAfterStart() {
+ val result = mutableListOf<String>()
+ var count by mutableStateOf(2)
+ lateinit var dispatcherOwner: TestOnBackPressedDispatcherOwner
+ lateinit var dispatcher: OnBackPressedDispatcher
+ var started = false
+
+ rule.setContent {
+ dispatcherOwner =
+ TestOnBackPressedDispatcherOwner(LocalLifecycleOwner.current.lifecycle)
+ CompositionLocalProvider(LocalOnBackPressedDispatcherOwner provides dispatcherOwner) {
+ PredictiveBackHandler(count > 1) { progress ->
+ if (count <= 1) {
+ started = true
+ }
+ progress.collect()
+ result += "onBack"
+ }
+ dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
+ }
+ }
+
+ dispatcher.startGestureBack()
+ // Changing the count right after starting the gesture is not received in the
+ // onBackStackStarted callback
+ count = 1
+
+ rule.runOnIdle { assertThat(started).isFalse() }
+ dispatcher.api34Complete()
+ rule.runOnIdle { assertThat(result).isEqualTo(listOf("onBack")) }
+ }
+
@Test(expected = IllegalStateException::class)
fun testNoCollection() {
val result = mutableListOf<String>()
diff --git a/activity/activity-compose/src/main/java/androidx/activity/compose/PredictiveBackHandler.kt b/activity/activity-compose/src/main/java/androidx/activity/compose/PredictiveBackHandler.kt
index 1000765..3bdbb96 100644
--- a/activity/activity-compose/src/main/java/androidx/activity/compose/PredictiveBackHandler.kt
+++ b/activity/activity-compose/src/main/java/androidx/activity/compose/PredictiveBackHandler.kt
@@ -76,10 +76,10 @@
// ensure we don't re-register callbacks when onBack changes
val currentOnBack by rememberUpdatedState(onBack)
val onBackScope = rememberCoroutineScope()
+ var onBackInstance: OnBackInstance? = null
val backCallBack = remember {
object : OnBackPressedCallback(enabled) {
- var onBackInstance: OnBackInstance? = null
override fun handleOnBackStarted(backEvent: BackEventCompat) {
super.handleOnBackStarted(backEvent)
@@ -125,7 +125,13 @@
}
}
- LaunchedEffect(enabled) { backCallBack.isEnabled = enabled }
+ LaunchedEffect(enabled) {
+ backCallBack.isEnabled = enabled
+ if (!enabled) {
+ onBackInstance?.close()
+ onBackInstance = null
+ }
+ }
val backDispatcher =
checkNotNull(LocalOnBackPressedDispatcherOwner.current) {
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/EnterpriseGlobalSearchSessionPlatformCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/EnterpriseGlobalSearchSessionPlatformCtsTest.java
index 968ce31..97f487a 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/EnterpriseGlobalSearchSessionPlatformCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/EnterpriseGlobalSearchSessionPlatformCtsTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+// @exportToFramework:skipFile()
package androidx.appsearch.cts.app;
import android.content.Context;
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GenericDocumentCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GenericDocumentCtsTest.java
index b666e4d..74a03e5 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GenericDocumentCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GenericDocumentCtsTest.java
@@ -20,12 +20,16 @@
import static org.junit.Assert.assertThrows;
+import android.os.Build;
+import android.os.Parcel;
+
import androidx.appsearch.app.EmbeddingVector;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.flags.CheckFlagsRule;
import androidx.appsearch.flags.DeviceFlagsValueProvider;
import androidx.appsearch.flags.Flags;
import androidx.appsearch.flags.RequiresFlagsEnabled;
+import androidx.test.filters.SdkSuppress;
import org.junit.Rule;
import org.junit.Test;
@@ -1219,4 +1223,39 @@
() -> new EmbeddingVector(new float[]{}, "my_model"));
assertThat(exception).hasMessageThat().contains("Embedding values cannot be empty.");
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_OVER_IPC)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public void testWriteToParcel() {
+ GenericDocument inDoc =
+ new GenericDocument.Builder<>("namespace", "id1", "schema1")
+ .setScore(42)
+ .setPropertyString("propString", "Hello")
+ .setPropertyBytes("propBytes", new byte[][] {{1, 2}})
+ .setPropertyDocument(
+ "propDocument",
+ new GenericDocument.Builder<>("namespace", "id2", "schema2")
+ .setPropertyString("propString", "Goodbye")
+ .setPropertyBytes("propBytes", new byte[][] {{3, 4}})
+ .build())
+ .build();
+
+ // Serialize the document
+ Parcel parcel = Parcel.obtain();
+ inDoc.writeToParcel(parcel, /* flags= */ 0);
+
+ // Deserialize the document
+ parcel.setDataPosition(0);
+ GenericDocument document = GenericDocument.createFromParcel(parcel);
+ parcel.recycle();
+
+ // Compare results
+ assertThat(document.getPropertyString("propString")).isEqualTo("Hello");
+ assertThat(document.getPropertyBytesArray("propBytes")).isEqualTo(new byte[][] {{1, 2}});
+ assertThat(document.getPropertyDocument("propDocument").getPropertyString("propString"))
+ .isEqualTo("Goodbye");
+ assertThat(document.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes"))
+ .isEqualTo(new byte[][] {{3, 4}});
+ }
}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java
index 1428f0f..d4856d7 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java
@@ -113,7 +113,7 @@
public void testFlagValue_enableSearchSpecSearchStringParameters() {
assertThat(Flags.FLAG_ENABLE_SEARCH_SPEC_SEARCH_STRING_PARAMETERS)
.isEqualTo(
- "com.android.appsearch.flags.enable_search_spec_search_spec_strings");
+ "com.android.appsearch.flags.enable_search_spec_search_string_parameters");
}
@Test
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/CurrentTimeMillisLong.java b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/CurrentTimeMillisLong.java
new file mode 100644
index 0000000..2b1d3f4
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/CurrentTimeMillisLong.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+// @exportToFramework:skipFile()
+package androidx.appsearch.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc Value is a non-negative timestamp measured as the number of
+ * milliseconds since 1970-01-01T00:00:00Z.
+ * @paramDoc Value is a non-negative timestamp measured as the number of
+ * milliseconds since 1970-01-01T00:00:00Z.
+ * @returnDoc Value is a non-negative timestamp measured as the number of
+ * milliseconds since 1970-01-01T00:00:00Z.
+ */
+@Retention(SOURCE)
+@Target({METHOD, PARAMETER, FIELD})
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public @interface CurrentTimeMillisLong {
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/SystemApi.java b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/SystemApi.java
new file mode 100644
index 0000000..678d085
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/SystemApi.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+// @exportToFramework:skipFile()
+package androidx.appsearch.annotation;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.TYPE;
+
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates an API is exposed for use by bundled system applications.
+ *
+ * <p>These APIs are not guaranteed to remain consistent release-to-release,
+ * and are not for use by apps linking against the Android SDK.
+ *
+ * <p>This annotation should only appear on API that is already marked @<pre>hide</pre>.
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
+@Retention(RetentionPolicy.RUNTIME)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public @interface SystemApi {
+ enum Client {
+ /**
+ * Specifies that the intended clients of a SystemApi are privileged apps.
+ * This is the default value for {@link #client}.
+ */
+ PRIVILEGED_APPS,
+
+ /**
+ * Specifies that the intended clients of a SystemApi are used by classes in
+ * <pre>BOOTCLASSPATH</pre> in mainline modules. Mainline modules can also expose
+ * this type of system APIs too when they're used only by the non-updatable
+ * platform code.
+ */
+ MODULE_LIBRARIES,
+
+ /**
+ * Specifies that the system API is available only in the system server process.
+ * Use this to expose APIs from code loaded by the system server process <em>but</em>
+ * not in <pre>BOOTCLASSPATH</pre>.
+ */
+ SYSTEM_SERVER
+ }
+
+ /**
+ * The intended client of this SystemAPI.
+ */
+ Client client() default Client.PRIVILEGED_APPS;
+
+ /**
+ * Container for {@link SystemApi} that allows it to be applied repeatedly to types.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(TYPE)
+ @interface Container {
+ SystemApi[] value();
+ }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBlobHandle.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBlobHandle.java
index d6ed1c2..2d6501d 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBlobHandle.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBlobHandle.java
@@ -71,7 +71,7 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Constructor
- private AppSearchBlobHandle(
+ AppSearchBlobHandle(
@Param(id = 1) @NonNull byte[] sha256Digest,
@Param(id = 2) @NonNull String label) {
mSha256Digest = Preconditions.checkNotNull(sha256Digest);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
index 6de5448..006d119 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
@@ -17,14 +17,19 @@
package androidx.appsearch.app;
import android.annotation.SuppressLint;
+import android.os.Build;
+import android.os.Parcel;
import android.util.Log;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.annotation.CurrentTimeMillisLong;
import androidx.appsearch.annotation.Document;
+import androidx.appsearch.annotation.SystemApi;
import androidx.appsearch.exceptions.AppSearchException;
import androidx.appsearch.flags.FlaggedApi;
import androidx.appsearch.flags.Flags;
@@ -152,6 +157,45 @@
}
/**
+ * Writes the {@link GenericDocument} to the given {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to write to.
+ * @param flags The flags to use for parceling.
+ * @exportToFramework:hide
+ */
+ // GenericDocument is an open class that can be extended, whereas parcelable classes must be
+ // final in those methods. Thus, we make this a system api to avoid 3p apps depending on it
+ // and getting confused by the inheritability.
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @FlaggedApi(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_OVER_IPC)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public final void writeToParcel(@NonNull Parcel dest, int flags) {
+ Objects.requireNonNull(dest);
+ dest.writeParcelable(mDocumentParcel, flags);
+ }
+
+ /**
+ * Creates a {@link GenericDocument} from a {@link Parcel}.
+ *
+ * @param parcel The {@link Parcel} to read from.
+ * @exportToFramework:hide
+ */
+ // GenericDocument is an open class that can be extended, whereas parcelable classes must be
+ // final in those methods. Thus, we make this a system api to avoid 3p apps depending on it
+ // and getting confused by the inheritability.
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+ @FlaggedApi(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_OVER_IPC)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @NonNull
+ public static GenericDocument createFromParcel(@NonNull Parcel parcel) {
+ Objects.requireNonNull(parcel);
+ return new GenericDocument(
+ parcel.readParcelable(
+ GenericDocumentParcel.class.getClassLoader(), GenericDocumentParcel.class));
+ }
+
+ /**
* Returns the {@link GenericDocumentParcel} holding the values for this
* {@link GenericDocument}.
*
@@ -202,7 +246,7 @@
*
* <p>The value is in the {@link System#currentTimeMillis} time base.
*/
- /*@exportToFramework:CurrentTimeMillisLong*/
+ @CurrentTimeMillisLong
public long getCreationTimestampMillis() {
return mDocumentParcel.getCreationTimestampMillis();
}
@@ -1358,7 +1402,7 @@
@CanIgnoreReturnValue
@NonNull
public BuilderType setCreationTimestampMillis(
- /*@exportToFramework:CurrentTimeMillisLong*/ long creationTimestampMillis) {
+ @CurrentTimeMillisLong long creationTimestampMillis) {
mDocumentParcelBuilder.setCreationTimestampMillis(creationTimestampMillis);
return mBuilderTypeInstance;
}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportSystemUsageRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportSystemUsageRequest.java
index 67550eb..00957811 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportSystemUsageRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportSystemUsageRequest.java
@@ -18,6 +18,7 @@
import androidx.annotation.NonNull;
import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.annotation.CurrentTimeMillisLong;
import androidx.core.util.Preconditions;
/**
@@ -79,7 +80,7 @@
*
* <p>The value is in the {@link System#currentTimeMillis} time base.
*/
- /*@exportToFramework:CurrentTimeMillisLong*/
+ @CurrentTimeMillisLong
public long getUsageTimestampMillis() {
return mUsageTimestampMillis;
}
@@ -127,7 +128,7 @@
@CanIgnoreReturnValue
@NonNull
public ReportSystemUsageRequest.Builder setUsageTimestampMillis(
- /*@exportToFramework:CurrentTimeMillisLong*/ long usageTimestampMillis) {
+ @CurrentTimeMillisLong long usageTimestampMillis) {
mUsageTimestampMillis = usageTimestampMillis;
return this;
}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java
index aafcc61..b911919 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java
@@ -22,6 +22,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.annotation.CurrentTimeMillisLong;
import androidx.appsearch.flags.FlaggedApi;
import androidx.appsearch.flags.Flags;
import androidx.appsearch.safeparcel.AbstractSafeParcelable;
@@ -84,7 +85,7 @@
*
* <p>The value is in the {@link System#currentTimeMillis} time base.
*/
- /*@exportToFramework:CurrentTimeMillisLong*/
+ @CurrentTimeMillisLong
public long getUsageTimestampMillis() {
return mUsageTimestampMillis;
}
@@ -127,7 +128,7 @@
@CanIgnoreReturnValue
@NonNull
public ReportUsageRequest.Builder setUsageTimestampMillis(
- /*@exportToFramework:CurrentTimeMillisLong*/ long usageTimestampMillis) {
+ @CurrentTimeMillisLong long usageTimestampMillis) {
mUsageTimestampMillis = usageTimestampMillis;
return this;
}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java b/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java
index 700cfb7..93d8138 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java
@@ -80,7 +80,7 @@
* methods.
*/
public static final String FLAG_ENABLE_SEARCH_SPEC_SEARCH_STRING_PARAMETERS =
- FLAG_PREFIX + "enable_search_spec_search_spec_strings";
+ FLAG_PREFIX + "enable_search_spec_search_string_parameters";
/** Enable addTakenActions API in PutDocumentsRequest. */
public static final String FLAG_ENABLE_PUT_DOCUMENTS_REQUEST_ADD_TAKEN_ACTIONS =
@@ -142,6 +142,10 @@
public static final String FLAG_ENABLE_BLOB_STORE =
FLAG_PREFIX + "enable_blob_store";
+ /** Enable {@link androidx.appsearch.app.GenericDocument#writeToParcel}. */
+ public static final String FLAG_ENABLE_GENERIC_DOCUMENT_OVER_IPC =
+ FLAG_PREFIX + "enable_generic_document_over_ipc";
+
/** Enable empty batch result fix for enterprise GetDocuments. */
public static final String FLAG_ENABLE_ENTERPRISE_EMPTY_BATCH_RESULT_FIX =
FLAG_PREFIX + "enable_enterprise_empty_batch_result_fix";
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcel.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcel.java
index 1d407fa..bbfb803 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcel.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcel.java
@@ -24,6 +24,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.annotation.CurrentTimeMillisLong;
import androidx.appsearch.app.AppSearchSchema;
import androidx.appsearch.app.AppSearchSession;
import androidx.appsearch.app.EmbeddingVector;
@@ -193,7 +194,7 @@
}
/** Returns the creation timestamp of the {@link GenericDocument}, in milliseconds. */
- /*@exportToFramework:CurrentTimeMillisLong*/
+ @CurrentTimeMillisLong
public long getCreationTimestampMillis() {
return mCreationTimestampMillis;
}
@@ -393,7 +394,7 @@
@CanIgnoreReturnValue
@NonNull
public Builder setCreationTimestampMillis(
- /*@exportToFramework:CurrentTimeMillisLong*/ long creationTimestampMillis) {
+ @CurrentTimeMillisLong long creationTimestampMillis) {
mCreationTimestampMillis = creationTimestampMillis;
return this;
}
diff --git a/appsearch/exportToFramework.py b/appsearch/exportToFramework.py
index b483b72..373cd50 100755
--- a/appsearch/exportToFramework.py
+++ b/appsearch/exportToFramework.py
@@ -32,9 +32,6 @@
# Replaced with @hide:
# <!--@exportToFramework:hide-->
#
-# Replaced with @CurrentTimeMillisLong:
-# /*@exportToFramework:CurrentTimeMillisLong*/
-#
# Removes the text appearing between ifJetpack() and else(), and causes the text appearing between
# else() and --> to become uncommented, to support framework-only Javadocs:
# <!--@exportToFramework:ifJetpack()-->
@@ -143,10 +140,6 @@
# Add additional imports if required
imports_to_add = []
- if '@exportToFramework:CurrentTimeMillisLong' in contents:
- imports_to_add.append('android.annotation.CurrentTimeMillisLong')
- if '@exportToFramework:UnsupportedAppUsage' in contents:
- imports_to_add.append('android.compat.annotation.UnsupportedAppUsage')
for import_to_add in imports_to_add:
contents = re.sub(
r'^(\s*package [^;]+;\s*)$', r'\1\nimport %s;\n' % import_to_add, contents,
@@ -167,6 +160,12 @@
'com.android.server.appsearch.external.localstorage.')
.replace('androidx.appsearch.flags.FlaggedApi', 'android.annotation.FlaggedApi')
.replace('androidx.appsearch.flags.Flags', 'com.android.appsearch.flags.Flags')
+ .replace(
+ 'androidx.appsearch.annotation.CurrentTimeMillis',
+ 'android.annotation.CurrentTimeMillis')
+ .replace(
+ 'androidx.appsearch.annotation.SystemApi',
+ 'android.annotation.SystemApi')
.replace('androidx.appsearch', 'android.app.appsearch')
.replace(
'androidx.annotation.GuardedBy',
@@ -190,9 +189,6 @@
.replace('@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)', '')
.replace('Preconditions.checkNotNull(', 'Objects.requireNonNull(')
.replace('ObjectsCompat.', 'Objects.')
-
- .replace('/*@exportToFramework:CurrentTimeMillisLong*/', '@CurrentTimeMillisLong')
- .replace('/*@exportToFramework:UnsupportedAppUsage*/', '@UnsupportedAppUsage')
.replace('<!--@exportToFramework:hide-->', '@hide')
.replace('@exportToFramework:hide', '@hide')
.replace('// @exportToFramework:skipFile()', '')
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index c2a6cc7..cdf3a164 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -22,6 +22,11 @@
repos.addMavenRepositories(repositories)
+project.tasks.withType(Jar).configureEach { task ->
+ task.reproducibleFileOrder = true
+ task.preserveFileTimestamps = false
+}
+
dependencies {
api(project("plugins"))
}
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 619c5f9..9c8f089 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -333,3 +333,5 @@
WARN: Attempt to load key 'java\.correct\.class\.type\.by\.place\.resolve\.scope' for not yet loaded registry
warning: unable to find kotlin\-stdlib\.jar in the Kotlin home directory\. Pass either '\-no\-stdlib' to prevent adding it to the classpath, or the correct '\-kotlin\-home'
warning: unable to find kotlin\-script\-runtime\.jar in the Kotlin home directory\. Pass either '\-no\-stdlib' to prevent adding it to the classpath, or the correct '\-kotlin\-home'
+# develocity plugin begign warning, reported back to Gradle
+Failed sysctl call: hw\.nperflevels, Error code: 2
diff --git a/development/validateRefactor.sh b/development/validateRefactor.sh
index 7170a98..805a46b 100755
--- a/development/validateRefactor.sh
+++ b/development/validateRefactor.sh
@@ -32,13 +32,18 @@
Validates that libraries built from the given versions are the same as
the build outputs built at HEAD. This can be used to validate that a refactor
- did not change the outputs. If a git treeish is given with no path, the path is considered to be frameworks/support
+ did not change the outputs.
+ If a git treeish is given with no path, the path is considered to be frameworks/support
Example: $0 HEAD^
Example: $0 prebuilts/androidx/external:HEAD^ frameworks/support:work^
- * A git treeish is what you type when you run 'git checkout <git treeish>'
+ * A git treeish is what you type when you run 'git checkout <git treeish>'
See also https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddeftree-ishatree-ishalsotreeish .
+
+ You can also supply additional arguments that will be passed through to validateRefactorHelper.py, using -P
+ For example, the baseline arguments that validateRefactorHelper.py accepts.
+ Example: $0 HEAD^ -PagpKmp
"
return 1
}
@@ -118,27 +123,35 @@
unzipInPlace "${tempOutPath}/dist/docs-public-0.zip"
}
-oldCommits="$(expandCommitArgs $@)"
+oldCommits=()
+passThruArgs=()
+while getopts ":p:g:" opt; do
+ case $opt in
+ \? ) usage;;
+ g ) oldCommits+="$(expandCommitArgs $OPTARG)";;
+ p ) passThruArgs+="$OPTARG";;
+ esac
+ case $OPTARG in
+ -*) usage;;
+ esac
+done
+
projectPaths="$(getParticipatingProjectPaths $oldCommits)"
-if echo $projectPaths | grep external/dokka >/dev/null; then
- if [ "$BUILD_DOKKA" == "" ]; then
- echo "It doesn't make sense to include the external/dokka project without also setting BUILD_DOKKA=true. Did you mean to set BUILD_DOKKA=true?"
- exit 1
- fi
-fi
-echo old commits: $oldCommits
if [ "$oldCommits" == "" ]; then
usage
fi
+
newCommits="$(getCurrentCommits $projectPaths)"
cd "$supportRoot"
+if [[ $(git update-index --refresh) ]]; then echo "You have local changes; stash or commit them or this script won't work"; exit 1; fi
+if [[ $(git diff-index --quiet HEAD) ]]; then echo "You have local changes; stash or commit them or this script won't work"; exit 1; fi
+echo old commits: $oldCommits
echo new commits: $newCommits
-
+cd "$supportRoot"
oldOutPath="${checkoutRoot}/out-old"
newOutPath="${checkoutRoot}/out-new"
tempOutPath="${checkoutRoot}/out"
-
rm -rf "$oldOutPath" "$newOutPath" "$tempOutPath"
echo building new commit
@@ -158,10 +171,9 @@
uncheckout "$projectPaths"
mv "$tempOutPath" "$oldOutPath"
+
echo
echo diffing results
-# Don't care about maven-metadata files because they have timestamps in them
-# We might care to know whether .sha1 or .md5 files have changed, but changes in those files will always be accompanied by more meaningful changes in other files, so we don't need to show changes in .sha1 or .md5 files
-# We also don't care about several specific files, either
-echoAndDo diff -r -x "*.md5*" -x "*.sha*" -x "*maven-metadata.xml" -x buildSrc.jar -x jetpad-integration.jar -x "top-of-tree-m2repository-all-0.zip" -x noto-emoji-compat-java.jar -x versionedparcelable-annotation.jar -x dokkaTipOfTreeDocs-0.zip "$oldOutPath/dist" "$newOutPath/dist"
+# This script performs the diff, and filters out known issues and non-issues with baselines
+python development/validateRefactorHelper.py "$passThruArgs"
echo end of difference
diff --git a/development/validateRefactorHelper.py b/development/validateRefactorHelper.py
new file mode 100644
index 0000000..dcdf3ae
--- /dev/null
+++ b/development/validateRefactorHelper.py
@@ -0,0 +1,197 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""A helper script for validateRefactor.sh. Should generally not be used directly.
+
+Can be used directly if validateRefactor.sh has already created the out-old & out-new dirs.
+In such a case, it can be run to compare those directories without regenerating them.
+This is generally only useful when updating baselines or iterating on this script itself.
+Takes baseline names as CLI arguments, which may be passed through from validateRefactor.sh.
+
+Typical usage example:
+
+ python validateRefactorHelper.py agpKmp
+"""
+import itertools
+import os
+import shutil
+import subprocess
+import sys
+
+# noto-emoji-compat `bundleinside`s an externally-built with-timestamps jar.
+# classes.jar is compared using `diffuse` instead of unzipping and diffing class files.
+bannedJars = ["-x", "noto-emoji-compat-java.jar", "-x", "classes.jar"]
+# java and json aren"t for unzipping, but the poor exclude-everything-but-jars regex doesn't
+# exclude them. Same for exclude-non-klib and .kt/.knm
+areNotZips = ["-x", r"**\.java", "-x", r"**\.json", "-x", r"**\.kt", "-x", r"**\.knm"]
+# keeps making my regexes fall over :(
+hasNoExtension = ["-x", "manifest", "-x", "module"]
+doNotUnzip = bannedJars + areNotZips + hasNoExtension
+
+def diff(excludes):
+ return popenAndReturn(["diff", "-r", "../../out-old/dist/", "../../out-new/dist/"] + excludes)
+
+def popenAndReturn(args):
+ return subprocess.Popen(args, stdout=subprocess.PIPE).stdout.read().decode("utf-8").split("\n")
+
+# Finds and unzips all files with old/new diff that _do not_ match the argument regex.
+def findFilesMatchingWithDiffAndUnzip(regexThatMatchesEverythingElse):
+ # Exclude all things that are *not* the desired zip type
+ # (because diff doesn"t have an --include, only --exclude).
+ zipsWithDiffs = diff(["-q", "-x", regexThatMatchesEverythingElse] + doNotUnzip)
+ # Take only changed files, not new/deleted ones (the diff there is obvious)
+ zipsWithDiffs = filter(lambda s: s.startswith("Files"), zipsWithDiffs)
+ zipsWithDiffs = map(lambda s: s.split()[1:4:2], zipsWithDiffs)
+ zipsWithDiffs = list(itertools.chain.from_iterable(zipsWithDiffs)) # flatten
+ # And unzip them
+ for filename in zipsWithDiffs:
+ print("unzipping " + filename)
+ # if os.path.exists(filename+".unzipped/"): os.rmdir(filename+".unzipped/")
+ shutil.rmtree(filename+".unzipped/")
+ subprocess.Popen(["unzip", "-qq", "-o", filename, "-d", filename+".unzipped/"])
+
+diffusePath = "../../prebuilts/build-tools/diffuse-0.3.0/bin/diffuse"
+
+def compareWithDiffuse(listOfJars):
+ for jarPath in list(filter(None, listOfJars)):
+ print("jarpath: " + jarPath)
+ newJarPath = jarPath.replace("out-old", "out-new")
+ print(popenAndReturn([diffusePath, "diff", "--jar", jarPath, newJarPath]))
+
+# We might care to know whether .sha1 or .md5 files have changed, but changes in those files will
+# always be accompanied by more meaningful changes in other files, so we don"t need to show changes
+# in .sha1 or .md5 files, or in .module files showing the hashes of other files, or config names.
+excludedHashes = ["-x", "*.md5*", "-x", "*.sha**", "-I", " \"md5\".*", \
+ "-I", " \"sha.*", "-I", " \"size\".*", "-I", " \"name\".*"]
+# Don"t care about maven-metadata files because they have timestamps in them.
+excludedFiles = ["-x", "*maven-metadata.xml**", "-x", r"**\.knm"] # temporarily ignore knms
+# Also, ignore files that we already unzipped
+excludedZips = ["-x", "*.zip", "-x", "*.jar", "-x", "*.aar", "-x", "*.apk", "-x", "*.klib"]
+
+# These are baselined changes that we understand and know are no-ops in refactors
+# "Unskippable" changes are multi-line and can't be skipped in `diff`, so post-process
+baselinedChangesForAgpKmp = [
+ # these are new attributes being added
+ """ "org.gradle.libraryelements": "aar",""",
+ """ "org.gradle.jvm.environment": "android",""",
+ """ "org.gradle.jvm.environment": "non-jvm",""",
+ """ "org.gradle.jvm.environment": "standard-jvm",""",
+ # this attribute swap occurs alongside the above new attributes added.
+ # https://chat.google.com/room/AAAAW8qmCIs/4phaNn_gsrc
+ """ "org.jetbrains.kotlin.platform.type": "androidJvm\"""",
+ """ "org.jetbrains.kotlin.platform.type": "jvm\"""",
+ # name-only change; nothing resolves based on names
+ """ "name": "releaseApiElements-published",""",
+ """ "name": "androidApiElements-published",""",
+ """ <pre>actual typealias""", # open bug in dackka b/339221337
+ # we are switching from our KMP sourcejars solution to the upstream one
+ """ "org.gradle.docstype": "fake-sources",""",
+ """ "org.gradle.docstype": "sources",""",
+]
+unskippableBaselinedChangesForAgpKmp = [
+ """
+< },
+< "excludes": [
+< {
+< "group": "org.jetbrains.kotlin",
+< "module": "kotlin-stdlib-common"
+< },
+< {
+< "group": "org.jetbrains.kotlin",
+< "module": "kotlin-test-common"
+< },
+< {
+< "group": "org.jetbrains.kotlin",
+< "module": "kotlin-test-annotations-common"
+< }
+< ]
+---
+> }
+""",
+"""
+< <exclusions>
+< <exclusion>
+< <groupId>org.jetbrains.kotlin</groupId>
+< <artifactId>kotlin-stdlib-common</artifactId>
+< </exclusion>
+< <exclusion>
+< <groupId>org.jetbrains.kotlin</groupId>
+< <artifactId>kotlin-test-common</artifactId>
+< </exclusion>
+< <exclusion>
+< <groupId>org.jetbrains.kotlin</groupId>
+< <artifactId>kotlin-test-annotations-common</artifactId>
+< </exclusion>
+< </exclusions>
+"""
+]
+
+baselinedChanges = []
+unskippableBaselinedChanges = []
+arguments = sys.argv[1:]
+if "agpKmp" in arguments:
+ arguments.remove("agpKmp")
+ print("IGNORING DIFF FOR agpKmp")
+ baselinedChanges += baselinedChangesForAgpKmp
+ unskippableBaselinedChanges += unskippableBaselinedChangesForAgpKmp
+if arguments:
+ print("invalid argument(s) for validateRefactorHelper: " + ", ".join(arguments))
+ print("currently recognized arguments: agpKmp")
+ exit()
+
+# interleave "-I" to tell diffutils to 'I'gnore the baselined lines
+baselinedChanges = list(itertools.chain.from_iterable(zip(["-I"]*99, baselinedChanges)))
+
+# post-process the diff output to remove multi-line changes that can't be excluded in `diff` itself
+def filterOutUnskippableBaselinedChanges(inputString):
+ result = inputString
+ for toRemove in unskippableBaselinedChanges:
+ i = result.find(toRemove)
+ while (i != -1):
+ j = result.rfind("\n", 0, i-2) # also find and remove previous line e.g. 82,96c70
+ result = result[:j+1] + result[i+len(toRemove):]
+ i = result.find(toRemove)
+ #remove all "diff -r ..." header lines that no longer have content due to baselining
+ result = result.split("\n")
+ nRemoved = 0
+ for i in range(len(result)): # check for consecutive `diff -r` lines: the first has no content
+ if not result[i-nRemoved].startswith("diff -r "): continue
+ if not result[i+1-nRemoved].startswith("diff -r "): continue
+ del result[i]
+ nRemoved+=1
+ if not result[-1]: del result[-1] # remove possible ending blank line
+ if result[-1].startswith("diff -r "): del result[-1] # terminal `diff -r` line: has no content
+ return "\n".join(result)
+
+# print(baselinedChanges)
+
+# Find all zip files with a diff, e.g. the tip-of-tree-repository file, and maybe the docs zip
+# findFilesMatchingWithDiffAndUnzip(r"**\.[^z][a-z]*")
+# Find all aar and apk files with a diff. The proper regex would be `.*\..*[^akpr]+.*`, but it
+# doesn"t work in difftools exclude's very limited regex syntax.
+findFilesMatchingWithDiffAndUnzip(r"**\.[^a][a-z]*")
+# Find all jars and klibs and unzip them (comes after because they could be inside aars/apks).
+findFilesMatchingWithDiffAndUnzip(r"**\.[^j][a-z]*")
+findFilesMatchingWithDiffAndUnzip(r"**\.[^k][a-z]*")
+# now find all diffs in classes.jars
+classesJarsWithDiffs = popenAndReturn(["find", "../../out-old/dist/", "-name", "classes.jar"])
+print("classes.jar s: " + str(classesJarsWithDiffs))
+compareWithDiffuse(classesJarsWithDiffs)
+# Now find all diffs in non-zipped files
+finalExcludes = excludedHashes + excludedFiles + excludedZips + baselinedChanges
+finalDiff = "\n".join(diff(finalExcludes))
+finalDiff = filterOutUnskippableBaselinedChanges(finalDiff)
+print(finalDiff)
+
diff --git a/ink/ink-brush/build.gradle b/ink/ink-brush/build.gradle
index 3beaaaa..2d7bafe 100644
--- a/ink/ink-brush/build.gradle
+++ b/ink/ink-brush/build.gradle
@@ -27,12 +27,20 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
androidXMultiplatform {
- android()
jvm()
+ androidLibrary {
+ namespace = "androidx.ink.brush"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ compileSdk = 35
+ aarMetadata.minCompileSdk = 35
+ }
defaultPlatform(PlatformIdentifier.JVM)
@@ -78,11 +86,6 @@
}
}
-android {
- compileSdk 35
- namespace = "androidx.ink.brush"
-}
-
androidx {
name = "Ink Brush"
type = LibraryType.PUBLISHED_LIBRARY
diff --git a/ink/ink-brush/lint-baseline.xml b/ink/ink-brush/lint-baseline.xml
new file mode 100644
index 0000000..73ba869
--- /dev/null
+++ b/ink/ink-brush/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.7.0-alpha02" type="baseline" client="gradle" dependencies="false" name="AGP (8.7.0-alpha02)" variant="all" version="8.7.0-alpha02">
+
+ <issue
+ id="Range"
+ message="Expected length ≥ 1 (was 0)"
+ errorLine1=" Rgb("", FloatArray(6), WhitePoint(0f, 0f), sIdentity, sIdentity, 0.0f, 1.0f)"
+ errorLine2=" ~~">
+ <location
+ file="src/jvmAndroidTest/kotlin/androidx/ink/brush/color/ColorSpaceTest.kt"/>
+ </issue>
+
+</issues>
diff --git a/libraryversions.toml b/libraryversions.toml
index efc3ca3..975bd7d 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -168,7 +168,7 @@
WEAR_WATCHFACE = "1.3.0-alpha03"
WEBKIT = "1.12.0-beta01"
# Adding a comment to prevent merge conflicts for Window artifact
-WINDOW = "1.4.0-alpha01"
+WINDOW = "1.4.0-alpha02"
WINDOW_EXTENSIONS = "1.4.0-beta01"
WINDOW_EXTENSIONS_CORE = "1.1.0-alpha01"
WINDOW_SIDECAR = "1.0.0-rc01"
diff --git a/playground-common/playground-plugin/build.gradle b/playground-common/playground-plugin/build.gradle
index c5b8bb8..8081016 100644
--- a/playground-common/playground-plugin/build.gradle
+++ b/playground-common/playground-plugin/build.gradle
@@ -21,7 +21,7 @@
dependencies {
implementation(project(":shared"))
- implementation("com.gradle:develocity-gradle-plugin:3.17.2")
+ implementation("com.gradle:develocity-gradle-plugin:3.18")
implementation("com.gradle:common-custom-user-data-gradle-plugin:2.0.1")
implementation("supportBuildSrc:private")
implementation("supportBuildSrc:public")
diff --git a/room/integration-tests/multiplatformtestapp/build.gradle b/room/integration-tests/multiplatformtestapp/build.gradle
index 95b2df0..4ba2f01 100644
--- a/room/integration-tests/multiplatformtestapp/build.gradle
+++ b/room/integration-tests/multiplatformtestapp/build.gradle
@@ -39,8 +39,10 @@
implementation(libs.kotlinStdlib)
implementation(project(":room:room-runtime"))
implementation(project(":room:room-testing"))
+ implementation(project(":room:room-paging"))
implementation(project(":sqlite:sqlite-bundled"))
implementation(project(":kruth:kruth"))
+ implementation(project(":paging:paging-common"))
implementation(libs.kotlinTest)
implementation(libs.kotlinCoroutinesTest)
}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
index 78e59dc..760c416 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
@@ -18,6 +18,8 @@
import androidx.kruth.assertThat
import androidx.kruth.assertThrows
+import androidx.paging.PagingSource
+import androidx.paging.PagingSource.LoadResult
import androidx.room.RoomRawQuery
import androidx.room.execSQL
import androidx.room.immediateTransaction
@@ -503,4 +505,67 @@
.hasMessageThat()
.contains("Only bind*() calls are allowed")
}
+
+ @Test
+ fun simplePagingQuery() = runTest {
+ val entity1 = SampleEntity(1, 1)
+ val entity2 = SampleEntity(2, 2)
+ val sampleEntities = listOf(entity1, entity2)
+ val dao = db.dao()
+
+ dao.insertSampleEntityList(sampleEntities)
+ val pagingSource = dao.getAllIds()
+
+ val onlyLoadFirst =
+ pagingSource.load(
+ PagingSource.LoadParams.Refresh(
+ key = null,
+ loadSize = 1,
+ placeholdersEnabled = true
+ )
+ ) as LoadResult.Page
+ assertThat(onlyLoadFirst.data).containsExactly(entity1)
+
+ val loadAll =
+ pagingSource.load(
+ PagingSource.LoadParams.Refresh(
+ key = null,
+ loadSize = 2,
+ placeholdersEnabled = true
+ )
+ ) as LoadResult.Page
+ assertThat(loadAll.data).containsExactlyElementsIn(sampleEntities)
+ }
+
+ @Test
+ fun pagingQueryWithParams() = runTest {
+ val entity1 = SampleEntity(1, 1)
+ val entity2 = SampleEntity(2, 2)
+ val entity3 = SampleEntity(3, 3)
+ val sampleEntities = listOf(entity1, entity2, entity3)
+ val dao = db.dao()
+
+ dao.insertSampleEntityList(sampleEntities)
+ val pagingSource = dao.getAllIdsWithArgs(1)
+
+ val onlyLoadFirst =
+ pagingSource.load(
+ PagingSource.LoadParams.Refresh(
+ key = null,
+ loadSize = 1,
+ placeholdersEnabled = true
+ )
+ ) as LoadResult.Page
+ assertThat(onlyLoadFirst.data).containsExactly(entity2)
+
+ val loadAll =
+ pagingSource.load(
+ PagingSource.LoadParams.Refresh(
+ key = null,
+ loadSize = 2,
+ placeholdersEnabled = true
+ )
+ ) as LoadResult.Page
+ assertThat(loadAll.data).containsExactlyElementsIn(listOf(entity2, entity3))
+ }
}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
index e511fc3..a2a3a6b 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
@@ -203,6 +203,12 @@
@Query("SELECT * FROM StringSampleEntity1")
suspend fun getSampleManyToMany(): SampleManyAndMany
+ @Query("SELECT * FROM SampleEntity")
+ fun getAllIds(): androidx.paging.PagingSource<Int, SampleEntity>
+
+ @Query("SELECT * FROM SampleEntity WHERE pk > :gt ORDER BY pk ASC")
+ fun getAllIdsWithArgs(gt: Long): androidx.paging.PagingSource<Int, SampleEntity>
+
data class Sample1And2(
@Embedded val sample1: SampleEntity,
@Relation(parentColumn = "pk", entityColumn = "pk2") val sample2: SampleEntity2
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
index c73de92..832e27b 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
@@ -337,7 +337,8 @@
RxJava3TypeNames.COMPLETABLE,
GuavaUtilConcurrentTypeNames.LISTENABLE_FUTURE,
KotlinTypeNames.FLOW,
- ReactiveStreamsTypeNames.PUBLISHER
+ ReactiveStreamsTypeNames.PUBLISHER,
+ PagingTypeNames.PAGING_SOURCE
)
fun XTypeName.defaultValue(): String {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/MultiTypedPagingSourceQueryResultBinderProvider.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/MultiTypedPagingSourceQueryResultBinderProvider.kt
index edd1307..384b238 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/MultiTypedPagingSourceQueryResultBinderProvider.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/MultiTypedPagingSourceQueryResultBinderProvider.kt
@@ -21,6 +21,7 @@
import androidx.room.compiler.processing.XRawType
import androidx.room.compiler.processing.XType
import androidx.room.ext.CommonTypeNames
+import androidx.room.ext.PagingTypeNames
import androidx.room.parser.ParsedQuery
import androidx.room.processor.Context
import androidx.room.processor.ProcessorErrors
@@ -33,7 +34,7 @@
class MultiTypedPagingSourceQueryResultBinderProvider(
private val context: Context,
private val roomPagingClassName: XClassName,
- pagingSourceTypeName: XClassName,
+ private val pagingSourceTypeName: XClassName,
) : QueryResultBinderProvider {
private val pagingSourceType: XRawType? by lazy {
@@ -60,7 +61,8 @@
return MultiTypedPagingSourceQueryResultBinder(
listAdapter = listAdapter,
tableNames = tableNames,
- className = roomPagingClassName
+ className = roomPagingClassName,
+ isBasePagingSource = pagingSourceTypeName == PagingTypeNames.PAGING_SOURCE
)
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
index 41c114e..8eea718 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
@@ -91,7 +91,7 @@
override fun convertAndReturn(
sqlQueryVar: String,
dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
@@ -128,7 +128,7 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(scope, statementVar)
+ bindStatement?.invoke(scope, statementVar)
val outVar = scope.getTmpVar("_result")
adapter?.convert(outVar, statementVar, scope)
addStatement("$returnPrefix%L", outVar)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
index 2a82c10..e9ba2ce 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
@@ -158,7 +158,7 @@
override fun convertAndReturn(
sqlQueryVar: String,
dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
@@ -194,7 +194,7 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(scope, statementVar)
+ bindStatement?.invoke(scope, statementVar)
val outVar = scope.getTmpVar("_result")
adapter?.convert(outVar, statementVar, scope)
addStatement("$returnPrefix%L", outVar)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt
index 647d801..26826b7 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt
@@ -95,7 +95,7 @@
override fun convertAndReturn(
sqlQueryVar: String,
dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
@@ -130,7 +130,7 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(scope, statementVar)
+ bindStatement?.invoke(scope, statementVar)
val outVar = scope.getTmpVar("_result")
adapter?.convert(outVar, statementVar, scope)
addStatement("$returnPrefix%L", outVar)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
index 5f1e385..9205913 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
@@ -85,7 +85,7 @@
override fun convertAndReturn(
sqlQueryVar: String,
dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
@@ -120,7 +120,7 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(scope, statementVar)
+ bindStatement?.invoke(scope, statementVar)
val outVar = scope.getTmpVar("_result")
adapter?.convert(outVar, statementVar, scope)
addStatement("$returnPrefix%L", outVar)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt
index e4898ee..683be46 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt
@@ -93,7 +93,7 @@
override fun convertAndReturn(
sqlQueryVar: String,
dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
@@ -139,7 +139,7 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(scope, statementVar)
+ bindStatement?.invoke(scope, statementVar)
val outVar = scope.getTmpVar("_result")
adapter?.convert(outVar, statementVar, scope)
addStatement("$returnPrefix%L", outVar)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
index 8b3f9a2..055401a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
@@ -16,15 +16,21 @@
package androidx.room.solver.query.result
+import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.VisibilityModifier
import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XFunSpec
import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.addStatement
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.ext.AndroidTypeNames.CURSOR
+import androidx.room.ext.AndroidTypeNames
import androidx.room.ext.CommonTypeNames
+import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.KotlinTypeNames
+import androidx.room.ext.RoomTypeNames.RAW_QUERY
+import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.solver.CodeGenScope
/**
@@ -35,13 +41,16 @@
class MultiTypedPagingSourceQueryResultBinder(
private val listAdapter: ListQueryResultAdapter?,
private val tableNames: Set<String>,
- className: XClassName
+ className: XClassName,
+ val isBasePagingSource: Boolean
) : QueryResultBinder(listAdapter) {
private val itemTypeName: XTypeName =
listAdapter?.rowAdapters?.firstOrNull()?.out?.asTypeName() ?: XTypeName.ANY_OBJECT
private val pagingSourceTypeName: XTypeName = className.parametrizedBy(itemTypeName)
+ override fun isMigratedToDriver(): Boolean = isBasePagingSource
+
override fun convertAndReturn(
roomSQLiteQueryVar: String,
canReleaseQuery: Boolean,
@@ -61,14 +70,134 @@
)
.apply {
superclass(pagingSourceTypeName)
- addFunction(createConvertRowsMethod(scope))
+ addFunction(
+ createConvertRowsMethod(
+ scope = scope,
+ stmtParamName = "cursor",
+ stmtParamTypeName = AndroidTypeNames.CURSOR,
+ rawQueryParamName = null
+ )
+ )
}
.build()
addStatement("return %L", pagingSourceSpec)
}
}
- private fun createConvertRowsMethod(scope: CodeGenScope): XFunSpec {
+ override fun convertAndReturn(
+ sqlQueryVar: String,
+ dbProperty: XPropertySpec,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
+ returnTypeName: XTypeName,
+ inTransaction: Boolean,
+ scope: CodeGenScope
+ ) {
+ check(isBasePagingSource) {
+ "This version of `convertAndReturn` should only be called when the binder is for the " +
+ "base PagingSource. "
+ }
+ val rawQueryVarName = scope.getTmpVar("_rawQuery")
+ val stmtVarName = scope.getTmpVar("_stmt")
+
+ when (scope.language) {
+ CodeLanguage.JAVA -> {
+ val assignExpr =
+ if (bindStatement != null) {
+ XCodeBlock.ofNewInstance(
+ language = scope.language,
+ typeName = RAW_QUERY,
+ "%L, %L",
+ sqlQueryVar,
+ Function1TypeSpec(
+ language = scope.language,
+ parameterTypeName = SQLiteDriverTypeNames.STATEMENT,
+ parameterName = stmtVarName,
+ returnTypeName = KotlinTypeNames.UNIT
+ ) {
+ val functionScope = scope.fork()
+ functionScope.builder
+ .apply { bindStatement.invoke(functionScope, stmtVarName) }
+ .build()
+ addCode(functionScope.generate())
+ addStatement("return %T.INSTANCE", KotlinTypeNames.UNIT)
+ }
+ )
+ } else {
+ XCodeBlock.ofNewInstance(
+ language = scope.language,
+ typeName = RAW_QUERY,
+ "%L",
+ sqlQueryVar
+ )
+ }
+ scope.builder.addLocalVariable(
+ name = rawQueryVarName,
+ typeName = RAW_QUERY,
+ assignExpr = assignExpr
+ )
+ }
+ CodeLanguage.KOTLIN ->
+ scope.builder.apply {
+ if (bindStatement != null) {
+ beginControlFlow(
+ "val %L: %T = %T(%N) { %L ->",
+ rawQueryVarName,
+ RAW_QUERY,
+ RAW_QUERY,
+ sqlQueryVar,
+ stmtVarName
+ )
+ bindStatement.invoke(scope, stmtVarName)
+ endControlFlow()
+ } else {
+ addLocalVariable(
+ name = rawQueryVarName,
+ typeName = RAW_QUERY,
+ assignExpr =
+ XCodeBlock.ofNewInstance(
+ language = scope.language,
+ typeName = RAW_QUERY,
+ argsFormat = "%N",
+ sqlQueryVar
+ )
+ )
+ }
+ }
+ }
+
+ scope.builder.apply {
+ val tableNamesList = tableNames.joinToString(", ") { "\"$it\"" }
+ val statementParamName = "statement"
+ val pagingSourceSpec =
+ XTypeSpec.anonymousClassBuilder(
+ language = language,
+ argsFormat = "%L, %N, %L",
+ rawQueryVarName,
+ dbProperty,
+ tableNamesList
+ )
+ .apply {
+ superclass(pagingSourceTypeName)
+ addFunction(
+ createConvertRowsMethod(
+ scope = scope,
+ stmtParamName = statementParamName,
+ stmtParamTypeName = SQLiteDriverTypeNames.STATEMENT,
+ rawQueryParamName = rawQueryVarName
+ )
+ )
+ }
+ .build()
+ addStatement("return %L", pagingSourceSpec)
+ }
+ }
+
+ private fun createConvertRowsMethod(
+ scope: CodeGenScope,
+ stmtParamName: String,
+ stmtParamTypeName: XTypeName,
+ rawQueryParamName: String?
+ ): XFunSpec {
return XFunSpec.builder(
language = scope.language,
name = "convertRows",
@@ -76,12 +205,24 @@
isOverride = true
)
.apply {
- val cursorParamName = "cursor"
returns(CommonTypeNames.LIST.parametrizedBy(itemTypeName))
- addParameter(typeName = CURSOR, name = cursorParamName)
+ addParameter(typeName = stmtParamTypeName, name = stmtParamName)
+ if (stmtParamTypeName == SQLiteDriverTypeNames.STATEMENT) {
+ // The SQLiteStatement version requires a second parameter for backwards
+ // compatibility for delegating to CursorSQLiteStatement.
+ addParameter(typeName = XTypeName.PRIMITIVE_INT, name = "itemCount")
+ }
val resultVar = scope.getTmpVar("_result")
val rowsScope = scope.fork()
- listAdapter?.convert(resultVar, cursorParamName, rowsScope)
+ if (stmtParamTypeName == SQLiteDriverTypeNames.STATEMENT) {
+ checkNotNull(rawQueryParamName)
+ addStatement(
+ "%L.getBindingFunction().invoke(%L)",
+ rawQueryParamName,
+ stmtParamName,
+ )
+ }
+ listAdapter?.convert(resultVar, stmtParamName, rowsScope)
addCode(rowsScope.generate())
addStatement("return %L", resultVar)
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
index b39c993..286394d 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
@@ -51,7 +51,7 @@
open fun convertAndReturn(
sqlQueryVar: String,
dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxLambdaQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxLambdaQueryResultBinder.kt
index 10973f37..57a43cb 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxLambdaQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxLambdaQueryResultBinder.kt
@@ -171,7 +171,7 @@
override fun convertAndReturn(
sqlQueryVar: String,
dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
@@ -206,7 +206,7 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(scope, statementVar)
+ bindStatement?.invoke(scope, statementVar)
val outVar = scope.getTmpVar("_result")
adapter?.convert(outVar, statementVar, scope)
addStatement("$returnPrefix%L", outVar)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt
index bc8877e..716fc2c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt
@@ -90,7 +90,7 @@
override fun convertAndReturn(
sqlQueryVar: String,
dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
+ bindStatement: (CodeGenScope.(String) -> Unit)?,
returnTypeName: XTypeName,
inTransaction: Boolean,
scope: CodeGenScope
@@ -134,7 +134,7 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(scope, statementVar)
+ bindStatement?.invoke(scope, statementVar)
val outVar = scope.getTmpVar("_result")
adapter?.convert(outVar, statementVar, scope)
addStatement("$returnPrefix%L", outVar)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
index 8ff5d2d..45a0481 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
@@ -685,7 +685,12 @@
method.queryResultBinder.convertAndReturn(
sqlQueryVar = sqlVar,
dbProperty = dbProperty,
- bindStatement = { stmtVar -> queryWriter.bindArgs(stmtVar, listSizeArgs, this) },
+ bindStatement =
+ if (queryWriter.parameters.isNotEmpty()) {
+ { stmtVar -> queryWriter.bindArgs(stmtVar, listSizeArgs, this) }
+ } else {
+ null
+ },
returnTypeName = method.returnType.asTypeName(),
inTransaction = method.inTransaction,
scope = scope
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
index a4afa61..38303f3 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
@@ -784,6 +784,9 @@
@Query("SELECT pk FROM MyEntity")
abstract fun getAllIds(): androidx.paging.PagingSource<Int, MyEntity>
+ @Query("SELECT * FROM MyEntity WHERE pk > :gt ORDER BY pk ASC")
+ abstract fun getAllIdsWithArgs(gt: Long): androidx.paging.PagingSource<Int, MyEntity>
+
@Query("SELECT pk FROM MyEntity")
abstract fun getAllIdsRx2(): androidx.paging.rxjava2.RxPagingSource<Int, MyEntity>
diff --git a/room/room-compiler/src/test/test-data/common/input/LimitOffsetPagingSource.kt b/room/room-compiler/src/test/test-data/common/input/LimitOffsetPagingSource.kt
index 7b72548..a9921a2 100644
--- a/room/room-compiler/src/test/test-data/common/input/LimitOffsetPagingSource.kt
+++ b/room/room-compiler/src/test/test-data/common/input/LimitOffsetPagingSource.kt
@@ -15,14 +15,14 @@
*/
package androidx.room.paging
-import android.database.Cursor
import androidx.paging.PagingState
import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomRawQuery
+import androidx.sqlite.SQLiteStatement
@Suppress("UNUSED_PARAMETER")
abstract class LimitOffsetPagingSource<T : Any>(
- private val sourceQuery: RoomSQLiteQuery,
+ private val sourceQuery: RoomRawQuery,
private val db: RoomDatabase,
vararg tables: String
) : androidx.paging.PagingSource<Int, T>() {
@@ -33,5 +33,5 @@
override public suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
return LoadResult.Invalid()
}
- protected abstract fun convertRows(cursor: Cursor): List<T>
+ protected abstract fun convertRows(statement: SQLiteStatement, itemCount: Int): List<T>
}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt
index 386d206..eab9d14 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt
@@ -2,12 +2,16 @@
import androidx.paging.ListenableFuturePagingSource
import androidx.paging.PagingSource
import androidx.room.RoomDatabase
+import androidx.room.RoomRawQuery
import androidx.room.RoomSQLiteQuery
import androidx.room.RoomSQLiteQuery.Companion.acquire
import androidx.room.paging.LimitOffsetPagingSource
import androidx.room.paging.guava.LimitOffsetListenableFuturePagingSource
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.sqlite.SQLiteStatement
import javax.`annotation`.processing.Generated
import kotlin.Int
+import kotlin.Long
import kotlin.String
import kotlin.Suppress
import kotlin.collections.List
@@ -31,15 +35,41 @@
public override fun getAllIds(): PagingSource<Int, MyEntity> {
val _sql: String = "SELECT pk FROM MyEntity"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- return object : LimitOffsetPagingSource<MyEntity>(_statement, __db, "MyEntity") {
- protected override fun convertRows(cursor: Cursor): List<MyEntity> {
+ val _rawQuery: RoomRawQuery = RoomRawQuery(_sql)
+ return object : LimitOffsetPagingSource<MyEntity>(_rawQuery, __db, "MyEntity") {
+ protected override fun convertRows(statement: SQLiteStatement, itemCount: Int):
+ List<MyEntity> {
+ _rawQuery.getBindingFunction().invoke(statement)
val _cursorIndexOfPk: Int = 0
val _result: MutableList<MyEntity> = mutableListOf()
- while (cursor.moveToNext()) {
+ while (statement.step()) {
val _item: MyEntity
val _tmpPk: Int
- _tmpPk = cursor.getInt(_cursorIndexOfPk)
+ _tmpPk = statement.getLong(_cursorIndexOfPk).toInt()
+ _item = MyEntity(_tmpPk)
+ _result.add(_item)
+ }
+ return _result
+ }
+ }
+ }
+
+ public override fun getAllIdsWithArgs(gt: Long): PagingSource<Int, MyEntity> {
+ val _sql: String = "SELECT * FROM MyEntity WHERE pk > ? ORDER BY pk ASC"
+ val _rawQuery: RoomRawQuery = RoomRawQuery(_sql) { _stmt ->
+ var _argIndex: Int = 1
+ _stmt.bindLong(_argIndex, gt)
+ }
+ return object : LimitOffsetPagingSource<MyEntity>(_rawQuery, __db, "MyEntity") {
+ protected override fun convertRows(statement: SQLiteStatement, itemCount: Int):
+ List<MyEntity> {
+ _rawQuery.getBindingFunction().invoke(statement)
+ val _cursorIndexOfPk: Int = getColumnIndexOrThrow(statement, "pk")
+ val _result: MutableList<MyEntity> = mutableListOf()
+ while (statement.step()) {
+ val _item: MyEntity
+ val _tmpPk: Int
+ _tmpPk = statement.getLong(_cursorIndexOfPk).toInt()
_item = MyEntity(_tmpPk)
_result.add(_item)
}
diff --git a/room/room-paging/build.gradle b/room/room-paging/build.gradle
index 8770d1d..c84fe70 100644
--- a/room/room-paging/build.gradle
+++ b/room/room-paging/build.gradle
@@ -17,7 +17,6 @@
import androidx.build.PlatformIdentifier
import androidx.build.LibraryType
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
-import org.jetbrains.kotlin.konan.target.Family
plugins {
id("AndroidXPlugin")
diff --git a/settings.gradle b/settings.gradle
index c02141b..6dd80d4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -26,7 +26,7 @@
dependencies {
// upgrade protobuf to be compatible with AGP
classpath("com.google.protobuf:protobuf-java:3.22.3")
- classpath("com.gradle:develocity-gradle-plugin:3.17.2")
+ classpath("com.gradle:develocity-gradle-plugin:3.18")
classpath("com.gradle:common-custom-user-data-gradle-plugin:2.0.1")
classpath("androidx.build.gradle.gcpbuildcache:gcpbuildcache:1.0.0-beta10")
classpath("com.android.settings:com.android.settings.gradle.plugin:8.7.0-alpha02")
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
index f76e662..7c4af90 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
@@ -743,13 +743,16 @@
open = true)
public @interface FontFamilyName {}
- /** Font family name that uses Roboto font. Supported in renderers supporting 1.4. */
+ /**
+ * Font family name that uses Roboto font. Supported in renderers supporting 1.4, but the
+ * actual availability of this font is dependent on the devices.
+ */
@RequiresSchemaVersion(major = 1, minor = 400)
public static final String ROBOTO_FONT = "roboto";
/**
- * Font family name that uses Roboto Flex variable font. Supported in renderers
- * supporting 1.4.
+ * Font family name that uses Roboto Flex variable font. Supported in renderers supporting
+ * 1.4, but the actual availability of this font is dependent on the devices.
*/
@RequiresSchemaVersion(major = 1, minor = 400)
public static final String ROBOTO_FLEX_FONT = "roboto-flex";
diff --git a/window/window/build.gradle b/window/window/build.gradle
index 5707f86..004b04a 100644
--- a/window/window/build.gradle
+++ b/window/window/build.gradle
@@ -54,14 +54,14 @@
implementation("androidx.core:core:1.8.0")
def extensions_core_version = "androidx.window.extensions.core:core:1.0.0"
- def extensions_version = "androidx.window.extensions:extensions:1.4.0-alpha01"
- // A compile only dependency on extnensions.core so that other libraries do not expose it
+ def extensions_version = "androidx.window.extensions:extensions:1.4.0-beta01"
+ // A compile only dependency on extensions.core so that other libraries do not expose it
// transitively.
compileOnly(extensions_core_version)
// Test implementation is required since extensions:core is on device. So it is required to
// import it in some form. For the library it will be available on device.
testImplementation(extensions_core_version)
- // A compile only dependency on extnensions.core so that other libraries do not expose it
+ // A compile only dependency on extensions.core so that other libraries do not expose it
// transitively. The androidTestCompile is added because tests are not getting the dependency
// transitively.
androidTestCompileOnly(extensions_core_version)