Merge "Reduce XElement API surface" into androidx-master-dev
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7cdf3e3..b390ac1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -52,7 +52,7 @@
We recommend cloning using blob filter to reduce checkout size:
```bash
-git clone --filter=blob:none https://github.com/YOUR_USERNAME/androidx.git
+git clone https://github.com/YOUR_USERNAME/androidx.git
```
Let’s assume that you want to make a contribution to Room. The first step is to launch Android Studio and import the Room project.
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchManagerTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchManagerTest.java
index aaa0939..a7fd8ad 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchManagerTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchManagerTest.java
@@ -442,7 +442,7 @@
// Set number of results per page is 7.
SearchResults searchResults = mDb1.query("body",
- SearchSpec.newBuilder()
+ new SearchSpec.Builder()
.setTermMatchType(SearchSpec.TERM_MATCH_TYPE_EXACT_ONLY)
.setNumPerPage(7)
.build());
@@ -980,7 +980,7 @@
AppSearchManager instance, String queryExpression, String... schemaTypes)
throws Exception {
SearchResults searchResults = instance.query(queryExpression,
- SearchSpec.newBuilder()
+ new SearchSpec.Builder()
.setSchemaTypes(schemaTypes)
.setTermMatchType(SearchSpec.TERM_MATCH_TYPE_EXACT_ONLY)
.build());
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultsTest.java
index b44f9e2..3829dc7 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultsTest.java
@@ -26,7 +26,7 @@
public class SearchResultsTest {
@Test
public void buildSearchSpecWithoutTermMatchType() {
- assertThrows(RuntimeException.class, () -> SearchSpec.newBuilder()
+ assertThrows(RuntimeException.class, () -> new SearchSpec.Builder()
.setSchemaTypes("testSchemaType")
.build());
}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
index c5ae289..17a8ef2 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
@@ -16,15 +16,13 @@
package androidx.appsearch.app;
+import android.os.Bundle;
+
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.appsearch.exceptions.IllegalSearchSpecException;
-
-import com.google.android.icing.proto.ResultSpecProto;
-import com.google.android.icing.proto.ScoringSpecProto;
-import com.google.android.icing.proto.SearchSpecProto;
-import com.google.android.icing.proto.TermMatchType;
+import androidx.core.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -38,36 +36,27 @@
// TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
public final class SearchSpec {
- private final SearchSpecProto mSearchSpecProto;
- private final ResultSpecProto mResultSpecProto;
- private final ScoringSpecProto mScoringSpecProto;
+ static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
+ static final String SCHEMA_TYPES_FIELD = "schemaType";
+ static final String NUM_PER_PAGE_FIELD = "numPerPage";
+ static final String RANKING_STRATEGY_FIELD = "rankingStrategy";
+ static final String ORDER_FILED = "order";
+ static final String NUM_TO_SNIPPET_FIELD = "numToSnippet";
+ static final String NUM_MATCHED_PER_PROPERTY_FIELD = "numMatchedPerProperty";
+ static final String MAX_SNIPPET_FIELD = "maxSnippet";
+ static final int DEFAULT_NUM_PER_PAGE = 10;
- SearchSpec(@NonNull SearchSpecProto searchSpecProto,
- @NonNull ResultSpecProto resultSpecProto, @NonNull ScoringSpecProto scoringSpecProto) {
- mSearchSpecProto = searchSpecProto;
- mResultSpecProto = resultSpecProto;
- mScoringSpecProto = scoringSpecProto;
+ private final Bundle mBundle;
+
+ SearchSpec(@NonNull Bundle bundle) {
+ Preconditions.checkNotNull(bundle);
+ mBundle = bundle;
}
- /** Creates a new {@link SearchSpec.Builder}. */
+ /** Returns the {@link Bundle} populated by this builder. */
@NonNull
- public static SearchSpec.Builder newBuilder() {
- return new SearchSpec.Builder();
- }
-
- @NonNull
- SearchSpecProto getSearchSpecProto() {
- return mSearchSpecProto;
- }
-
- @NonNull
- ResultSpecProto getResultSpecProto() {
- return mResultSpecProto;
- }
-
- @NonNull
- ScoringSpecProto getScoringSpecProto() {
- return mScoringSpecProto;
+ Bundle getBundle() {
+ return mBundle;
}
/** Term Match Type for the query. */
@@ -127,13 +116,13 @@
/** Builder for {@link SearchSpec objects}. */
public static final class Builder {
- private final SearchSpecProto.Builder mSearchSpecBuilder = SearchSpecProto.newBuilder();
- private final ResultSpecProto.Builder mResultSpecBuilder = ResultSpecProto.newBuilder();
- private final ScoringSpecProto.Builder mScoringSpecBuilder = ScoringSpecProto.newBuilder();
- private final ResultSpecProto.SnippetSpecProto.Builder mSnippetSpecBuilder =
- ResultSpecProto.SnippetSpecProto.newBuilder();
+ private final Bundle mBundle;
+ private boolean mBuilt = false;
- Builder() {
+ /** Creates a new {@link SearchSpec.Builder}. */
+ public Builder() {
+ mBundle = new Bundle();
+ mBundle.putInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
}
/**
@@ -141,13 +130,10 @@
*/
@NonNull
public Builder setTermMatchType(@TermMatchTypeCode int termMatchTypeCode) {
- TermMatchType.Code termMatchTypeCodeProto =
- TermMatchType.Code.forNumber(termMatchTypeCode);
- if (termMatchTypeCodeProto == null) {
- throw new IllegalArgumentException("Invalid term match type: "
- + termMatchTypeCode);
- }
- mSearchSpecBuilder.setTermMatchType(termMatchTypeCodeProto);
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkArgumentInRange(termMatchTypeCode, TERM_MATCH_TYPE_EXACT_ONLY,
+ TERM_MATCH_TYPE_PREFIX, "Term match type");
+ mBundle.putInt(TERM_MATCH_TYPE_FIELD, termMatchTypeCode);
return this;
}
@@ -158,29 +144,32 @@
*/
@NonNull
public Builder setSchemaTypes(@NonNull String... schemaTypes) {
- for (String schemaType : schemaTypes) {
- mSearchSpecBuilder.addSchemaTypeFilters(schemaType);
- }
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putStringArray(SCHEMA_TYPES_FIELD, schemaTypes);
return this;
}
- /** Sets the number of results per page in the returned object. */
+ /**
+ * Sets the number of results per page in the returned object.
+ * <p> The default number of results per page is 10.
+ */
@NonNull
public SearchSpec.Builder setNumPerPage(int numPerPage) {
- mResultSpecBuilder.setNumPerPage(numPerPage);
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ if (numPerPage <= 0) {
+ throw new IllegalArgumentException("Invalid number per page :" + numPerPage);
+ }
+ mBundle.putInt(NUM_PER_PAGE_FIELD, numPerPage);
return this;
}
/** Sets ranking strategy for AppSearch results.*/
@NonNull
public Builder setRankingStrategy(@RankingStrategyCode int rankingStrategy) {
- ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
- ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategy);
- if (rankingStrategyCodeProto == null) {
- throw new IllegalArgumentException("Invalid result ranking strategy: "
- + rankingStrategyCodeProto);
- }
- mScoringSpecBuilder.setRankBy(rankingStrategyCodeProto);
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkArgumentInRange(rankingStrategy, RANKING_STRATEGY_NONE,
+ RANKING_STRATEGY_CREATION_TIMESTAMP, "Result ranking strategy");
+ mBundle.putInt(RANKING_STRATEGY_FIELD, rankingStrategy);
return this;
}
@@ -191,13 +180,10 @@
*/
@NonNull
public Builder setOrder(@OrderCode int order) {
- ScoringSpecProto.Order.Code orderCodeProto =
- ScoringSpecProto.Order.Code.forNumber(order);
- if (orderCodeProto == null) {
- throw new IllegalArgumentException("Invalid result ranking order: "
- + orderCodeProto);
- }
- mScoringSpecBuilder.setOrderBy(orderCodeProto);
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkArgumentInRange(order, ORDER_DESCENDING, ORDER_ASCENDING,
+ "Result ranking order");
+ mBundle.putInt(ORDER_FILED, order);
return this;
}
@@ -209,7 +195,8 @@
*/
@NonNull
public SearchSpec.Builder setNumToSnippet(int numToSnippet) {
- mSnippetSpecBuilder.setNumToSnippet(numToSnippet);
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putInt(NUM_TO_SNIPPET_FIELD, numToSnippet);
return this;
}
@@ -221,7 +208,8 @@
*/
@NonNull
public SearchSpec.Builder setNumMatchesPerProperty(int numMatchesPerProperty) {
- mSnippetSpecBuilder.setNumMatchesPerProperty(numMatchesPerProperty);
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putInt(NUM_MATCHED_PER_PROPERTY_FIELD, numMatchesPerProperty);
return this;
}
@@ -237,7 +225,8 @@
*/
@NonNull
public SearchSpec.Builder setMaxSnippetSize(int maxSnippetSize) {
- mSnippetSpecBuilder.setMaxWindowBytes(maxSnippetSize);
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putInt(MAX_SNIPPET_FIELD, maxSnippetSize);
return this;
}
@@ -248,12 +237,12 @@
*/
@NonNull
public SearchSpec build() {
- if (mSearchSpecBuilder.getTermMatchType() == TermMatchType.Code.UNKNOWN) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ if (!mBundle.containsKey(TERM_MATCH_TYPE_FIELD)) {
throw new IllegalSearchSpecException("Missing termMatchType field.");
}
- mResultSpecBuilder.setSnippetSpec(mSnippetSpecBuilder);
- return new SearchSpec(mSearchSpecBuilder.build(), mResultSpecBuilder.build(),
- mScoringSpecBuilder.build());
+ mBuilt = true;
+ return new SearchSpec(mBundle);
}
}
}
diff --git a/appsearch/local-backend/src/main/java/androidx/appsearch/app/SearchSpecToProtoConverter.java b/appsearch/local-backend/src/main/java/androidx/appsearch/app/SearchSpecToProtoConverter.java
index 1d15bc6..82ebebe 100644
--- a/appsearch/local-backend/src/main/java/androidx/appsearch/app/SearchSpecToProtoConverter.java
+++ b/appsearch/local-backend/src/main/java/androidx/appsearch/app/SearchSpecToProtoConverter.java
@@ -16,6 +16,8 @@
package androidx.appsearch.app;
+import android.os.Bundle;
+
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.core.util.Preconditions;
@@ -23,6 +25,9 @@
import com.google.android.icing.proto.ResultSpecProto;
import com.google.android.icing.proto.ScoringSpecProto;
import com.google.android.icing.proto.SearchSpecProto;
+import com.google.android.icing.proto.TermMatchType;
+
+import java.util.Arrays;
/**
* Translates a {@link SearchSpec} into icing search protos.
@@ -36,20 +41,63 @@
@NonNull
public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) {
Preconditions.checkNotNull(spec);
- return spec.getSearchSpecProto();
+ Bundle bundle = spec.getBundle();
+ SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder();
+
+ TermMatchType.Code termMatchTypeCode = TermMatchType.Code.forNumber(
+ bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD));
+ if (termMatchTypeCode == null || termMatchTypeCode.equals(TermMatchType.Code.UNKNOWN)) {
+ throw new IllegalArgumentException("Invalid term match type: " + termMatchTypeCode);
+ }
+
+ protoBuilder.setTermMatchType(termMatchTypeCode);
+ String[] schemaTypes = bundle.getStringArray(SearchSpec.SCHEMA_TYPES_FIELD);
+ if (schemaTypes != null) {
+ protoBuilder.addAllSchemaTypeFilters(Arrays.asList(schemaTypes));
+ }
+ return protoBuilder.build();
}
/** Extracts {@link ResultSpecProto} information from a {@link SearchSpec}. */
@NonNull
public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
Preconditions.checkNotNull(spec);
- return spec.getResultSpecProto();
+ Bundle bundle = spec.getBundle();
+ return ResultSpecProto.newBuilder()
+ .setNumPerPage(bundle.getInt(
+ SearchSpec.NUM_PER_PAGE_FIELD, SearchSpec.DEFAULT_NUM_PER_PAGE))
+ .setSnippetSpec(ResultSpecProto.SnippetSpecProto.newBuilder()
+ .setNumToSnippet(bundle.getInt(SearchSpec.NUM_TO_SNIPPET_FIELD))
+ .setNumMatchesPerProperty(
+ bundle.getInt(SearchSpec.NUM_MATCHED_PER_PROPERTY_FIELD))
+ .setMaxWindowBytes(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)))
+ .build();
+
}
/** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */
@NonNull
public static ScoringSpecProto toScoringSpecProto(@NonNull SearchSpec spec) {
Preconditions.checkNotNull(spec);
- return spec.getScoringSpecProto();
+ Bundle bundle = spec.getBundle();
+ ScoringSpecProto.Builder protoBuilder = ScoringSpecProto.newBuilder();
+ ScoringSpecProto.Order.Code orderCodeProto =
+ ScoringSpecProto.Order.Code.forNumber(bundle.getInt(SearchSpec.ORDER_FILED));
+ if (orderCodeProto == null) {
+ throw new IllegalArgumentException("Invalid result ranking order: "
+ + orderCodeProto);
+ }
+ protoBuilder.setOrderBy(orderCodeProto);
+
+ ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
+ ScoringSpecProto.RankingStrategy.Code.forNumber(
+ bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD));
+ if (rankingStrategyCodeProto == null) {
+ throw new IllegalArgumentException("Invalid result ranking strategy: "
+ + rankingStrategyCodeProto);
+ }
+ protoBuilder.setRankBy(rankingStrategyCodeProto);
+
+ return protoBuilder.build();
}
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 72964f7..d8d999d 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -88,7 +88,7 @@
const val REACTIVE_STREAMS = "org.reactivestreams:reactive-streams:1.0.0"
const val RX_JAVA = "io.reactivex.rxjava2:rxjava:2.2.9"
const val RX_JAVA3 = "io.reactivex.rxjava3:rxjava:3.0.0"
-const val SKIKO_VERSION = "0.1.5"
+const val SKIKO_VERSION = "0.1.6"
const val SKIKO = "org.jetbrains.skiko:skiko-jvm:$SKIKO_VERSION"
const val SKIKO_LINUX = "org.jetbrains.skiko:skiko-jvm-runtime-linux:$SKIKO_VERSION"
const val SKIKO_MACOS = "org.jetbrains.skiko:skiko-jvm-runtime-macos:$SKIKO_VERSION"
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageSaveLocationValidator.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageSaveLocationValidator.java
index dbdd729..a7f2261 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageSaveLocationValidator.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageSaveLocationValidator.java
@@ -23,6 +23,7 @@
import androidx.annotation.NonNull;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -45,7 +46,6 @@
* @return true if the image capture result can be saved to the specified storage option,
* false otherwise.
*/
- @SuppressWarnings("ConstantConditions")
static boolean isValid(final @NonNull ImageCapture.OutputFileOptions outputFileOptions) {
if (isSaveToFile(outputFileOptions)) {
return canSaveToFile(outputFileOptions.getFile());
@@ -70,10 +70,12 @@
}
private static boolean canSaveToFile(@NonNull final File file) {
- try {
- return file.canWrite();
- } catch (SecurityException exception) {
- Logger.e(TAG, "Error while verifying if image capture file is writable", exception);
+ // Try opening a write stream to the output file. If this succeeds, the image save
+ // destination is valid. Otherwise, it's invalid.
+ try (FileOutputStream ignored = new FileOutputStream(file)) {
+ return true;
+ } catch (IOException exception) {
+ Logger.e(TAG, "Failed to open a write stream to " + file.toString(), exception);
return false;
}
}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageSaveLocationValidatorTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageSaveLocationValidatorTest.java
index 11593c5..928636d 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageSaveLocationValidatorTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageSaveLocationValidatorTest.java
@@ -62,20 +62,22 @@
}
@Test
- public void cannotSaveToReadOnlyFile() throws IOException {
- final File saveLocation = File.createTempFile("test", ".jpg");
- saveLocation.setReadOnly();
+ public void canSaveToFileInCache() {
+ final File cacheDir = ApplicationProvider.getApplicationContext().getCacheDir();
+ final String fileName = System.currentTimeMillis() + ".jpg";
+ final File saveLocation = new File(cacheDir, fileName);
saveLocation.deleteOnExit();
final ImageCapture.OutputFileOptions outputOptions =
new ImageCapture.OutputFileOptions.Builder(saveLocation).build();
- assertThat(ImageSaveLocationValidator.isValid(outputOptions)).isFalse();
+ assertThat(ImageSaveLocationValidator.isValid(outputOptions)).isTrue();
}
@Test
- public void cannotSaveToInvalidFile() throws IOException {
+ public void cannotSaveToReadOnlyFile() throws IOException {
final File saveLocation = File.createTempFile("test", ".jpg");
- saveLocation.delete();
+ saveLocation.setReadOnly();
+ saveLocation.deleteOnExit();
final ImageCapture.OutputFileOptions outputOptions =
new ImageCapture.OutputFileOptions.Builder(saveLocation).build();
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewTransformationDeviceTest.kt b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewTransformationDeviceTest.kt
index c292d8f..7605453 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewTransformationDeviceTest.kt
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewTransformationDeviceTest.kt
@@ -50,7 +50,7 @@
// 1:1 crop rect.
private val FIT_SURFACE_SIZE = Size(60, 60)
- private val FIT_CROP_RECT = Rect(0, 0, 60, 60)
+ private val MISMATCHED_CROP_RECT = Rect(0, 0, 60, 60)
private const val FLOAT_ERROR = 1e-3f
}
@@ -208,33 +208,92 @@
}
@Test
- fun fitStart_surfacePositionIsStart() {
- assertFitTranslationX(PreviewView.ScaleType.FIT_START, LayoutDirection.LTR, 0f)
+ fun mismatchedCropRect_fitStart() {
+ assertForMismatchedCropRect(
+ PreviewView.ScaleType.FIT_START,
+ LayoutDirection.LTR,
+ PREVIEW_VIEW_SIZE.height.toFloat() / MISMATCHED_CROP_RECT.height(),
+ 0f,
+ 0f
+ )
}
@Test
- fun fitCenter_surfacePositionIsCenter() {
- assertFitTranslationX(PreviewView.ScaleType.FIT_CENTER, LayoutDirection.LTR, 100f)
+ fun mismatchedCropRect_fitCenter() {
+ assertForMismatchedCropRect(
+ PreviewView.ScaleType.FIT_CENTER,
+ LayoutDirection.LTR,
+ PREVIEW_VIEW_SIZE.height.toFloat() / MISMATCHED_CROP_RECT.height(),
+ 100f,
+ 0f
+ )
}
@Test
- fun fitEnd_surfacePositionIsEnd() {
- assertFitTranslationX(PreviewView.ScaleType.FIT_END, LayoutDirection.LTR, 200f)
+ fun mismatchedCropRect_fitEnd() {
+ assertForMismatchedCropRect(
+ PreviewView.ScaleType.FIT_END,
+ LayoutDirection.LTR,
+ PREVIEW_VIEW_SIZE.height.toFloat() / MISMATCHED_CROP_RECT.height(),
+ 200f,
+ 0f
+ )
}
@Test
- fun fitStartWithRTL_behavesLikeFitEndWithLTR() {
- assertFitTranslationX(PreviewView.ScaleType.FIT_START, LayoutDirection.RTL, 200f)
+ fun mismatchedCropRect_fillStart() {
+ assertForMismatchedCropRect(
+ PreviewView.ScaleType.FILL_START,
+ LayoutDirection.LTR,
+ PREVIEW_VIEW_SIZE.width.toFloat() / MISMATCHED_CROP_RECT.width(),
+ 0f,
+ 0f
+ )
}
- private fun assertFitTranslationX(
+ @Test
+ fun mismatchedCropRect_fillCenter() {
+ assertForMismatchedCropRect(
+ PreviewView.ScaleType.FILL_CENTER,
+ LayoutDirection.LTR,
+ PREVIEW_VIEW_SIZE.width.toFloat() / MISMATCHED_CROP_RECT.width(),
+ 0f,
+ -100f
+ )
+ }
+
+ @Test
+ fun mismatchedCropRect_fillEnd() {
+ assertForMismatchedCropRect(
+ PreviewView.ScaleType.FILL_END,
+ LayoutDirection.LTR,
+ PREVIEW_VIEW_SIZE.width.toFloat() / MISMATCHED_CROP_RECT.width(),
+ 0f,
+ -200f
+ )
+ }
+
+ @Test
+ fun mismatchedCropRect_fitStartWithRtl_actsLikeFitEnd() {
+ assertForMismatchedCropRect(
+ PreviewView.ScaleType.FIT_START,
+ LayoutDirection.RTL,
+ PREVIEW_VIEW_SIZE.height.toFloat() / MISMATCHED_CROP_RECT.height(),
+ 200f,
+ 0f
+ )
+ }
+
+ private fun assertForMismatchedCropRect(
scaleType: PreviewView.ScaleType,
layoutDirection: Int,
- translationX: Float
+ scale: Float,
+ translationX: Float,
+ translationY: Float
) {
// Arrange.
mPreviewTransform.setTransformationInfo(
- SurfaceRequest.TransformationInfo.of(FIT_CROP_RECT, 90, Surface.ROTATION_90),
+ SurfaceRequest.TransformationInfo.of(MISMATCHED_CROP_RECT, 90, Surface.ROTATION_90),
FIT_SURFACE_SIZE
)
mPreviewTransform.scaleType = scaleType
@@ -243,10 +302,9 @@
mPreviewTransform.transformView(PREVIEW_VIEW_SIZE, layoutDirection, mView)
// Assert.
- val scale: Float = PREVIEW_VIEW_SIZE.height.toFloat() / FIT_CROP_RECT.height()
assertThat(mView.scaleX).isWithin(FLOAT_ERROR).of(scale)
assertThat(mView.scaleY).isWithin(FLOAT_ERROR).of(scale)
assertThat(mView.translationX).isWithin(FLOAT_ERROR).of(translationX)
- assertThat(mView.translationY).isWithin(FLOAT_ERROR).of(0f)
+ assertThat(mView.translationY).isWithin(FLOAT_ERROR).of(translationY)
}
}
\ No newline at end of file
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewBitmapTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewBitmapTest.java
index dc2454d..8fc0720 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewBitmapTest.java
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewBitmapTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import android.Manifest;
import android.content.Context;
import android.graphics.Bitmap;
@@ -39,13 +38,13 @@
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
-import androidx.test.rule.GrantPermissionRule;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import java.util.concurrent.ExecutionException;
@@ -61,8 +60,7 @@
public final ActivityTestRule<FakeActivity> mActivityRule =
new ActivityTestRule<>(FakeActivity.class);
@Rule
- public final GrantPermissionRule mPermissionRule = GrantPermissionRule.grant(
- Manifest.permission.CAMERA);
+ public final TestRule mUseCamera = CameraUtil.grantCameraPermissionAndPreTest();
private static final int CAMERA_LENS = CameraSelector.LENS_FACING_BACK;
private ProcessCameraProvider mCameraProvider;
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewStreamStateTest.kt b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewStreamStateTest.kt
index 1bd466d..bdfd39b 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewStreamStateTest.kt
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewStreamStateTest.kt
@@ -34,13 +34,13 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.rule.GrantPermissionRule
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assume
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.junit.rules.TestRule
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.util.concurrent.CountDownLatch
@@ -65,7 +65,7 @@
private lateinit var mCameraProvider: ProcessCameraProvider
@get:Rule
- var mCameraPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA)
+ val mUseCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
@Suppress("DEPRECATION")
@get:Rule
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java
index 83bf616..37246b9 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java
@@ -33,7 +33,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.Manifest;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
@@ -62,6 +61,7 @@
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.FutureCallback;
import androidx.camera.core.impl.utils.futures.Futures;
+import androidx.camera.testing.CameraUtil;
import androidx.camera.testing.SurfaceFormatUtil;
import androidx.camera.testing.fakes.FakeActivity;
import androidx.camera.testing.fakes.FakeCamera;
@@ -74,13 +74,13 @@
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
-import androidx.test.rule.GrantPermissionRule;
import com.google.common.util.concurrent.ListenableFuture;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
@@ -98,8 +98,7 @@
private static final Size DEFAULT_SURFACE_SIZE = new Size(640, 480);
@Rule
- public final GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
- Manifest.permission.CAMERA);
+ public TestRule mUseCamera = CameraUtil.grantCameraPermissionAndPreTest();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@Rule
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/PreviewTransformation.java b/camera/camera-view/src/main/java/androidx/camera/view/PreviewTransformation.java
index 4f17274..23e1b26 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/PreviewTransformation.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/PreviewTransformation.java
@@ -20,6 +20,11 @@
import static android.graphics.Paint.DITHER_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+import static androidx.camera.view.PreviewView.ScaleType.FILL_CENTER;
+import static androidx.camera.view.PreviewView.ScaleType.FIT_CENTER;
+import static androidx.camera.view.PreviewView.ScaleType.FIT_END;
+import static androidx.camera.view.PreviewView.ScaleType.FIT_START;
+
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
@@ -86,8 +91,7 @@
private static final String TAG = "PreviewTransform";
- private static final PreviewView.ScaleType DEFAULT_SCALE_TYPE =
- PreviewView.ScaleType.FILL_CENTER;
+ private static final PreviewView.ScaleType DEFAULT_SCALE_TYPE = FILL_CENTER;
// Each vertex is represented by a pair of (x, y) which is 2 slots in a float array.
private static final int FLOAT_NUMBER_PER_VERTEX = 2;
@@ -220,13 +224,14 @@
float[] previewViewCropRectVertexes;
if (isCropRectAspectRatioMatchPreviewView(previewViewSize)) {
// If crop rect has the same aspect ratio as PreviewView, scale the crop rect to fill
- // the entire PreviewView. This happens if the scale type is FILL_*.
+ // the entire PreviewView. This happens if the scale type is FILL_* AND a
+ // PreviewView-based viewport is used.
previewViewCropRectVertexes = sizeToVertexes(previewViewSize);
} else {
- // If crop rect's aspect ratio doesn't match PreviewView, fit the crop rect into the
- // PreviewView. This happens if the scale type is FIT_*.
- RectF previewViewCropRect = getPreviewViewCropRectForFitTypes(previewViewSize,
- layoutDirection);
+ // If the aspect ratios don't match, it could be 1) scale type is FIT_*, 2) the
+ // Viewport is not based on the PreviewView or 3) both.
+ RectF previewViewCropRect = getPreviewViewCropRectForMismatchedAspectRatios(
+ previewViewSize, layoutDirection);
previewViewCropRectVertexes = rectToVertexes(previewViewCropRect);
}
float[] rotatedPreviewViewCropRectVertexes = createRotatedVertexes(
@@ -241,23 +246,67 @@
}
/**
- * Gets the crop rect coordinates in {@link PreviewView} for FIT_* types.
+ * Gets the crop rect in {@link PreviewView} coordinates for the case where crop rect's aspect
+ * ratio doesn't match {@link PreviewView}'s aspect ratio.
+ *
+ * <p> When aspect ratios don't match, additional calculation is needed to figure out how to
+ * fit crop rect into the{@link PreviewView}.
*/
- RectF getPreviewViewCropRectForFitTypes(Size previewViewSize, int layoutOrientation) {
- Matrix matrix = new Matrix();
+ RectF getPreviewViewCropRectForMismatchedAspectRatios(Size previewViewSize,
+ int layoutDirection) {
RectF previewViewRect = new RectF(0, 0, previewViewSize.getWidth(),
previewViewSize.getHeight());
SizeF rotatedCropRectSize = getRotatedCropRectSize();
- RectF sourceCropRect = new RectF(0, 0, rotatedCropRectSize.getWidth(),
+ RectF rotatedSurfaceCropRect = new RectF(0, 0, rotatedCropRectSize.getWidth(),
rotatedCropRectSize.getHeight());
- // Get the mapping that fits crop rect to preview based on the fit type.
- matrix.setRectToRect(sourceCropRect, previewViewRect, getMatrixFitScaleType());
- // Use the mapping to map the crop rect.
- matrix.mapRect(sourceCropRect);
- if (layoutOrientation == LayoutDirection.RTL) {
- return flipHorizontally(sourceCropRect, (float) previewViewSize.getWidth() / 2);
+ Matrix matrix = new Matrix();
+ setMatrixRectToRect(matrix, rotatedSurfaceCropRect, previewViewRect, mScaleType);
+ matrix.mapRect(rotatedSurfaceCropRect);
+ if (layoutDirection == LayoutDirection.RTL) {
+ return flipHorizontally(rotatedSurfaceCropRect, (float) previewViewSize.getWidth() / 2);
}
- return sourceCropRect;
+ return rotatedSurfaceCropRect;
+ }
+
+ /**
+ * Set the matrix that maps the source rectangle to the destination rectangle.
+ *
+ * <p> This static method is an extension of {@link Matrix#setRectToRect} with an additional
+ * support for FILL_* types.
+ */
+ private static void setMatrixRectToRect(Matrix matrix, RectF source, RectF destination,
+ PreviewView.ScaleType scaleType) {
+ Matrix.ScaleToFit matrixScaleType;
+ switch (scaleType) {
+ case FIT_CENTER:
+ // Fallthrough.
+ case FILL_CENTER:
+ matrixScaleType = Matrix.ScaleToFit.CENTER;
+ break;
+ case FIT_END:
+ // Fallthrough.
+ case FILL_END:
+ matrixScaleType = Matrix.ScaleToFit.END;
+ break;
+ case FIT_START:
+ // Fallthrough.
+ case FILL_START:
+ matrixScaleType = Matrix.ScaleToFit.START;
+ break;
+ default:
+ Logger.e(TAG, "Unexpected crop rect: " + scaleType);
+ matrixScaleType = Matrix.ScaleToFit.FILL;
+ }
+ boolean isFitTypes =
+ scaleType == FIT_CENTER || scaleType == FIT_START || scaleType == FIT_END;
+ if (isFitTypes) {
+ matrix.setRectToRect(source, destination, matrixScaleType);
+ } else {
+ // android.graphics.Matrix doesn't support fill scale types. The workaround is
+ // mapping inversely from destination to source, then invert the matrix.
+ matrix.setRectToRect(destination, source, matrixScaleType);
+ matrix.invert(matrix);
+ }
}
/**
@@ -272,23 +321,6 @@
}
/**
- * {@link PreviewView.ScaleType} to {@link Matrix.ScaleToFit} conversion.
- */
- private Matrix.ScaleToFit getMatrixFitScaleType() {
- switch (mScaleType) {
- case FIT_CENTER:
- return Matrix.ScaleToFit.CENTER;
- case FIT_END:
- return Matrix.ScaleToFit.END;
- case FIT_START:
- return Matrix.ScaleToFit.START;
- default:
- Logger.w(TAG, "Unexpected crop rect. Please use PreviewView.getViewPort().");
- return Matrix.ScaleToFit.CENTER;
- }
- }
-
- /**
* Returns crop rect size with target rotation applied.
*/
private SizeF getRotatedCropRectSize() {
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
index f44b9c3..8b16ab1 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
@@ -19,6 +19,7 @@
import android.net.Uri
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
+import androidx.camera.testing.CameraUtil
import androidx.camera.view.PreviewView
import androidx.fragment.app.testing.FragmentScenario
import androidx.lifecycle.Lifecycle
@@ -34,6 +35,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
+import org.junit.rules.TestRule
import org.junit.runner.RunWith
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
@@ -51,8 +53,10 @@
val thrown: ExpectedException = ExpectedException.none()
@get:Rule
+ val useCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+
+ @get:Rule
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
- android.Manifest.permission.CAMERA,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.Manifest.permission.RECORD_AUDIO
)
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraViewFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraViewFragmentTest.kt
index d1a6296..6024366 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraViewFragmentTest.kt
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraViewFragmentTest.kt
@@ -38,6 +38,7 @@
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.junit.rules.TestRule
import org.junit.runner.RunWith
@LargeTest
@@ -45,10 +46,14 @@
class CameraViewFragmentTest {
@get:Rule
+ val useCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+
+ @get:Rule
val permissionRule: GrantPermissionRule =
- GrantPermissionRule.grant(android.Manifest.permission.CAMERA,
+ GrantPermissionRule.grant(
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
- android.Manifest.permission.RECORD_AUDIO)
+ android.Manifest.permission.RECORD_AUDIO
+ )
@Before
fun setup() {
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewFragmentTest.java b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewFragmentTest.java
index 61d9d3c..5246d91 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewFragmentTest.java
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewFragmentTest.java
@@ -39,6 +39,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
@@ -53,8 +54,8 @@
private static final int PREVIEW_UPDATE_COUNT = 30;
@Rule
- public GrantPermissionRule mCameraPermissionRule =
- GrantPermissionRule.grant(android.Manifest.permission.CAMERA);
+ public TestRule mUseCamera = CameraUtil.grantCameraPermissionAndPreTest();
+
@Rule
public GrantPermissionRule mStoragePermissionRule =
GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index 92a5917..5b3d3a2 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -1,6 +1,10 @@
// Signature format: 3.0
package androidx.compose.animation.core {
+ public final class AndroidAnimationClockKt {
+ method public static void setRootAnimationClockFactory(kotlin.jvm.functions.Function0<? extends androidx.compose.animation.core.AnimationClockObservable> p);
+ }
+
public abstract class AnimatedFloat extends androidx.compose.animation.core.BaseAnimatedValue<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> {
ctor public AnimatedFloat(androidx.compose.animation.core.AnimationClockObservable clock, float visibilityThreshold);
method public final float getMax();
@@ -27,10 +31,6 @@
method public static void fling(androidx.compose.animation.core.AnimatedFloat, float startVelocity, androidx.compose.animation.core.FloatDecayAnimationSpec decay = androidx.compose.animation.core.ExponentialDecay(), kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.animation.core.TargetAnimation> adjustTarget, kotlin.jvm.functions.Function3<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,? super java.lang.Float,kotlin.Unit>? onEnd = null);
}
- public final class AnimationClockKt {
- method public static void setRootAnimationClockFactory(kotlin.jvm.functions.Function0<? extends androidx.compose.animation.core.AnimationClockObservable> p);
- }
-
public interface AnimationClockObservable {
method public void subscribe(androidx.compose.animation.core.AnimationClockObserver observer);
method public void unsubscribe(androidx.compose.animation.core.AnimationClockObserver observer);
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 92a5917..5b3d3a2 100644
--- a/compose/animation/animation-core/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation-core/api/public_plus_experimental_current.txt
@@ -1,6 +1,10 @@
// Signature format: 3.0
package androidx.compose.animation.core {
+ public final class AndroidAnimationClockKt {
+ method public static void setRootAnimationClockFactory(kotlin.jvm.functions.Function0<? extends androidx.compose.animation.core.AnimationClockObservable> p);
+ }
+
public abstract class AnimatedFloat extends androidx.compose.animation.core.BaseAnimatedValue<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> {
ctor public AnimatedFloat(androidx.compose.animation.core.AnimationClockObservable clock, float visibilityThreshold);
method public final float getMax();
@@ -27,10 +31,6 @@
method public static void fling(androidx.compose.animation.core.AnimatedFloat, float startVelocity, androidx.compose.animation.core.FloatDecayAnimationSpec decay = androidx.compose.animation.core.ExponentialDecay(), kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.animation.core.TargetAnimation> adjustTarget, kotlin.jvm.functions.Function3<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,? super java.lang.Float,kotlin.Unit>? onEnd = null);
}
- public final class AnimationClockKt {
- method public static void setRootAnimationClockFactory(kotlin.jvm.functions.Function0<? extends androidx.compose.animation.core.AnimationClockObservable> p);
- }
-
public interface AnimationClockObservable {
method public void subscribe(androidx.compose.animation.core.AnimationClockObserver observer);
method public void unsubscribe(androidx.compose.animation.core.AnimationClockObserver observer);
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index 92a5917..5b3d3a2 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -1,6 +1,10 @@
// Signature format: 3.0
package androidx.compose.animation.core {
+ public final class AndroidAnimationClockKt {
+ method public static void setRootAnimationClockFactory(kotlin.jvm.functions.Function0<? extends androidx.compose.animation.core.AnimationClockObservable> p);
+ }
+
public abstract class AnimatedFloat extends androidx.compose.animation.core.BaseAnimatedValue<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> {
ctor public AnimatedFloat(androidx.compose.animation.core.AnimationClockObservable clock, float visibilityThreshold);
method public final float getMax();
@@ -27,10 +31,6 @@
method public static void fling(androidx.compose.animation.core.AnimatedFloat, float startVelocity, androidx.compose.animation.core.FloatDecayAnimationSpec decay = androidx.compose.animation.core.ExponentialDecay(), kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.animation.core.TargetAnimation> adjustTarget, kotlin.jvm.functions.Function3<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,? super java.lang.Float,kotlin.Unit>? onEnd = null);
}
- public final class AnimationClockKt {
- method public static void setRootAnimationClockFactory(kotlin.jvm.functions.Function0<? extends androidx.compose.animation.core.AnimationClockObservable> p);
- }
-
public interface AnimationClockObservable {
method public void subscribe(androidx.compose.animation.core.AnimationClockObserver observer);
method public void unsubscribe(androidx.compose.animation.core.AnimationClockObserver observer);
diff --git a/compose/animation/animation-core/src/androidMain/kotlin/androidx/compose/animation/core/AndroidAnimationClock.kt b/compose/animation/animation-core/src/androidMain/kotlin/androidx/compose/animation/core/AndroidAnimationClock.kt
index bd12127..e2d9347 100644
--- a/compose/animation/animation-core/src/androidMain/kotlin/androidx/compose/animation/core/AndroidAnimationClock.kt
+++ b/compose/animation/animation-core/src/androidMain/kotlin/androidx/compose/animation/core/AndroidAnimationClock.kt
@@ -22,6 +22,12 @@
import android.view.Choreographer
import java.util.concurrent.CountDownLatch
+/** @suppress */
+@InternalAnimationApi
+var rootAnimationClockFactory: () -> AnimationClockObservable = { DefaultAnimationClock() }
+ // @TestOnly
+ set
+
/**
* Default Choreographer based clock that pushes a new frame to all subscribers on each
* Choreographer tick, until all subscribers have unsubscribed. An instance of this clock will be
@@ -32,7 +38,7 @@
* synchronously on the main thread. If this poses a problem, consider initializing this clock on
* the main thread itself.
*/
-actual class DefaultAnimationClock actual constructor() : BaseAnimationClock() {
+class DefaultAnimationClock : BaseAnimationClock() {
private val mainChoreographer: Choreographer
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClock.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClock.kt
index e5734ec..6ee51af 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClock.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClock.kt
@@ -18,14 +18,6 @@
import androidx.compose.ui.util.annotation.CallSuper
-expect class DefaultAnimationClock() : BaseAnimationClock
-
-/** @suppress */
-@InternalAnimationApi
-var rootAnimationClockFactory: () -> AnimationClockObservable = { DefaultAnimationClock() }
- // @TestOnly
- set
-
/**
* A custom clock whose frame time can be manually updated via mutating [clockTimeMillis].
* Observers will be called immediately with the current time when they are subscribed. Use
diff --git a/compose/animation/animation-core/src/desktopMain/kotlin/androidx/compose/animation/core/DefaultAnimationClock.kt b/compose/animation/animation-core/src/desktopMain/kotlin/androidx/compose/animation/core/DefaultAnimationClock.kt
deleted file mode 100644
index eef5dd2..0000000
--- a/compose/animation/animation-core/src/desktopMain/kotlin/androidx/compose/animation/core/DefaultAnimationClock.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.compose.animation.core
-
-import androidx.compose.runtime.dispatch.DesktopUiDispatcher
-
-actual class DefaultAnimationClock(
- fps: Int,
- private val dispatcher: DesktopUiDispatcher
-) : BaseAnimationClock() {
-
- // TODO: detect actual display refresh rate? what to do with displays with
- // different refresh rates?
- actual constructor() : this(60, DesktopUiDispatcher.Dispatcher)
-
- val delay = 1_000 / fps
-
- @Volatile
- private var scheduled = false
-
- private fun frameCallback(time: Long) {
- scheduled = false
- dispatchTime(time / 1000000)
- }
-
- override fun subscribe(observer: AnimationClockObserver) {
- if (!scheduled) {
- dispatcher.scheduleCallbackWithDelay(delay, ::frameCallback)
- scheduled = true
- }
- super.subscribe(observer)
- }
-
- override fun dispatchTime(frameTimeMillis: Long) {
- super.dispatchTime(frameTimeMillis)
- scheduled = if (hasObservers()) {
- dispatcher.scheduleCallbackWithDelay(delay, ::frameCallback)
- true
- } else {
- false
- }
- }
-}
\ No newline at end of file
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt
index 7e8f4a9..1a62aa5 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt
@@ -30,7 +30,7 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.RowScope.weight
+import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Stack
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
@@ -157,7 +157,7 @@
alignment: Alignment,
visible: Boolean
) {
- Stack(Modifier.fillMaxHeight().weight(1f)) {
+ Stack(with(RowScope) { Modifier.fillMaxHeight().weight(1f) }) {
val animationAlignment = if (oppositeDirection) opposite(alignment) else alignment
val enter = when (animationAlignment) {
diff --git a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt
index e7152d9..a4027e7 100644
--- a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt
@@ -44,7 +44,7 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ColumnScope.align
+import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.Stack
@@ -166,7 +166,7 @@
var expanded by remember { mutableStateOf(true) }
FloatingActionButton(
onClick = { expanded = !expanded },
- modifier = Modifier.align(Alignment.CenterHorizontally)
+ modifier = with(ColumnScope) { Modifier.align(Alignment.CenterHorizontally) }
) {
Row(Modifier.padding(start = 12.dp, end = 12.dp)) {
Icon(Icons.Default.Favorite, Modifier.align(Alignment.CenterVertically))
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index aa33496..dfb6564 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -47,14 +47,13 @@
api(KOTLIN_COROUTINES_CORE)
api(SKIKO)
-
- implementation(KOTLIN_COROUTINES_SWING)
}
jvmTest {
resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
resources.srcDirs += "src/jvmTest/res"
dependencies {
+ implementation(KOTLIN_COROUTINES_TEST)
implementation(SKIKO_CURRENT_OS)
implementation project(":ui:ui-test")
implementation(JUNIT)
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/example1/Main.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/example1/Main.kt
index f13e30d..5e42c4a 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/example1/Main.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/example1/Main.kt
@@ -15,7 +15,11 @@
*/
package androidx.compose.desktop.examples.example1
+import androidx.compose.animation.animate
+import androidx.compose.animation.core.TweenSpec
import androidx.compose.desktop.AppWindow
+import androidx.compose.desktop.Window
+import androidx.compose.foundation.Box
import androidx.compose.foundation.Image
import androidx.compose.foundation.Text
import androidx.compose.foundation.background
@@ -26,16 +30,17 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.ExperimentalLazyDsl
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material.Button
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.ExtendedFloatingActionButton
+import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Slider
import androidx.compose.material.TextField
@@ -55,8 +60,6 @@
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.annotatedString
-import androidx.compose.ui.text.font.fontFamily
-import androidx.compose.ui.text.platform.font
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextDecoration.Companion.Underline
import androidx.compose.ui.unit.IntSize
@@ -66,10 +69,8 @@
private const val title = "Desktop Compose Elements"
-val italicFont = fontFamily(font("Noto Italic", "NotoSans-Italic.ttf"))
-
fun main() {
- AppWindow(title, IntSize(1024, 768)).show {
+ AppWindow(title, IntSize(1024, 850)).show {
Scaffold(
topBar = {
TopAppBar(
@@ -116,7 +117,9 @@
Text(
text = annotatedString {
append("The quick ")
- appendInlineContent(inlineIndicatorId)
+ if (animation.value) {
+ appendInlineContent(inlineIndicatorId)
+ }
pushStyle(SpanStyle(
color = Color(0xff964B00),
shadow = Shadow(Color.Green, offset = Offset(1f, 1f))
@@ -195,11 +198,10 @@
" smaller.quickSort() + pivot + greater.quickSort()\n" +
" }\n" +
"}",
- modifier = Modifier.padding(10.dp),
- fontFamily = italicFont
+ modifier = Modifier.padding(10.dp)
)
- Button(onClick = {
+ Button(modifier = Modifier.padding(4.dp), onClick = {
amount.value++
}) {
Text("Base")
@@ -209,16 +211,27 @@
modifier = Modifier.padding(vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically
) {
- Button(
- onClick = {
- animation.value = !animation.value
- }) {
- Text("Toggle")
+ Row {
+ Button(
+ modifier = Modifier.padding(4.dp),
+ onClick = {
+ animation.value = !animation.value
+ }) {
+ Text("Toggle")
+ }
+
+ Button(
+ modifier = Modifier.padding(4.dp),
+ onClick = {
+ Window(size = IntSize(400, 200)) {
+ Animations(isCircularEnabled = animation.value)
+ }
+ }) {
+ Text("Window")
+ }
}
- if (animation.value) {
- CircularProgressIndicator()
- }
+ Animations(isCircularEnabled = animation.value)
}
Slider(value = amount.value.toFloat() / 100f,
@@ -238,15 +251,30 @@
}
}
+@Composable
+fun Animations(isCircularEnabled: Boolean) = Row {
+ if (isCircularEnabled) {
+ CircularProgressIndicator(Modifier.padding(10.dp))
+ }
+
+ val enabled = remember { mutableStateOf(true) }
+ val color = animate(
+ if (enabled.value) Color.Green else Color.Red,
+ animSpec = TweenSpec(durationMillis = 2000)
+ )
+
+ MaterialTheme {
+ Box(
+ Modifier.size(70.dp).clickable { enabled.value = !enabled.value },
+ backgroundColor = color
+ )
+ }
+}
+
@OptIn(ExperimentalLazyDsl::class)
@Composable
private fun RightColumn(modifier: Modifier) = LazyColumn(modifier) {
- items((1..100).toList()) { x ->
- LazyRow {
- items((0..23).toList()) { y ->
- val str = if (y == 0) x.toString() else ('a' + y).toString()
- Text("$str ")
- }
- }
+ items((1..10000).toList()) { x ->
+ Text(x.toString())
}
}
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.kt
index 97f74a1..a120643 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.kt
@@ -21,7 +21,6 @@
import androidx.compose.foundation.ContentGravity
import androidx.compose.foundation.Text
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ColumnScope.align
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -57,7 +56,7 @@
modifier = Modifier.fillMaxSize(),
color = Color.White
) {
- Column(Modifier.align(Alignment.CenterHorizontally)) {
+ Column {
Spacer(modifier = Modifier.height(50.dp))
Row(modifier = Modifier.preferredHeight(40.dp)) {
Button("Popup", { popupState.value = true })
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/AppFrame.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/AppFrame.kt
index e3266fd..2ee20f0 100644
--- a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/AppFrame.kt
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/AppFrame.kt
@@ -42,6 +42,9 @@
var isCentered: Boolean = true
protected set
+ var isClosed: Boolean = false
+ protected set
+
val onDismissEvents = mutableListOf<() -> Unit>()
abstract fun setPosition(x: Int, y: Int)
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/AppWindow.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/AppWindow.kt
index ce6fee7..220ef86d 100644
--- a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/AppWindow.kt
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/AppWindow.kt
@@ -61,6 +61,7 @@
if (defaultCloseOperation != WindowConstants.DO_NOTHING_ON_CLOSE) {
onDismissEvents.forEach { it.invoke() }
AppManager.removeWindow(parent)
+ isClosed = true
}
}
})
@@ -166,15 +167,11 @@
window.dispatchEvent(WindowEvent(window, WindowEvent.WINDOW_CLOSING))
}
- internal override fun dispose() {
- window.apply {
- disposeCanvas()
- dispose()
- }
+ override fun dispose() {
invoker?.unlockWindow()
}
- internal override fun lockWindow() {
+ override fun lockWindow() {
window.apply {
defaultCloseOperation = WindowConstants.DO_NOTHING_ON_CLOSE
setFocusableWindowState(false)
@@ -184,7 +181,7 @@
invoker?.connectPair(this)
}
- internal override fun unlockWindow() {
+ override fun unlockWindow() {
window.apply {
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
setFocusableWindowState(true)
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/ComposeWindow.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
index acbd3cc..523d2aa 100644
--- a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
@@ -15,14 +15,11 @@
*/
package androidx.compose.desktop
-import androidx.compose.runtime.dispatch.DesktopUiDispatcher
import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
import androidx.compose.ui.input.mouse.MouseScrollEvent
import androidx.compose.ui.input.mouse.MouseScrollUnit
import androidx.compose.ui.platform.DesktopOwners
import org.jetbrains.skija.Canvas
-import org.jetbrains.skiko.SkiaLayer
-import org.jetbrains.skiko.SkiaRenderer
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import java.awt.event.InputMethodEvent
@@ -43,7 +40,7 @@
}
val parent: AppFrame
- private val layer: SkiaLayer = SkiaLayer()
+ private val layer = FrameSkiaLayer()
var owners: DesktopOwners? = null
set(value) {
@@ -53,7 +50,7 @@
constructor(parent: AppFrame) : super() {
this.parent = parent
- contentPane.add(layer)
+ contentPane.add(layer.wrapped)
addComponentListener(object : ComponentAdapter() {
override fun componentResized(e: ComponentEvent) {
@@ -63,104 +60,83 @@
initCanvas()
}
- fun updateLayer() {
+ private fun updateLayer() {
if (!isVisible) {
return
}
layer.updateLayer()
}
- fun redrawLayer() {
+ fun needRedrawLayer() {
if (!isVisible) {
return
}
- layer.redrawLayer()
+ layer.needRedrawLayer()
+ }
+
+ override fun dispose() {
+ layer.dispose()
+ super.dispose()
}
private fun initCanvas() {
- layer.addInputMethodListener(object : InputMethodListener {
+ layer.wrapped.addInputMethodListener(object : InputMethodListener {
override fun caretPositionChanged(p0: InputMethodEvent?) {
TODO("Implement input method caret change")
}
- override fun inputMethodTextChanged(
- event: InputMethodEvent
- ) = DesktopUiDispatcher.Dispatcher.lockCallbacks {
+ override fun inputMethodTextChanged(event: InputMethodEvent) {
owners?.onInputMethodTextChanged(event)
}
})
- layer.addMouseListener(object : MouseAdapter() {
+ layer.wrapped.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(event: MouseEvent) = Unit
- override fun mousePressed(
- event: MouseEvent
- ) = DesktopUiDispatcher.Dispatcher.lockCallbacks {
+ override fun mousePressed(event: MouseEvent) {
owners?.onMousePressed(event.x, event.y)
}
- override fun mouseReleased(
- event: MouseEvent
- ) = DesktopUiDispatcher.Dispatcher.lockCallbacks {
+ override fun mouseReleased(event: MouseEvent) {
owners?.onMouseReleased(event.x, event.y)
}
})
- layer.addMouseMotionListener(object : MouseMotionAdapter() {
- override fun mouseDragged(
- event: MouseEvent
- ) = DesktopUiDispatcher.Dispatcher.lockCallbacks {
+ layer.wrapped.addMouseMotionListener(object : MouseMotionAdapter() {
+ override fun mouseDragged(event: MouseEvent) {
owners?.onMouseDragged(event.x, event.y)
}
})
- layer.addMouseWheelListener { event ->
- DesktopUiDispatcher.Dispatcher.lockCallbacks {
- owners?.onMouseScroll(event.x, event.y, event.toComposeEvent())
- }
+ layer.wrapped.addMouseWheelListener { event ->
+ owners?.onMouseScroll(event.x, event.y, event.toComposeEvent())
}
- layer.addKeyListener(object : KeyAdapter() {
- override fun keyPressed(
- event: KeyEvent
- ) = DesktopUiDispatcher.Dispatcher.lockCallbacks {
+ layer.wrapped.addKeyListener(object : KeyAdapter() {
+ override fun keyPressed(event: KeyEvent) {
owners?.onKeyPressed(event.keyCode, event.keyChar)
}
- override fun keyReleased(
- event: KeyEvent
- ) = DesktopUiDispatcher.Dispatcher.lockCallbacks {
+ override fun keyReleased(event: KeyEvent) {
owners?.onKeyReleased(event.keyCode, event.keyChar)
}
- override fun keyTyped(
- event: KeyEvent
- ) = DesktopUiDispatcher.Dispatcher.lockCallbacks {
+ override fun keyTyped(event: KeyEvent) {
owners?.onKeyTyped(event.keyChar)
}
})
}
- fun disposeCanvas() {
- layer.disposeLayer()
- layer.updateLayer()
- layer.renderer!!.onDispose()
- }
-
override fun setVisible(value: Boolean) {
- super.setVisible(value)
- updateLayer()
+ if (value != isVisible) {
+ super.setVisible(value)
+ updateLayer()
+ needRedrawLayer()
+ }
}
}
-private class OwnersRenderer(private val owners: DesktopOwners) : SkiaRenderer {
- override fun onDispose() = Unit
- override fun onInit() = Unit
- override fun onReshape(width: Int, height: Int) = Unit
-
- override fun onRender(canvas: Canvas, width: Int, height: Int) {
+private class OwnersRenderer(private val owners: DesktopOwners) : FrameSkiaLayer.Renderer {
+ override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
try {
- Thread.currentThread().contextClassLoader = ClassLoader.getSystemClassLoader()
- DesktopUiDispatcher.Dispatcher.lockCallbacks {
- owners.onRender(canvas, width, height)
- }
+ owners.onRender(canvas, width, height, nanoTime)
} catch (e: Throwable) {
e.printStackTrace(System.err)
if (System.getProperty("compose.desktop.ignore.errors") == null) {
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/FrameDispatcher.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/FrameDispatcher.kt
new file mode 100644
index 0000000..96803b3
--- /dev/null
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/FrameDispatcher.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.desktop
+
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.yield
+import kotlin.coroutines.CoroutineContext
+
+/**
+ * Dispatch frame after call of [scheduleFrame]
+ *
+ * Frames executed not more frequent than [framesPerSecond]
+ */
+internal class FrameDispatcher(
+ private val onFrame: suspend (nanoTime: Long) -> Unit,
+ private val framesPerSecond: () -> Int,
+ private val nanoTime: () -> Long = System::nanoTime,
+ context: CoroutineContext = Dispatchers.Main
+) {
+ private var needFrame = CompletableDeferred<Unit>()
+
+ private val job = GlobalScope.launch(context) {
+ while (true) {
+ needFrame.await()
+ needFrame = CompletableDeferred()
+
+ val frameNanoTime = nanoTime()
+ onFrame(frameNanoTime)
+
+ val elapsed = nanoTime() - frameNanoTime
+ val refreshRate = framesPerSecond()
+ val singleFrameNanos = 1_000_000_000 / refreshRate
+ val needToWaitMillis = maxOf(0, singleFrameNanos - elapsed) / 1_000_000
+ delayOrYield(needToWaitMillis)
+ }
+ }
+
+ private suspend fun delayOrYield(millis: Long) {
+ if (millis > 0) {
+ delay(millis)
+ } else {
+ yield()
+ }
+ }
+
+ fun cancel() {
+ job.cancel()
+ }
+
+ fun scheduleFrame() {
+ needFrame.complete(Unit)
+ }
+}
\ No newline at end of file
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/FrameSkiaLayer.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/FrameSkiaLayer.kt
new file mode 100644
index 0000000..bce3980
--- /dev/null
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/FrameSkiaLayer.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.desktop
+
+import org.jetbrains.skija.Canvas
+import org.jetbrains.skija.Picture
+import org.jetbrains.skija.PictureRecorder
+import org.jetbrains.skija.Rect
+import org.jetbrains.skiko.SkiaLayer
+import org.jetbrains.skiko.SkiaRenderer
+import java.awt.DisplayMode
+
+internal class FrameSkiaLayer {
+ var renderer: Renderer? = null
+
+ private var isDisposed = false
+ private var frameNanoTime = 0L
+ private val frameDispatcher = FrameDispatcher(
+ onFrame = { onFrame(it) },
+ framesPerSecond = ::getFramesPerSecond
+ )
+
+ @Volatile private var picture: Picture? = null
+ private val pictureRecorder = PictureRecorder()
+
+ private fun onFrame(nanoTime: Long) {
+ this.frameNanoTime = nanoTime
+ wrapped.redrawLayer()
+ }
+
+ val wrapped = object : SkiaLayer() {
+ override fun redrawLayer() {
+ preparePicture(frameNanoTime)
+ super.redrawLayer()
+ }
+ }
+
+ init {
+ wrapped.renderer = object : SkiaRenderer {
+ override fun onRender(canvas: Canvas, width: Int, height: Int) {
+ if (picture != null) {
+ canvas.drawPicture(picture)
+ }
+ }
+
+ override fun onDispose() = Unit
+ override fun onInit() = Unit
+ override fun onReshape(width: Int, height: Int) = Unit
+ }
+ }
+
+ // We draw into picture, because SkiaLayer.draw can be called from the other thread,
+ // but onRender should be called in AWT thread. Picture doesn't add any visible overhead on
+ // CPU/RAM.
+ private fun preparePicture(frameTimeNanos: Long) {
+ val bounds = Rect.makeWH(wrapped.width.toFloat(), wrapped.height.toFloat())
+ val pictureCanvas = pictureRecorder.beginRecording(bounds)
+ renderer?.onRender(pictureCanvas, wrapped.width, wrapped.height, frameTimeNanos)
+ picture?.close()
+ picture = pictureRecorder.finishRecordingAsPicture()
+ }
+
+ fun reinit() {
+ check(!isDisposed)
+ wrapped.reinit()
+ }
+
+ private fun getFramesPerSecond(): Int {
+ val refreshRate = wrapped.graphicsConfiguration.device.displayMode.refreshRate
+ return if (refreshRate != DisplayMode.REFRESH_RATE_UNKNOWN) refreshRate else 60
+ }
+
+ fun updateLayer() {
+ check(!isDisposed)
+ wrapped.updateLayer()
+ }
+
+ fun dispose() {
+ check(!isDisposed)
+ frameDispatcher.cancel()
+ wrapped.disposeLayer()
+ wrapped.updateLayer()
+ picture?.close()
+ pictureRecorder.close()
+ isDisposed = true
+ }
+
+ fun needRedrawLayer() {
+ check(!isDisposed)
+ frameDispatcher.scheduleFrame()
+ }
+
+ interface Renderer {
+ fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long)
+ }
+}
\ No newline at end of file
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/Wrapper.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/Wrapper.kt
index c92f9bb8..a23d2f2 100644
--- a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/Wrapper.kt
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/desktop/Wrapper.kt
@@ -22,7 +22,7 @@
import androidx.compose.ui.platform.setContent
fun ComposeWindow.setContent(content: @Composable () -> Unit): Composition {
- val owners = DesktopOwners(this, this::redrawLayer)
+ val owners = DesktopOwners(this, this::needRedrawLayer)
val owner = DesktopOwner(owners)
val composition = owner.setContent(content)
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/AppDialog.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/AppDialog.kt
index 15c42f1..e41551e 100644
--- a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/AppDialog.kt
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/AppDialog.kt
@@ -53,6 +53,8 @@
}
onDispose {
- dialog.dispose()
+ if (!dialog.isClosed) {
+ dialog.close()
+ }
}
}
diff --git a/compose/desktop/desktop/src/jvmTest/kotlin/androidx/compose/desktop/FrameDispatcherTest.kt b/compose/desktop/desktop/src/jvmTest/kotlin/androidx/compose/desktop/FrameDispatcherTest.kt
new file mode 100644
index 0000000..a40d6fc7
--- /dev/null
+++ b/compose/desktop/desktop/src/jvmTest/kotlin/androidx/compose/desktop/FrameDispatcherTest.kt
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.desktop
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.runBlockingTest
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@OptIn(ExperimentalCoroutinesApi::class)
+internal class FrameDispatcherTest {
+ var frameIndex = 0
+ var frameTime = 0L
+
+ var frameDuration = 0L
+
+ suspend fun onFrame(nanoTime: Long) {
+ frameIndex++
+ frameTime = nanoTime / 1_000_000
+ delay(frameDuration)
+ }
+
+ fun TestCoroutineScope.testFrameDispatcher() = FrameDispatcher(
+ ::onFrame,
+ framesPerSecond = { 100 }, // one frame is 10 milliseconds
+ nanoTime = { currentTime * 1_000_000 },
+ coroutineContext
+ )
+
+ @Test
+ fun `don't schedule`() = runBlockingTest {
+ val timer = testFrameDispatcher()
+
+ runCurrent()
+ assertEquals(0, currentTime)
+ assertEquals(0, frameIndex)
+ assertEquals(0, frameTime)
+
+ advanceTimeBy(10_000)
+ assertEquals(0, frameIndex)
+ assertEquals(0, frameTime)
+
+ timer.cancel()
+ }
+
+ @Test
+ fun `schedule one time`() = runBlockingTest {
+ val timer = testFrameDispatcher()
+
+ advanceTimeBy(1234)
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(1234, frameTime)
+
+ advanceTimeBy(10_000)
+ assertEquals(1, frameIndex)
+ assertEquals(1234, frameTime)
+
+ timer.cancel()
+ }
+
+ @Test
+ fun `schedule multiple times`() = runBlockingTest {
+ val timer = testFrameDispatcher()
+
+ advanceTimeBy(10_000)
+ timer.scheduleFrame()
+
+ timer.scheduleFrame()
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ advanceTimeBy(9)
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ advanceTimeBy(1)
+ assertEquals(2, frameIndex)
+ assertEquals(10_010, frameTime)
+
+ advanceTimeBy(10_000)
+ assertEquals(2, frameIndex)
+ assertEquals(10_010, frameTime)
+
+ timer.cancel()
+ }
+
+ @Test
+ fun `schedule after short delay`() = runBlockingTest {
+ val timer = testFrameDispatcher()
+
+ advanceTimeBy(10_000)
+ timer.scheduleFrame()
+
+ advanceTimeBy(5)
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ advanceTimeBy(4)
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ advanceTimeBy(1)
+ timer.scheduleFrame()
+ assertEquals(2, frameIndex)
+ assertEquals(10_010, frameTime)
+
+ timer.cancel()
+ }
+
+ @Test
+ fun `schedule after long delay`() = runBlockingTest {
+ val timer = testFrameDispatcher()
+
+ advanceTimeBy(10_000)
+ timer.scheduleFrame()
+
+ advanceTimeBy(10_000)
+ timer.scheduleFrame()
+ assertEquals(2, frameIndex)
+ assertEquals(20_000, frameTime)
+
+ timer.cancel()
+ }
+
+ @Test
+ fun `schedule after short frame`() = runBlockingTest {
+ val timer = testFrameDispatcher()
+ frameDuration = 7
+
+ advanceTimeBy(10_000)
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ advanceTimeBy(9)
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ advanceTimeBy(1)
+ assertEquals(2, frameIndex)
+ assertEquals(10_010, frameTime)
+
+ advanceTimeBy(10_000)
+ assertEquals(2, frameIndex)
+ assertEquals(10_010, frameTime)
+
+ timer.cancel()
+ }
+
+ @Test
+ fun `schedule after long frame`() = runBlockingTest {
+ val timer = testFrameDispatcher()
+ frameDuration = 13
+
+ advanceTimeBy(10_000)
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ advanceTimeBy(12)
+ timer.scheduleFrame()
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+
+ advanceTimeBy(1)
+ assertEquals(2, frameIndex)
+ assertEquals(10_013, frameTime)
+
+ advanceTimeBy(10_000)
+ assertEquals(2, frameIndex)
+ assertEquals(10_013, frameTime)
+
+ timer.cancel()
+ }
+
+ @Test
+ fun cancel() = runBlockingTest {
+ val timer = testFrameDispatcher()
+
+ advanceTimeBy(10_000)
+ timer.scheduleFrame()
+
+ timer.scheduleFrame()
+ timer.scheduleFrame()
+ timer.cancel()
+ advanceTimeBy(10_000)
+ assertEquals(1, frameIndex)
+ assertEquals(10_000, frameTime)
+ }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/api/current.txt b/compose/foundation/foundation-layout/api/current.txt
index 348d88b..245230d 100644
--- a/compose/foundation/foundation-layout/api/current.txt
+++ b/compose/foundation/foundation-layout/api/current.txt
@@ -86,13 +86,16 @@
method @androidx.compose.runtime.Composable public static inline void Column(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement = Arrangement.Top, androidx.compose.ui.Alignment.Horizontal horizontalAlignment = Alignment.Start, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> children);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public final class ColumnScope {
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.VerticalAlignmentLine alignmentLine);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
- method @Deprecated @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal align);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
- field public static final androidx.compose.foundation.layout.ColumnScope INSTANCE;
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ColumnScope {
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.VerticalAlignmentLine alignmentLine);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
+ method @Deprecated @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal align);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
+ field public static final androidx.compose.foundation.layout.ColumnScope.Companion Companion;
+ }
+
+ public static final class ColumnScope.Companion implements androidx.compose.foundation.layout.ColumnScope {
}
@androidx.compose.foundation.layout.LayoutScopeMarker public final class ConstrainScope {
@@ -417,13 +420,16 @@
method @androidx.compose.runtime.Composable public static inline void Row(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement = Arrangement.Start, androidx.compose.ui.Alignment.Vertical verticalAlignment = Alignment.Top, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> children);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public final class RowScope {
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical alignment);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.HorizontalAlignmentLine alignmentLine);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
- method @Deprecated @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical align);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
- field public static final androidx.compose.foundation.layout.RowScope INSTANCE;
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface RowScope {
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical alignment);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.HorizontalAlignmentLine alignmentLine);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
+ method @Deprecated @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical align);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
+ field public static final androidx.compose.foundation.layout.RowScope.Companion Companion;
+ }
+
+ public static final class RowScope.Companion implements androidx.compose.foundation.layout.RowScope {
}
public enum SizeMode {
@@ -441,11 +447,14 @@
method @androidx.compose.runtime.Composable public static void Stack(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.Alignment alignment = Alignment.TopStart, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.StackScope,kotlin.Unit> children);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public final class StackScope {
- ctor public StackScope();
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment alignment);
- method @Deprecated @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment align);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier matchParentSize(androidx.compose.ui.Modifier);
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface StackScope {
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment alignment);
+ method @Deprecated @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment align);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier matchParentSize(androidx.compose.ui.Modifier);
+ field public static final androidx.compose.foundation.layout.StackScope.Companion Companion;
+ }
+
+ public static final class StackScope.Companion implements androidx.compose.foundation.layout.StackScope {
}
public final class State extends androidx.constraintlayout.core.state.State {
diff --git a/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt b/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
index 348d88b..245230d 100644
--- a/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
@@ -86,13 +86,16 @@
method @androidx.compose.runtime.Composable public static inline void Column(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement = Arrangement.Top, androidx.compose.ui.Alignment.Horizontal horizontalAlignment = Alignment.Start, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> children);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public final class ColumnScope {
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.VerticalAlignmentLine alignmentLine);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
- method @Deprecated @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal align);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
- field public static final androidx.compose.foundation.layout.ColumnScope INSTANCE;
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ColumnScope {
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.VerticalAlignmentLine alignmentLine);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
+ method @Deprecated @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal align);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
+ field public static final androidx.compose.foundation.layout.ColumnScope.Companion Companion;
+ }
+
+ public static final class ColumnScope.Companion implements androidx.compose.foundation.layout.ColumnScope {
}
@androidx.compose.foundation.layout.LayoutScopeMarker public final class ConstrainScope {
@@ -417,13 +420,16 @@
method @androidx.compose.runtime.Composable public static inline void Row(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement = Arrangement.Start, androidx.compose.ui.Alignment.Vertical verticalAlignment = Alignment.Top, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> children);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public final class RowScope {
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical alignment);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.HorizontalAlignmentLine alignmentLine);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
- method @Deprecated @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical align);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
- field public static final androidx.compose.foundation.layout.RowScope INSTANCE;
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface RowScope {
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical alignment);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.HorizontalAlignmentLine alignmentLine);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
+ method @Deprecated @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical align);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
+ field public static final androidx.compose.foundation.layout.RowScope.Companion Companion;
+ }
+
+ public static final class RowScope.Companion implements androidx.compose.foundation.layout.RowScope {
}
public enum SizeMode {
@@ -441,11 +447,14 @@
method @androidx.compose.runtime.Composable public static void Stack(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.Alignment alignment = Alignment.TopStart, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.StackScope,kotlin.Unit> children);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public final class StackScope {
- ctor public StackScope();
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment alignment);
- method @Deprecated @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment align);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier matchParentSize(androidx.compose.ui.Modifier);
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface StackScope {
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment alignment);
+ method @Deprecated @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment align);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier matchParentSize(androidx.compose.ui.Modifier);
+ field public static final androidx.compose.foundation.layout.StackScope.Companion Companion;
+ }
+
+ public static final class StackScope.Companion implements androidx.compose.foundation.layout.StackScope {
}
public final class State extends androidx.constraintlayout.core.state.State {
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index e2e2300..19273b4 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -88,13 +88,16 @@
field @kotlin.PublishedApi internal static final androidx.compose.ui.node.LayoutNode.MeasureBlocks DefaultColumnMeasureBlocks;
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public final class ColumnScope {
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.VerticalAlignmentLine alignmentLine);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
- method @Deprecated @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal align);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
- field public static final androidx.compose.foundation.layout.ColumnScope INSTANCE;
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ColumnScope {
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.VerticalAlignmentLine alignmentLine);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
+ method @Deprecated @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal align);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
+ field public static final androidx.compose.foundation.layout.ColumnScope.Companion Companion;
+ }
+
+ public static final class ColumnScope.Companion implements androidx.compose.foundation.layout.ColumnScope {
}
@androidx.compose.foundation.layout.LayoutScopeMarker public final class ConstrainScope {
@@ -422,13 +425,16 @@
field @kotlin.PublishedApi internal static final androidx.compose.ui.node.LayoutNode.MeasureBlocks DefaultRowMeasureBlocks;
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public final class RowScope {
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical alignment);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.HorizontalAlignmentLine alignmentLine);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
- method @Deprecated @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical align);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
- field public static final androidx.compose.foundation.layout.RowScope INSTANCE;
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface RowScope {
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical alignment);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, androidx.compose.ui.HorizontalAlignmentLine alignmentLine);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier alignWithSiblings(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
+ method @Deprecated @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical align);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float weight, boolean fill = true);
+ field public static final androidx.compose.foundation.layout.RowScope.Companion Companion;
+ }
+
+ public static final class RowScope.Companion implements androidx.compose.foundation.layout.RowScope {
}
public enum SizeMode {
@@ -446,11 +452,14 @@
method @androidx.compose.runtime.Composable public static void Stack(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.Alignment alignment = Alignment.TopStart, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.StackScope,kotlin.Unit> children);
}
- @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public final class StackScope {
- ctor public StackScope();
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment alignment);
- method @Deprecated @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment align);
- method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier matchParentSize(androidx.compose.ui.Modifier);
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface StackScope {
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment alignment);
+ method @Deprecated @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier gravity(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment align);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.Modifier matchParentSize(androidx.compose.ui.Modifier);
+ field public static final androidx.compose.foundation.layout.StackScope.Companion Companion;
+ }
+
+ public static final class StackScope.Companion implements androidx.compose.foundation.layout.StackScope {
}
public final class State extends androidx.constraintlayout.core.state.State {
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/RelativePaddingFromSamples.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/RelativePaddingFromSamples.kt
index 1f11695..81b12b4 100644
--- a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/RelativePaddingFromSamples.kt
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/RelativePaddingFromSamples.kt
@@ -22,13 +22,21 @@
import androidx.compose.foundation.text.FirstBaseline
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.platform.DensityAmbient
+import androidx.compose.ui.unit.sp
@Sampled
@Composable
fun RelativePaddingFromSample() {
+ // We want to have 30.sp distance from the top of the layout box to the baseline of the
+ // first line of text.
+ val distanceToBaseline = 30.sp
+ // We convert the 30.sp value to dps, which is required for the relativePaddingFrom API.
+ val distanceToBaselineDp = with(DensityAmbient.current) { distanceToBaseline.toDp() }
+ // The result will be a layout with 30.sp distance from the top of the layout box to the
+ // baseline of the first line of text.
Text(
text = "This is an example.",
- modifier = Modifier.relativePaddingFrom(FirstBaseline, before = 30.dp)
+ modifier = Modifier.relativePaddingFrom(FirstBaseline, before = distanceToBaselineDp)
)
}
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
index d24efd2..da3734e 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
@@ -16,9 +16,6 @@
package androidx.compose.foundation.layout
-import androidx.compose.foundation.layout.ColumnScope.alignWithSiblings
-import androidx.compose.foundation.layout.ColumnScope.weight
-import androidx.compose.foundation.layout.RowScope.alignWithSiblings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
@@ -118,7 +115,7 @@
*/
@LayoutScopeMarker
@Immutable
-object ColumnScope {
+interface ColumnScope {
/**
* Align the element horizontally within the [Column]. This alignment will have priority over
* the [Column]'s `horizontalAlignment` parameter.
@@ -195,4 +192,6 @@
fun Modifier.alignWithSiblings(
alignmentLineBlock: (Measured) -> Int
) = this.then(SiblingsAlignedModifier.WithAlignmentLineBlock(alignmentLineBlock))
+
+ companion object : ColumnScope
}
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
index 736f0dda..a9a342c 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.layout
-import androidx.compose.foundation.layout.RowScope.weight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
@@ -123,7 +122,7 @@
*/
@LayoutScopeMarker
@Immutable
-object RowScope {
+interface RowScope {
/**
* Align the element vertically within the [Row]. This alignment will have priority over the
* [Row]'s `verticalAlignment` parameter.
@@ -199,4 +198,6 @@
fun Modifier.alignWithSiblings(
alignmentLineBlock: (Measured) -> Int
) = this.then(SiblingsAlignedModifier.WithAlignmentLineBlock(alignmentLineBlock))
+
+ companion object : RowScope
}
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Stack.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Stack.kt
index f59f573..2e8a212 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Stack.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Stack.kt
@@ -51,7 +51,7 @@
alignment: Alignment = Alignment.TopStart,
children: @Composable StackScope.() -> Unit
) {
- val stackChildren: @Composable () -> Unit = { StackScope().children() }
+ val stackChildren: @Composable () -> Unit = { StackScope.children() }
Layout(stackChildren, modifier = modifier) { measurables, constraints ->
val placeables = arrayOfNulls<Placeable>(measurables.size)
@@ -102,7 +102,7 @@
*/
@LayoutScopeMarker
@Immutable
-class StackScope {
+interface StackScope {
/**
* Pull the content element to a specific [Alignment] within the [Stack]. This alignment will
* have priority over the [Stack]'s `alignment` parameter.
@@ -129,12 +129,12 @@
@Stable
fun Modifier.matchParentSize() = this.then(StretchAlignModifier)
- internal companion object {
- @Stable
- val StretchAlignModifier: ParentDataModifier = StackChildData(Alignment.Center, true)
- }
+ companion object : StackScope
}
+@Stable
+private val StretchAlignModifier: ParentDataModifier = StackChildData(Alignment.Center, true)
+
private val Measurable.stackChildData: StackChildData? get() = parentData as? StackChildData
private val Measurable.stretch: Boolean get() = stackChildData?.stretch ?: false
diff --git a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/ui/test/TestComposeWindow.kt b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
index bd0cd7e1..e8538a4 100644
--- a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
+++ b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
@@ -37,7 +37,7 @@
val surface = Surface.makeRasterN32Premul(width, height)
val canvas = surface.canvas
val component = object : Component() {}
- val owners = DesktopOwners(component = component, redraw = {})
+ val owners = DesktopOwners(component = component, invalidate = {})
fun setContent(content: @Composable () -> Unit): DesktopOwners {
val owner = DesktopOwner(owners, density)
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index 86bc5ab..8bf4929 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -12,7 +12,7 @@
method @androidx.compose.runtime.Composable public static void TopAppBar-ye6PvEY(androidx.compose.ui.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.compose.material.AppBarKt.TopAppBarElevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
}
- public final class BackdropConstants {
+ public final class BackdropScaffoldConstants {
method public float getDefaultFrontLayerElevation();
method public long getDefaultFrontLayerScrimColor();
method public androidx.compose.ui.graphics.Shape getDefaultFrontLayerShape();
@@ -23,12 +23,12 @@
property public final androidx.compose.ui.graphics.Shape DefaultFrontLayerShape;
property public final float DefaultHeaderHeight;
property public final float DefaultPeekHeight;
- field public static final androidx.compose.material.BackdropConstants INSTANCE;
+ field public static final androidx.compose.material.BackdropScaffoldConstants INSTANCE;
}
public final class BackdropScaffoldKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BackdropScaffold-n7o2bDw(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BackdropScaffoldState backdropScaffoldState = rememberBackdropState(Concealed), boolean gesturesEnabled = true, float peekHeight = BackdropConstants.DefaultPeekHeight, float headerHeight = BackdropConstants.DefaultHeaderHeight, boolean persistentAppBar = true, boolean stickyFrontLayer = true, long backLayerBackgroundColor = MaterialTheme.colors.primary, long backLayerContentColor = contentColorFor(backLayerBackgroundColor), androidx.compose.ui.graphics.Shape frontLayerShape = BackdropConstants.DefaultFrontLayerShape, float frontLayerElevation = BackdropConstants.DefaultFrontLayerElevation, long frontLayerBackgroundColor = MaterialTheme.colors.surface, long frontLayerContentColor = contentColorFor(frontLayerBackgroundColor), long frontLayerScrimColor = BackdropConstants.DefaultFrontLayerScrimColor, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit> appBar, kotlin.jvm.functions.Function0<kotlin.Unit> backLayerContent, kotlin.jvm.functions.Function0<kotlin.Unit> frontLayerContent);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BackdropScaffoldState rememberBackdropState(androidx.compose.material.BackdropValue initialValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange = { return true }, androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState());
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BackdropScaffold-n7o2bDw(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BackdropScaffoldState scaffoldState = rememberBackdropScaffoldState(Concealed), boolean gesturesEnabled = true, float peekHeight = BackdropScaffoldConstants.DefaultPeekHeight, float headerHeight = BackdropScaffoldConstants.DefaultHeaderHeight, boolean persistentAppBar = true, boolean stickyFrontLayer = true, long backLayerBackgroundColor = MaterialTheme.colors.primary, long backLayerContentColor = contentColorFor(backLayerBackgroundColor), androidx.compose.ui.graphics.Shape frontLayerShape = BackdropScaffoldConstants.DefaultFrontLayerShape, float frontLayerElevation = BackdropScaffoldConstants.DefaultFrontLayerElevation, long frontLayerBackgroundColor = MaterialTheme.colors.surface, long frontLayerContentColor = contentColorFor(frontLayerBackgroundColor), long frontLayerScrimColor = BackdropScaffoldConstants.DefaultFrontLayerScrimColor, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit> appBar, kotlin.jvm.functions.Function0<kotlin.Unit> backLayerContent, kotlin.jvm.functions.Function0<kotlin.Unit> frontLayerContent);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BackdropScaffoldState rememberBackdropScaffoldState(androidx.compose.material.BackdropValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock = AnimationClockAmbient.current, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange = { return true }, androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState());
}
@androidx.compose.material.ExperimentalMaterialApi public final class BackdropScaffoldState extends androidx.compose.material.SwipeableState<androidx.compose.material.BackdropValue> {
@@ -87,6 +87,49 @@
}), long selectedContentColor = contentColor(), long unselectedContentColor = EmphasisAmbient.current.medium.applyEmphasis(selectedContentColor));
}
+ public final class BottomSheetScaffoldConstants {
+ method public float getDefaultSheetElevation();
+ method public float getDefaultSheetPeekHeight();
+ property public final float DefaultSheetElevation;
+ property public final float DefaultSheetPeekHeight;
+ field public static final androidx.compose.material.BottomSheetScaffoldConstants INSTANCE;
+ }
+
+ public final class BottomSheetScaffoldKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold-0Ttp7_s(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BottomSheetScaffoldState scaffoldState = rememberBottomSheetScaffoldState(), kotlin.jvm.functions.Function0<kotlin.Unit>? topBar = null, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = null, androidx.compose.material.FabPosition floatingActionButtonPosition = androidx.compose.material.FabPosition.End, boolean sheetGesturesEnabled = true, androidx.compose.ui.graphics.Shape sheetShape = large, float sheetElevation = BottomSheetScaffoldConstants.DefaultSheetElevation, long sheetBackgroundColor = MaterialTheme.colors.surface, long sheetContentColor = contentColorFor(sheetBackgroundColor), float sheetPeekHeight = BottomSheetScaffoldConstants.DefaultSheetPeekHeight, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent = null, boolean drawerGesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long drawerScrimColor = DrawerConstants.defaultScrimColor, long backgroundColor = MaterialTheme.colors.background, long contentColor = contentColorFor(backgroundColor), kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> bodyContent);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetScaffoldState rememberBottomSheetScaffoldState(androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), androidx.compose.material.BottomSheetState bottomSheetState = rememberBottomSheetState(BottomSheetValue.Collapsed), androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState());
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetState rememberBottomSheetState(androidx.compose.material.BottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock = AnimationClockAmbient.current, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange = { return true });
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class BottomSheetScaffoldState {
+ ctor public BottomSheetScaffoldState(androidx.compose.material.DrawerState drawerState, androidx.compose.material.BottomSheetState bottomSheetState, androidx.compose.material.SnackbarHostState snackbarHostState);
+ method public androidx.compose.material.BottomSheetState getBottomSheetState();
+ method public androidx.compose.material.DrawerState getDrawerState();
+ method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class BottomSheetState extends androidx.compose.material.SwipeableState<androidx.compose.material.BottomSheetValue> {
+ ctor public BottomSheetState(androidx.compose.material.BottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method public void collapse(kotlin.jvm.functions.Function0<kotlin.Unit>? onCollapsed = null);
+ method public void expand(kotlin.jvm.functions.Function0<kotlin.Unit>? onExpanded = null);
+ method public boolean isCollapsed();
+ method public boolean isExpanded();
+ property public final boolean isCollapsed;
+ property public final boolean isExpanded;
+ field public static final androidx.compose.material.BottomSheetState.Companion Companion;
+ }
+
+ public static final class BottomSheetState.Companion {
+ method public androidx.compose.runtime.savedinstancestate.Saver<androidx.compose.material.BottomSheetState,?> Saver(androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum BottomSheetValue {
+ method public static androidx.compose.material.BottomSheetValue valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.compose.material.BottomSheetValue[] values();
+ enum_constant public static final androidx.compose.material.BottomSheetValue Collapsed;
+ enum_constant public static final androidx.compose.material.BottomSheetValue Expanded;
+ }
+
public final class ButtonConstants {
method @androidx.compose.runtime.Composable public androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> defaultAnimatedElevation-qDazgog(androidx.compose.foundation.InteractionState interactionState, boolean enabled, float defaultElevation = 2.dp, float pressedElevation = 8.dp, float disabledElevation = 0.dp);
method @androidx.compose.runtime.Composable public long defaultButtonBackgroundColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledBackgroundColor);
@@ -210,14 +253,16 @@
public final class DrawerConstants {
method public float getDefaultElevation();
+ method public long getDefaultScrimColor();
property public final float DefaultElevation;
+ property public final long defaultScrimColor;
field public static final androidx.compose.material.DrawerConstants INSTANCE;
field public static final float ScrimDefaultOpacity = 0.32f;
}
public final class DrawerKt {
- method @androidx.compose.runtime.Composable public static void BottomDrawerLayout--6CoO6E(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BottomDrawerState drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = MaterialTheme.colors.onSurface.copy(DrawerConstants.ScrimDefaultOpacity), kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
- method @androidx.compose.runtime.Composable public static void ModalDrawerLayout-TlzqArY(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = MaterialTheme.colors.onSurface.copy(DrawerConstants.ScrimDefaultOpacity), kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+ method @androidx.compose.runtime.Composable public static void BottomDrawerLayout--6CoO6E(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BottomDrawerState drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = DrawerConstants.defaultScrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+ method @androidx.compose.runtime.Composable public static void ModalDrawerLayout-TlzqArY(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = DrawerConstants.defaultScrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
method @androidx.compose.runtime.Composable public static androidx.compose.material.BottomDrawerState rememberBottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange = { return true });
method @androidx.compose.runtime.Composable public static androidx.compose.material.DrawerState rememberDrawerState(androidx.compose.material.DrawerValue initialValue, kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange = { return true });
}
@@ -335,6 +380,40 @@
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
+ public final class ModalBottomSheetConstants {
+ method public float getDefaultElevation();
+ method public long getDefaultScrimColor();
+ property public final float DefaultElevation;
+ property public final long DefaultScrimColor;
+ field public static final androidx.compose.material.ModalBottomSheetConstants INSTANCE;
+ }
+
+ public final class ModalBottomSheetKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ModalBottomSheetLayout-dpadJcU(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.ModalBottomSheetState sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), androidx.compose.ui.graphics.Shape sheetShape = large, float sheetElevation = ModalBottomSheetConstants.DefaultElevation, long sheetBackgroundColor = MaterialTheme.colors.surface, long sheetContentColor = contentColorFor(sheetBackgroundColor), long scrimColor = ModalBottomSheetConstants.DefaultScrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock = AnimationClockAmbient.current, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange = { return true });
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class ModalBottomSheetState extends androidx.compose.material.SwipeableState<androidx.compose.material.ModalBottomSheetValue> {
+ ctor public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method public void hide(kotlin.jvm.functions.Function0<kotlin.Unit>? onHidden = null);
+ method public boolean isVisible();
+ method public void show(kotlin.jvm.functions.Function0<kotlin.Unit>? onShown = null);
+ property public final boolean isVisible;
+ field public static final androidx.compose.material.ModalBottomSheetState.Companion Companion;
+ }
+
+ public static final class ModalBottomSheetState.Companion {
+ method public androidx.compose.runtime.savedinstancestate.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum ModalBottomSheetValue {
+ method public static androidx.compose.material.ModalBottomSheetValue valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.compose.material.ModalBottomSheetValue[] values();
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue Expanded;
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue HalfExpanded;
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue Hidden;
+ }
+
public final class OutlinedTextFieldKt {
method @androidx.compose.runtime.Composable public static void OutlinedTextField--KhY4tc(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onImeActionPerformed = { _, _ -> }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted = {}, long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error);
method @androidx.compose.runtime.Composable public static void OutlinedTextField-YV8zA4E(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onImeActionPerformed = { _, _ -> }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted = {}, long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error);
@@ -381,7 +460,7 @@
}
public final class ScaffoldKt {
- method @androidx.compose.runtime.Composable public static void Scaffold-6SxdeRQ(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.ScaffoldState scaffoldState = rememberScaffoldState(), kotlin.jvm.functions.Function0<kotlin.Unit>? topBar = null, kotlin.jvm.functions.Function0<kotlin.Unit>? bottomBar = null, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = null, androidx.compose.material.FabPosition floatingActionButtonPosition = androidx.compose.material.FabPosition.End, boolean isFloatingActionButtonDocked = false, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent = null, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long drawerScrimColor = MaterialTheme.colors.onSurface.copy(DrawerConstants.ScrimDefaultOpacity), long backgroundColor = MaterialTheme.colors.background, long contentColor = contentColorFor(backgroundColor), kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> bodyContent);
+ method @androidx.compose.runtime.Composable public static void Scaffold-6SxdeRQ(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.ScaffoldState scaffoldState = rememberScaffoldState(), kotlin.jvm.functions.Function0<kotlin.Unit>? topBar = null, kotlin.jvm.functions.Function0<kotlin.Unit>? bottomBar = null, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = null, androidx.compose.material.FabPosition floatingActionButtonPosition = androidx.compose.material.FabPosition.End, boolean isFloatingActionButtonDocked = false, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent = null, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long drawerScrimColor = DrawerConstants.defaultScrimColor, long backgroundColor = MaterialTheme.colors.background, long contentColor = contentColorFor(backgroundColor), kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> bodyContent);
method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState(), boolean isDrawerGesturesEnabled = true);
}
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index 86bc5ab..8bf4929 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -12,7 +12,7 @@
method @androidx.compose.runtime.Composable public static void TopAppBar-ye6PvEY(androidx.compose.ui.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.compose.material.AppBarKt.TopAppBarElevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
}
- public final class BackdropConstants {
+ public final class BackdropScaffoldConstants {
method public float getDefaultFrontLayerElevation();
method public long getDefaultFrontLayerScrimColor();
method public androidx.compose.ui.graphics.Shape getDefaultFrontLayerShape();
@@ -23,12 +23,12 @@
property public final androidx.compose.ui.graphics.Shape DefaultFrontLayerShape;
property public final float DefaultHeaderHeight;
property public final float DefaultPeekHeight;
- field public static final androidx.compose.material.BackdropConstants INSTANCE;
+ field public static final androidx.compose.material.BackdropScaffoldConstants INSTANCE;
}
public final class BackdropScaffoldKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BackdropScaffold-n7o2bDw(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BackdropScaffoldState backdropScaffoldState = rememberBackdropState(Concealed), boolean gesturesEnabled = true, float peekHeight = BackdropConstants.DefaultPeekHeight, float headerHeight = BackdropConstants.DefaultHeaderHeight, boolean persistentAppBar = true, boolean stickyFrontLayer = true, long backLayerBackgroundColor = MaterialTheme.colors.primary, long backLayerContentColor = contentColorFor(backLayerBackgroundColor), androidx.compose.ui.graphics.Shape frontLayerShape = BackdropConstants.DefaultFrontLayerShape, float frontLayerElevation = BackdropConstants.DefaultFrontLayerElevation, long frontLayerBackgroundColor = MaterialTheme.colors.surface, long frontLayerContentColor = contentColorFor(frontLayerBackgroundColor), long frontLayerScrimColor = BackdropConstants.DefaultFrontLayerScrimColor, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit> appBar, kotlin.jvm.functions.Function0<kotlin.Unit> backLayerContent, kotlin.jvm.functions.Function0<kotlin.Unit> frontLayerContent);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BackdropScaffoldState rememberBackdropState(androidx.compose.material.BackdropValue initialValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange = { return true }, androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState());
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BackdropScaffold-n7o2bDw(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BackdropScaffoldState scaffoldState = rememberBackdropScaffoldState(Concealed), boolean gesturesEnabled = true, float peekHeight = BackdropScaffoldConstants.DefaultPeekHeight, float headerHeight = BackdropScaffoldConstants.DefaultHeaderHeight, boolean persistentAppBar = true, boolean stickyFrontLayer = true, long backLayerBackgroundColor = MaterialTheme.colors.primary, long backLayerContentColor = contentColorFor(backLayerBackgroundColor), androidx.compose.ui.graphics.Shape frontLayerShape = BackdropScaffoldConstants.DefaultFrontLayerShape, float frontLayerElevation = BackdropScaffoldConstants.DefaultFrontLayerElevation, long frontLayerBackgroundColor = MaterialTheme.colors.surface, long frontLayerContentColor = contentColorFor(frontLayerBackgroundColor), long frontLayerScrimColor = BackdropScaffoldConstants.DefaultFrontLayerScrimColor, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit> appBar, kotlin.jvm.functions.Function0<kotlin.Unit> backLayerContent, kotlin.jvm.functions.Function0<kotlin.Unit> frontLayerContent);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BackdropScaffoldState rememberBackdropScaffoldState(androidx.compose.material.BackdropValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock = AnimationClockAmbient.current, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange = { return true }, androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState());
}
@androidx.compose.material.ExperimentalMaterialApi public final class BackdropScaffoldState extends androidx.compose.material.SwipeableState<androidx.compose.material.BackdropValue> {
@@ -87,6 +87,49 @@
}), long selectedContentColor = contentColor(), long unselectedContentColor = EmphasisAmbient.current.medium.applyEmphasis(selectedContentColor));
}
+ public final class BottomSheetScaffoldConstants {
+ method public float getDefaultSheetElevation();
+ method public float getDefaultSheetPeekHeight();
+ property public final float DefaultSheetElevation;
+ property public final float DefaultSheetPeekHeight;
+ field public static final androidx.compose.material.BottomSheetScaffoldConstants INSTANCE;
+ }
+
+ public final class BottomSheetScaffoldKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold-0Ttp7_s(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BottomSheetScaffoldState scaffoldState = rememberBottomSheetScaffoldState(), kotlin.jvm.functions.Function0<kotlin.Unit>? topBar = null, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = null, androidx.compose.material.FabPosition floatingActionButtonPosition = androidx.compose.material.FabPosition.End, boolean sheetGesturesEnabled = true, androidx.compose.ui.graphics.Shape sheetShape = large, float sheetElevation = BottomSheetScaffoldConstants.DefaultSheetElevation, long sheetBackgroundColor = MaterialTheme.colors.surface, long sheetContentColor = contentColorFor(sheetBackgroundColor), float sheetPeekHeight = BottomSheetScaffoldConstants.DefaultSheetPeekHeight, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent = null, boolean drawerGesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long drawerScrimColor = DrawerConstants.defaultScrimColor, long backgroundColor = MaterialTheme.colors.background, long contentColor = contentColorFor(backgroundColor), kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> bodyContent);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetScaffoldState rememberBottomSheetScaffoldState(androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), androidx.compose.material.BottomSheetState bottomSheetState = rememberBottomSheetState(BottomSheetValue.Collapsed), androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState());
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetState rememberBottomSheetState(androidx.compose.material.BottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock = AnimationClockAmbient.current, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange = { return true });
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class BottomSheetScaffoldState {
+ ctor public BottomSheetScaffoldState(androidx.compose.material.DrawerState drawerState, androidx.compose.material.BottomSheetState bottomSheetState, androidx.compose.material.SnackbarHostState snackbarHostState);
+ method public androidx.compose.material.BottomSheetState getBottomSheetState();
+ method public androidx.compose.material.DrawerState getDrawerState();
+ method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class BottomSheetState extends androidx.compose.material.SwipeableState<androidx.compose.material.BottomSheetValue> {
+ ctor public BottomSheetState(androidx.compose.material.BottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method public void collapse(kotlin.jvm.functions.Function0<kotlin.Unit>? onCollapsed = null);
+ method public void expand(kotlin.jvm.functions.Function0<kotlin.Unit>? onExpanded = null);
+ method public boolean isCollapsed();
+ method public boolean isExpanded();
+ property public final boolean isCollapsed;
+ property public final boolean isExpanded;
+ field public static final androidx.compose.material.BottomSheetState.Companion Companion;
+ }
+
+ public static final class BottomSheetState.Companion {
+ method public androidx.compose.runtime.savedinstancestate.Saver<androidx.compose.material.BottomSheetState,?> Saver(androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum BottomSheetValue {
+ method public static androidx.compose.material.BottomSheetValue valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.compose.material.BottomSheetValue[] values();
+ enum_constant public static final androidx.compose.material.BottomSheetValue Collapsed;
+ enum_constant public static final androidx.compose.material.BottomSheetValue Expanded;
+ }
+
public final class ButtonConstants {
method @androidx.compose.runtime.Composable public androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> defaultAnimatedElevation-qDazgog(androidx.compose.foundation.InteractionState interactionState, boolean enabled, float defaultElevation = 2.dp, float pressedElevation = 8.dp, float disabledElevation = 0.dp);
method @androidx.compose.runtime.Composable public long defaultButtonBackgroundColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledBackgroundColor);
@@ -210,14 +253,16 @@
public final class DrawerConstants {
method public float getDefaultElevation();
+ method public long getDefaultScrimColor();
property public final float DefaultElevation;
+ property public final long defaultScrimColor;
field public static final androidx.compose.material.DrawerConstants INSTANCE;
field public static final float ScrimDefaultOpacity = 0.32f;
}
public final class DrawerKt {
- method @androidx.compose.runtime.Composable public static void BottomDrawerLayout--6CoO6E(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BottomDrawerState drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = MaterialTheme.colors.onSurface.copy(DrawerConstants.ScrimDefaultOpacity), kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
- method @androidx.compose.runtime.Composable public static void ModalDrawerLayout-TlzqArY(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = MaterialTheme.colors.onSurface.copy(DrawerConstants.ScrimDefaultOpacity), kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+ method @androidx.compose.runtime.Composable public static void BottomDrawerLayout--6CoO6E(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BottomDrawerState drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = DrawerConstants.defaultScrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+ method @androidx.compose.runtime.Composable public static void ModalDrawerLayout-TlzqArY(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = DrawerConstants.defaultScrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
method @androidx.compose.runtime.Composable public static androidx.compose.material.BottomDrawerState rememberBottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange = { return true });
method @androidx.compose.runtime.Composable public static androidx.compose.material.DrawerState rememberDrawerState(androidx.compose.material.DrawerValue initialValue, kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange = { return true });
}
@@ -335,6 +380,40 @@
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
+ public final class ModalBottomSheetConstants {
+ method public float getDefaultElevation();
+ method public long getDefaultScrimColor();
+ property public final float DefaultElevation;
+ property public final long DefaultScrimColor;
+ field public static final androidx.compose.material.ModalBottomSheetConstants INSTANCE;
+ }
+
+ public final class ModalBottomSheetKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ModalBottomSheetLayout-dpadJcU(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.ModalBottomSheetState sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), androidx.compose.ui.graphics.Shape sheetShape = large, float sheetElevation = ModalBottomSheetConstants.DefaultElevation, long sheetBackgroundColor = MaterialTheme.colors.surface, long sheetContentColor = contentColorFor(sheetBackgroundColor), long scrimColor = ModalBottomSheetConstants.DefaultScrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock = AnimationClockAmbient.current, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange = { return true });
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class ModalBottomSheetState extends androidx.compose.material.SwipeableState<androidx.compose.material.ModalBottomSheetValue> {
+ ctor public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method public void hide(kotlin.jvm.functions.Function0<kotlin.Unit>? onHidden = null);
+ method public boolean isVisible();
+ method public void show(kotlin.jvm.functions.Function0<kotlin.Unit>? onShown = null);
+ property public final boolean isVisible;
+ field public static final androidx.compose.material.ModalBottomSheetState.Companion Companion;
+ }
+
+ public static final class ModalBottomSheetState.Companion {
+ method public androidx.compose.runtime.savedinstancestate.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum ModalBottomSheetValue {
+ method public static androidx.compose.material.ModalBottomSheetValue valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.compose.material.ModalBottomSheetValue[] values();
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue Expanded;
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue HalfExpanded;
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue Hidden;
+ }
+
public final class OutlinedTextFieldKt {
method @androidx.compose.runtime.Composable public static void OutlinedTextField--KhY4tc(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onImeActionPerformed = { _, _ -> }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted = {}, long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error);
method @androidx.compose.runtime.Composable public static void OutlinedTextField-YV8zA4E(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onImeActionPerformed = { _, _ -> }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted = {}, long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error);
@@ -381,7 +460,7 @@
}
public final class ScaffoldKt {
- method @androidx.compose.runtime.Composable public static void Scaffold-6SxdeRQ(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.ScaffoldState scaffoldState = rememberScaffoldState(), kotlin.jvm.functions.Function0<kotlin.Unit>? topBar = null, kotlin.jvm.functions.Function0<kotlin.Unit>? bottomBar = null, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = null, androidx.compose.material.FabPosition floatingActionButtonPosition = androidx.compose.material.FabPosition.End, boolean isFloatingActionButtonDocked = false, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent = null, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long drawerScrimColor = MaterialTheme.colors.onSurface.copy(DrawerConstants.ScrimDefaultOpacity), long backgroundColor = MaterialTheme.colors.background, long contentColor = contentColorFor(backgroundColor), kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> bodyContent);
+ method @androidx.compose.runtime.Composable public static void Scaffold-6SxdeRQ(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.ScaffoldState scaffoldState = rememberScaffoldState(), kotlin.jvm.functions.Function0<kotlin.Unit>? topBar = null, kotlin.jvm.functions.Function0<kotlin.Unit>? bottomBar = null, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = null, androidx.compose.material.FabPosition floatingActionButtonPosition = androidx.compose.material.FabPosition.End, boolean isFloatingActionButtonDocked = false, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent = null, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long drawerScrimColor = DrawerConstants.defaultScrimColor, long backgroundColor = MaterialTheme.colors.background, long contentColor = contentColorFor(backgroundColor), kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> bodyContent);
method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState(), boolean isDrawerGesturesEnabled = true);
}
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index 86bc5ab..8bf4929 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -12,7 +12,7 @@
method @androidx.compose.runtime.Composable public static void TopAppBar-ye6PvEY(androidx.compose.ui.Modifier modifier = Modifier, long backgroundColor = MaterialTheme.colors.primarySurface, long contentColor = contentColorFor(backgroundColor), float elevation = androidx.compose.material.AppBarKt.TopAppBarElevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
}
- public final class BackdropConstants {
+ public final class BackdropScaffoldConstants {
method public float getDefaultFrontLayerElevation();
method public long getDefaultFrontLayerScrimColor();
method public androidx.compose.ui.graphics.Shape getDefaultFrontLayerShape();
@@ -23,12 +23,12 @@
property public final androidx.compose.ui.graphics.Shape DefaultFrontLayerShape;
property public final float DefaultHeaderHeight;
property public final float DefaultPeekHeight;
- field public static final androidx.compose.material.BackdropConstants INSTANCE;
+ field public static final androidx.compose.material.BackdropScaffoldConstants INSTANCE;
}
public final class BackdropScaffoldKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BackdropScaffold-n7o2bDw(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BackdropScaffoldState backdropScaffoldState = rememberBackdropState(Concealed), boolean gesturesEnabled = true, float peekHeight = BackdropConstants.DefaultPeekHeight, float headerHeight = BackdropConstants.DefaultHeaderHeight, boolean persistentAppBar = true, boolean stickyFrontLayer = true, long backLayerBackgroundColor = MaterialTheme.colors.primary, long backLayerContentColor = contentColorFor(backLayerBackgroundColor), androidx.compose.ui.graphics.Shape frontLayerShape = BackdropConstants.DefaultFrontLayerShape, float frontLayerElevation = BackdropConstants.DefaultFrontLayerElevation, long frontLayerBackgroundColor = MaterialTheme.colors.surface, long frontLayerContentColor = contentColorFor(frontLayerBackgroundColor), long frontLayerScrimColor = BackdropConstants.DefaultFrontLayerScrimColor, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit> appBar, kotlin.jvm.functions.Function0<kotlin.Unit> backLayerContent, kotlin.jvm.functions.Function0<kotlin.Unit> frontLayerContent);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BackdropScaffoldState rememberBackdropState(androidx.compose.material.BackdropValue initialValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange = { return true }, androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState());
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BackdropScaffold-n7o2bDw(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BackdropScaffoldState scaffoldState = rememberBackdropScaffoldState(Concealed), boolean gesturesEnabled = true, float peekHeight = BackdropScaffoldConstants.DefaultPeekHeight, float headerHeight = BackdropScaffoldConstants.DefaultHeaderHeight, boolean persistentAppBar = true, boolean stickyFrontLayer = true, long backLayerBackgroundColor = MaterialTheme.colors.primary, long backLayerContentColor = contentColorFor(backLayerBackgroundColor), androidx.compose.ui.graphics.Shape frontLayerShape = BackdropScaffoldConstants.DefaultFrontLayerShape, float frontLayerElevation = BackdropScaffoldConstants.DefaultFrontLayerElevation, long frontLayerBackgroundColor = MaterialTheme.colors.surface, long frontLayerContentColor = contentColorFor(frontLayerBackgroundColor), long frontLayerScrimColor = BackdropScaffoldConstants.DefaultFrontLayerScrimColor, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit> appBar, kotlin.jvm.functions.Function0<kotlin.Unit> backLayerContent, kotlin.jvm.functions.Function0<kotlin.Unit> frontLayerContent);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BackdropScaffoldState rememberBackdropScaffoldState(androidx.compose.material.BackdropValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock = AnimationClockAmbient.current, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange = { return true }, androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState());
}
@androidx.compose.material.ExperimentalMaterialApi public final class BackdropScaffoldState extends androidx.compose.material.SwipeableState<androidx.compose.material.BackdropValue> {
@@ -87,6 +87,49 @@
}), long selectedContentColor = contentColor(), long unselectedContentColor = EmphasisAmbient.current.medium.applyEmphasis(selectedContentColor));
}
+ public final class BottomSheetScaffoldConstants {
+ method public float getDefaultSheetElevation();
+ method public float getDefaultSheetPeekHeight();
+ property public final float DefaultSheetElevation;
+ property public final float DefaultSheetPeekHeight;
+ field public static final androidx.compose.material.BottomSheetScaffoldConstants INSTANCE;
+ }
+
+ public final class BottomSheetScaffoldKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold-0Ttp7_s(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BottomSheetScaffoldState scaffoldState = rememberBottomSheetScaffoldState(), kotlin.jvm.functions.Function0<kotlin.Unit>? topBar = null, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = null, androidx.compose.material.FabPosition floatingActionButtonPosition = androidx.compose.material.FabPosition.End, boolean sheetGesturesEnabled = true, androidx.compose.ui.graphics.Shape sheetShape = large, float sheetElevation = BottomSheetScaffoldConstants.DefaultSheetElevation, long sheetBackgroundColor = MaterialTheme.colors.surface, long sheetContentColor = contentColorFor(sheetBackgroundColor), float sheetPeekHeight = BottomSheetScaffoldConstants.DefaultSheetPeekHeight, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent = null, boolean drawerGesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long drawerScrimColor = DrawerConstants.defaultScrimColor, long backgroundColor = MaterialTheme.colors.background, long contentColor = contentColorFor(backgroundColor), kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> bodyContent);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetScaffoldState rememberBottomSheetScaffoldState(androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), androidx.compose.material.BottomSheetState bottomSheetState = rememberBottomSheetState(BottomSheetValue.Collapsed), androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState());
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetState rememberBottomSheetState(androidx.compose.material.BottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock = AnimationClockAmbient.current, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange = { return true });
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class BottomSheetScaffoldState {
+ ctor public BottomSheetScaffoldState(androidx.compose.material.DrawerState drawerState, androidx.compose.material.BottomSheetState bottomSheetState, androidx.compose.material.SnackbarHostState snackbarHostState);
+ method public androidx.compose.material.BottomSheetState getBottomSheetState();
+ method public androidx.compose.material.DrawerState getDrawerState();
+ method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class BottomSheetState extends androidx.compose.material.SwipeableState<androidx.compose.material.BottomSheetValue> {
+ ctor public BottomSheetState(androidx.compose.material.BottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method public void collapse(kotlin.jvm.functions.Function0<kotlin.Unit>? onCollapsed = null);
+ method public void expand(kotlin.jvm.functions.Function0<kotlin.Unit>? onExpanded = null);
+ method public boolean isCollapsed();
+ method public boolean isExpanded();
+ property public final boolean isCollapsed;
+ property public final boolean isExpanded;
+ field public static final androidx.compose.material.BottomSheetState.Companion Companion;
+ }
+
+ public static final class BottomSheetState.Companion {
+ method public androidx.compose.runtime.savedinstancestate.Saver<androidx.compose.material.BottomSheetState,?> Saver(androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum BottomSheetValue {
+ method public static androidx.compose.material.BottomSheetValue valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.compose.material.BottomSheetValue[] values();
+ enum_constant public static final androidx.compose.material.BottomSheetValue Collapsed;
+ enum_constant public static final androidx.compose.material.BottomSheetValue Expanded;
+ }
+
public final class ButtonConstants {
method @androidx.compose.runtime.Composable public androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> defaultAnimatedElevation-qDazgog(androidx.compose.foundation.InteractionState interactionState, boolean enabled, float defaultElevation = 2.dp, float pressedElevation = 8.dp, float disabledElevation = 0.dp);
method @androidx.compose.runtime.Composable public long defaultButtonBackgroundColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledBackgroundColor);
@@ -210,14 +253,16 @@
public final class DrawerConstants {
method public float getDefaultElevation();
+ method public long getDefaultScrimColor();
property public final float DefaultElevation;
+ property public final long defaultScrimColor;
field public static final androidx.compose.material.DrawerConstants INSTANCE;
field public static final float ScrimDefaultOpacity = 0.32f;
}
public final class DrawerKt {
- method @androidx.compose.runtime.Composable public static void BottomDrawerLayout--6CoO6E(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BottomDrawerState drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = MaterialTheme.colors.onSurface.copy(DrawerConstants.ScrimDefaultOpacity), kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
- method @androidx.compose.runtime.Composable public static void ModalDrawerLayout-TlzqArY(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = MaterialTheme.colors.onSurface.copy(DrawerConstants.ScrimDefaultOpacity), kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+ method @androidx.compose.runtime.Composable public static void BottomDrawerLayout--6CoO6E(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.BottomDrawerState drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = DrawerConstants.defaultScrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
+ method @androidx.compose.runtime.Composable public static void ModalDrawerLayout-TlzqArY(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), boolean gesturesEnabled = true, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long scrimColor = DrawerConstants.defaultScrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> bodyContent);
method @androidx.compose.runtime.Composable public static androidx.compose.material.BottomDrawerState rememberBottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange = { return true });
method @androidx.compose.runtime.Composable public static androidx.compose.material.DrawerState rememberDrawerState(androidx.compose.material.DrawerValue initialValue, kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange = { return true });
}
@@ -335,6 +380,40 @@
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
+ public final class ModalBottomSheetConstants {
+ method public float getDefaultElevation();
+ method public long getDefaultScrimColor();
+ property public final float DefaultElevation;
+ property public final long DefaultScrimColor;
+ field public static final androidx.compose.material.ModalBottomSheetConstants INSTANCE;
+ }
+
+ public final class ModalBottomSheetKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ModalBottomSheetLayout-dpadJcU(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.ModalBottomSheetState sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), androidx.compose.ui.graphics.Shape sheetShape = large, float sheetElevation = ModalBottomSheetConstants.DefaultElevation, long sheetBackgroundColor = MaterialTheme.colors.surface, long sheetContentColor = contentColorFor(sheetBackgroundColor), long scrimColor = ModalBottomSheetConstants.DefaultScrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock = AnimationClockAmbient.current, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec = SwipeableConstants.DefaultAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange = { return true });
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class ModalBottomSheetState extends androidx.compose.material.SwipeableState<androidx.compose.material.ModalBottomSheetValue> {
+ ctor public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method public void hide(kotlin.jvm.functions.Function0<kotlin.Unit>? onHidden = null);
+ method public boolean isVisible();
+ method public void show(kotlin.jvm.functions.Function0<kotlin.Unit>? onShown = null);
+ property public final boolean isVisible;
+ field public static final androidx.compose.material.ModalBottomSheetState.Companion Companion;
+ }
+
+ public static final class ModalBottomSheetState.Companion {
+ method public androidx.compose.runtime.savedinstancestate.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationClockObservable clock, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum ModalBottomSheetValue {
+ method public static androidx.compose.material.ModalBottomSheetValue valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.compose.material.ModalBottomSheetValue[] values();
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue Expanded;
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue HalfExpanded;
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue Hidden;
+ }
+
public final class OutlinedTextFieldKt {
method @androidx.compose.runtime.Composable public static void OutlinedTextField--KhY4tc(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onImeActionPerformed = { _, _ -> }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted = {}, long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error);
method @androidx.compose.runtime.Composable public static void OutlinedTextField-YV8zA4E(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onImeActionPerformed = { _, _ -> }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted = {}, long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error);
@@ -381,7 +460,7 @@
}
public final class ScaffoldKt {
- method @androidx.compose.runtime.Composable public static void Scaffold-6SxdeRQ(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.ScaffoldState scaffoldState = rememberScaffoldState(), kotlin.jvm.functions.Function0<kotlin.Unit>? topBar = null, kotlin.jvm.functions.Function0<kotlin.Unit>? bottomBar = null, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = null, androidx.compose.material.FabPosition floatingActionButtonPosition = androidx.compose.material.FabPosition.End, boolean isFloatingActionButtonDocked = false, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent = null, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long drawerScrimColor = MaterialTheme.colors.onSurface.copy(DrawerConstants.ScrimDefaultOpacity), long backgroundColor = MaterialTheme.colors.background, long contentColor = contentColorFor(backgroundColor), kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> bodyContent);
+ method @androidx.compose.runtime.Composable public static void Scaffold-6SxdeRQ(androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.material.ScaffoldState scaffoldState = rememberScaffoldState(), kotlin.jvm.functions.Function0<kotlin.Unit>? topBar = null, kotlin.jvm.functions.Function0<kotlin.Unit>? bottomBar = null, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost = { SnackbarHost(it) }, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = null, androidx.compose.material.FabPosition floatingActionButtonPosition = androidx.compose.material.FabPosition.End, boolean isFloatingActionButtonDocked = false, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent = null, androidx.compose.ui.graphics.Shape drawerShape = large, float drawerElevation = DrawerConstants.DefaultElevation, long drawerBackgroundColor = MaterialTheme.colors.surface, long drawerContentColor = contentColorFor(drawerBackgroundColor), long drawerScrimColor = DrawerConstants.defaultScrimColor, long backgroundColor = MaterialTheme.colors.background, long contentColor = contentColorFor(backgroundColor), kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> bodyContent);
method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(androidx.compose.material.DrawerState drawerState = rememberDrawerState(DrawerValue.Closed), androidx.compose.material.SnackbarHostState snackbarHostState = androidx.compose.material.SnackbarHostState(), boolean isDrawerGesturesEnabled = true);
}
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt
index 6bcaecd..6bf3ef2 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt
@@ -20,11 +20,13 @@
import androidx.compose.integration.demos.common.ComposableDemo
import androidx.compose.integration.demos.common.DemoCategory
import androidx.compose.material.samples.AlertDialogSample
-import androidx.compose.material.samples.BackdropSample
+import androidx.compose.material.samples.BackdropScaffoldSample
import androidx.compose.material.samples.BottomDrawerSample
import androidx.compose.material.samples.CustomAlertDialogSample
import androidx.compose.material.samples.EmphasisSample
+import androidx.compose.material.samples.ModalBottomSheetSample
import androidx.compose.material.samples.ModalDrawerSample
+import androidx.compose.material.samples.BottomSheetScaffoldSample
import androidx.compose.material.samples.ScaffoldWithBottomBarAndCutout
import androidx.compose.material.samples.ScaffoldWithCoroutinesSnackbar
@@ -34,8 +36,9 @@
ComposableDemo("Custom buttons") { CustomAlertDialogSample() }
)),
ComposableDemo("App Bars") { AppBarDemo() },
- ComposableDemo("Backdrop") { BackdropSample() },
+ ComposableDemo("Backdrop") { BackdropScaffoldSample() },
ComposableDemo("Bottom Navigation") { BottomNavigationDemo() },
+ ComposableDemo("Bottom Sheet") { BottomSheetScaffoldSample() },
ComposableDemo("Buttons & FABs") { ButtonDemo() },
DemoCategory("Navigation drawer", listOf(
ComposableDemo("Modal drawer") { ModalDrawerSample() },
@@ -55,6 +58,7 @@
ActivityDemo("Dynamic Theme", DynamicThemeActivity::class)
)
),
+ ComposableDemo("Modal bottom sheet") { ModalBottomSheetSample() },
ComposableDemo("Progress Indicators") { ProgressIndicatorDemo() },
ComposableDemo("Scaffold") { ScaffoldWithBottomBarAndCutout() },
ComposableDemo("Selection Controls") { SelectionControlsDemo() },
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 6e4dc36..28f0b72 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
@@ -21,7 +21,7 @@
import androidx.compose.foundation.ScrollableColumn
import androidx.compose.foundation.Text
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ColumnScope.align
+import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -233,7 +233,7 @@
}
@Composable
-private fun Title(title: String) {
+private fun ColumnScope.Title(title: String) {
Text(
text = title,
style = MaterialTheme.typography.body1,
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt
index 333cfc84..098fd3d 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt
@@ -25,7 +25,7 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumnFor
import androidx.compose.material.BackdropScaffold
-import androidx.compose.material.BackdropValue.Concealed
+import androidx.compose.material.BackdropValue
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.IconButton
import androidx.compose.material.ListItem
@@ -34,7 +34,7 @@
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Menu
-import androidx.compose.material.rememberBackdropState
+import androidx.compose.material.rememberBackdropScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -49,70 +49,22 @@
@Sampled
@Composable
@OptIn(ExperimentalMaterialApi::class)
-fun BackdropSample() {
- val selection = remember { mutableStateOf(1) }
- val backdropScaffoldState = rememberBackdropState(Concealed)
- BackdropScaffold(
- backdropScaffoldState = backdropScaffoldState,
- appBar = {
- TopAppBar(
- title = { Text("Backdrop") },
- navigationIcon = {
- if (backdropScaffoldState.isConcealed) {
- IconButton(onClick = { backdropScaffoldState.reveal() }) {
- Icon(Icons.Default.Menu)
- }
- } else {
- IconButton(onClick = { backdropScaffoldState.conceal() }) {
- Icon(Icons.Default.Close)
- }
- }
- },
- elevation = 0.dp,
- backgroundColor = Color.Transparent
- )
- },
- backLayerContent = {
- LazyColumnFor((1..5).toList()) {
- ListItem(
- Modifier.clickable {
- selection.value = it
- backdropScaffoldState.conceal()
- },
- text = { Text("Select $it") }
- )
- }
- },
- frontLayerContent = {
- Box(
- Modifier.fillMaxSize(),
- gravity = ContentGravity.Center
- ) {
- Text("Selection: ${selection.value}")
- }
- }
- )
-}
-
-@Sampled
-@Composable
-@OptIn(ExperimentalMaterialApi::class)
-fun BackdropWithSnackbarSample() {
+fun BackdropScaffoldSample() {
val scope = rememberCoroutineScope()
val selection = remember { mutableStateOf(1) }
- val backdropScaffoldState = rememberBackdropState(Concealed)
+ val scaffoldState = rememberBackdropScaffoldState(BackdropValue.Concealed)
BackdropScaffold(
- backdropScaffoldState = backdropScaffoldState,
+ scaffoldState = scaffoldState,
appBar = {
TopAppBar(
- title = { Text("Backdrop") },
+ title = { Text("Backdrop scaffold") },
navigationIcon = {
- if (backdropScaffoldState.isConcealed) {
- IconButton(onClick = { backdropScaffoldState.reveal() }) {
+ if (scaffoldState.isConcealed) {
+ IconButton(onClick = { scaffoldState.reveal() }) {
Icon(Icons.Default.Menu)
}
} else {
- IconButton(onClick = { backdropScaffoldState.conceal() }) {
+ IconButton(onClick = { scaffoldState.conceal() }) {
Icon(Icons.Default.Close)
}
}
@@ -122,7 +74,7 @@
IconButton(onClick = {
// show snackbar as a suspend function
scope.launch {
- backdropScaffoldState.snackbarHostState
+ scaffoldState.snackbarHostState
.showSnackbar("Snackbar #${++clickCount}")
}
}) {
@@ -138,7 +90,7 @@
ListItem(
Modifier.clickable {
selection.value = it
- backdropScaffoldState.conceal()
+ scaffoldState.conceal()
},
text = { Text("Select $it") }
)
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomSheetScaffoldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomSheetScaffoldSamples.kt
new file mode 100644
index 0000000..8f5a7b8
--- /dev/null
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomSheetScaffoldSamples.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.material.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.Box
+import androidx.compose.foundation.ContentGravity
+import androidx.compose.foundation.Icon
+import androidx.compose.foundation.ScrollableColumn
+import androidx.compose.foundation.Text
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.material.BottomSheetScaffold
+import androidx.compose.material.Button
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.FabPosition
+import androidx.compose.material.FloatingActionButton
+import androidx.compose.material.IconButton
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.Menu
+import androidx.compose.material.rememberBottomSheetScaffoldState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.launch
+
+private val colors = listOf(
+ Color(0xFFffd7d7.toInt()),
+ Color(0xFFffe9d6.toInt()),
+ Color(0xFFfffbd0.toInt()),
+ Color(0xFFe3ffd9.toInt()),
+ Color(0xFFd0fff8.toInt())
+)
+
+@Sampled
+@Composable
+@OptIn(ExperimentalMaterialApi::class)
+fun BottomSheetScaffoldSample() {
+ val scope = rememberCoroutineScope()
+ val scaffoldState = rememberBottomSheetScaffoldState()
+ BottomSheetScaffold(
+ sheetContent = {
+ Box(
+ Modifier.fillMaxWidth().preferredHeight(128.dp),
+ gravity = ContentGravity.Center
+ ) {
+ Text("Swipe up to expand sheet")
+ }
+ Column(
+ Modifier.fillMaxWidth().padding(64.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text("Sheet content")
+ Spacer(Modifier.preferredHeight(20.dp))
+ Button(onClick = { scaffoldState.bottomSheetState.collapse() }) {
+ Text("Click to collapse sheet")
+ }
+ }
+ },
+ scaffoldState = scaffoldState,
+ topBar = {
+ TopAppBar(
+ title = { Text("Bottom sheet scaffold") },
+ navigationIcon = {
+ IconButton(onClick = { scaffoldState.drawerState.open() }) {
+ Icon(Icons.Default.Menu)
+ }
+ }
+ )
+ },
+ floatingActionButton = {
+ var clickCount by remember { mutableStateOf(0) }
+ FloatingActionButton(onClick = {
+ // show snackbar as a suspend function
+ scope.launch {
+ scaffoldState.snackbarHostState.showSnackbar("Snackbar #${++clickCount}")
+ }
+ }) {
+ Icon(Icons.Default.Favorite)
+ }
+ },
+ floatingActionButtonPosition = FabPosition.End,
+ sheetPeekHeight = 128.dp,
+ drawerContent = {
+ Column(
+ Modifier.fillMaxWidth().padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text("Drawer content")
+ Spacer(Modifier.preferredHeight(20.dp))
+ Button(onClick = { scaffoldState.drawerState.close() }) {
+ Text("Click to close drawer")
+ }
+ }
+ }
+ ) { innerPadding ->
+ ScrollableColumn(contentPadding = innerPadding) {
+ repeat(100) {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .preferredHeight(50.dp)
+ .background(colors[it % colors.size])
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt
new file mode 100644
index 0000000..144cf53
--- /dev/null
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.material.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.Icon
+import androidx.compose.foundation.Text
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.material.Button
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.ListItem
+import androidx.compose.material.ModalBottomSheetLayout
+import androidx.compose.material.ModalBottomSheetValue
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Sampled
+@Composable
+@OptIn(ExperimentalMaterialApi::class)
+fun ModalBottomSheetSample() {
+ val state = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
+ ModalBottomSheetLayout(
+ sheetState = state,
+ sheetContent = {
+ for (i in 1..5) {
+ ListItem(
+ text = { Text("Item $i") },
+ icon = { Icon(Icons.Default.Favorite) }
+ )
+ }
+ }
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize().padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text("Rest of the UI")
+ Spacer(Modifier.preferredHeight(20.dp))
+ Button(onClick = { state.show() }) {
+ Text("Click to show sheet")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt
index 1f81634..48cfeb0 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt
@@ -71,7 +71,7 @@
fun backdropScaffold_testOffset_whenConcealed() {
rule.setContent {
BackdropScaffold(
- backdropScaffoldState = rememberBackdropState(Concealed),
+ scaffoldState = rememberBackdropScaffoldState(Concealed),
peekHeight = peekHeight,
headerHeight = headerHeight,
appBar = { Box(Modifier.preferredHeight(peekHeight)) },
@@ -88,7 +88,7 @@
fun backdropScaffold_testOffset_whenRevealed() {
rule.setContent {
BackdropScaffold(
- backdropScaffoldState = rememberBackdropState(Revealed),
+ scaffoldState = rememberBackdropScaffoldState(Revealed),
peekHeight = peekHeight,
headerHeight = headerHeight,
appBar = { Box(Modifier.preferredHeight(peekHeight)) },
@@ -105,7 +105,7 @@
fun backdropScaffold_testOffset_whenRevealed_backContentTooLarge() {
rule.setContent {
BackdropScaffold(
- backdropScaffoldState = rememberBackdropState(Revealed),
+ scaffoldState = rememberBackdropScaffoldState(Revealed),
peekHeight = peekHeight,
headerHeight = headerHeight,
appBar = { Box(Modifier.preferredHeight(peekHeight)) },
@@ -122,7 +122,7 @@
fun backdropScaffold_testOffset_whenRevealed_nonPersistentAppBar() {
rule.setContent {
BackdropScaffold(
- backdropScaffoldState = rememberBackdropState(Revealed),
+ scaffoldState = rememberBackdropScaffoldState(Revealed),
peekHeight = peekHeight,
headerHeight = headerHeight,
persistentAppBar = false,
@@ -140,7 +140,7 @@
fun backdropScaffold_testOffset_whenRevealed_nonStickyFrontLayer() {
rule.setContent {
BackdropScaffold(
- backdropScaffoldState = rememberBackdropState(Revealed),
+ scaffoldState = rememberBackdropScaffoldState(Revealed),
peekHeight = peekHeight,
headerHeight = headerHeight,
stickyFrontLayer = false,
@@ -156,10 +156,10 @@
@Test
fun backdropScaffold_revealAndConceal_manually() {
- val backdropState = BackdropScaffoldState(Concealed, clock = clock)
+ val scaffoldState = BackdropScaffoldState(Concealed, clock = clock)
rule.setContent {
BackdropScaffold(
- backdropScaffoldState = backdropState,
+ scaffoldState = scaffoldState,
peekHeight = peekHeight,
headerHeight = headerHeight,
appBar = { Box(Modifier.preferredHeight(peekHeight)) },
@@ -172,7 +172,7 @@
.assertTopPositionInRootIsEqualTo(peekHeight)
rule.runOnIdle {
- backdropState.reveal()
+ scaffoldState.reveal()
}
advanceClock()
@@ -181,7 +181,7 @@
.assertTopPositionInRootIsEqualTo(peekHeight + contentHeight)
rule.runOnIdle {
- backdropState.conceal()
+ scaffoldState.conceal()
}
advanceClock()
@@ -192,10 +192,10 @@
@Test
fun backdropScaffold_revealBySwiping() {
- val backdropState = BackdropScaffoldState(Concealed, clock)
+ val scaffoldState = BackdropScaffoldState(Concealed, clock)
rule.setContent {
BackdropScaffold(
- backdropScaffoldState = backdropState,
+ scaffoldState = scaffoldState,
peekHeight = peekHeight,
headerHeight = headerHeight,
appBar = { Box(Modifier.preferredHeight(peekHeight)) },
@@ -205,7 +205,7 @@
}
rule.runOnIdle {
- assertThat(backdropState.value).isEqualTo(Concealed)
+ assertThat(scaffoldState.value).isEqualTo(Concealed)
}
rule.onNodeWithTag(frontLayer)
@@ -214,16 +214,16 @@
advanceClock()
rule.runOnIdle {
- assertThat(backdropState.value).isEqualTo(Revealed)
+ assertThat(scaffoldState.value).isEqualTo(Revealed)
}
}
@Test
fun backdropScaffold_concealByTapingOnFrontLayer() {
- val backdropState = BackdropScaffoldState(Revealed, clock)
+ val scaffoldState = BackdropScaffoldState(Revealed, clock)
rule.setContent {
BackdropScaffold(
- backdropScaffoldState = backdropState,
+ scaffoldState = scaffoldState,
peekHeight = peekHeight,
headerHeight = headerHeight,
frontLayerScrimColor = Color.Red,
@@ -234,7 +234,7 @@
}
rule.runOnIdle {
- assertThat(backdropState.value).isEqualTo(Revealed)
+ assertThat(scaffoldState.value).isEqualTo(Revealed)
}
rule.onNodeWithTag(frontLayer)
@@ -243,17 +243,17 @@
advanceClock()
rule.runOnIdle {
- assertThat(backdropState.value).isEqualTo(Concealed)
+ assertThat(scaffoldState.value).isEqualTo(Concealed)
}
}
@Test
fun backdropScaffold_scrimIsDisabledWhenTransparent() {
var frontLayerClicks = 0
- val backdropState = BackdropScaffoldState(Revealed, clock)
+ val scaffoldState = BackdropScaffoldState(Revealed, clock)
rule.setContent {
BackdropScaffold(
- backdropScaffoldState = backdropState,
+ scaffoldState = scaffoldState,
peekHeight = peekHeight,
headerHeight = headerHeight,
frontLayerScrimColor = Color.Transparent,
@@ -269,7 +269,7 @@
rule.runOnIdle {
assertThat(frontLayerClicks).isEqualTo(0)
- assertThat(backdropState.value).isEqualTo(Revealed)
+ assertThat(scaffoldState.value).isEqualTo(Revealed)
}
rule.onNodeWithTag(frontLayer)
@@ -279,7 +279,7 @@
rule.runOnIdle {
assertThat(frontLayerClicks).isEqualTo(1)
- assertThat(backdropState.value).isEqualTo(Revealed)
+ assertThat(scaffoldState.value).isEqualTo(Revealed)
}
}
}
\ No newline at end of file
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
new file mode 100644
index 0000000..2e559fa
--- /dev/null
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
@@ -0,0 +1,346 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.material
+
+import androidx.compose.animation.core.ManualAnimationClock
+import androidx.compose.foundation.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.runtime.emptyContent
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.MediumTest
+import androidx.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.ui.test.createComposeRule
+import androidx.ui.test.onNodeWithTag
+import androidx.ui.test.performGesture
+import androidx.ui.test.swipeDown
+import androidx.ui.test.swipeUp
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@MediumTest
+@RunWith(JUnit4::class)
+@OptIn(ExperimentalMaterialApi::class)
+class ModalBottomSheetTest {
+
+ @get:Rule
+ val rule = createComposeRule(disableTransitions = true)
+
+ private val sheetHeight = 256.dp
+ private val sheetTag = "sheetContentTag"
+
+ private lateinit var clock: ManualAnimationClock
+
+ private fun advanceClock() {
+ clock.clockTimeMillis += 100000L
+ }
+
+ @Before
+ fun init() {
+ clock = ManualAnimationClock(initTimeMillis = 0L)
+ }
+
+ @Test
+ fun modalBottomSheet_testOffset_whenHidden() {
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .preferredHeight(sheetHeight)
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ val height = rule.rootHeight()
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height)
+ }
+
+ @Test
+ fun modalBottomSheet_testOffset_whenExpanded() {
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Expanded),
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .preferredHeight(sheetHeight)
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ val height = rule.rootHeight()
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height - sheetHeight)
+ }
+
+ @Test
+ fun modalBottomSheet_testOffset_tallBottomSheet_whenHidden() {
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ val height = rule.rootHeight()
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height)
+ }
+
+ @Test
+ fun modalBottomSheet_testOffset_tallBottomSheet_whenExpanded() {
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Expanded),
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun modalBottomSheet_testOffset_tallBottomSheet_whenHalfExpanded() {
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.HalfExpanded),
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ val height = rule.rootHeight()
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height / 2)
+ }
+
+ @Test
+ fun modalBottomSheet_showAndHide_manually() {
+ val sheetState = ModalBottomSheetState(ModalBottomSheetValue.Hidden, clock)
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = sheetState,
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .preferredHeight(sheetHeight)
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ val height = rule.rootHeight()
+
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height)
+
+ rule.runOnIdle {
+ sheetState.show()
+ }
+
+ advanceClock()
+
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height - sheetHeight)
+
+ rule.runOnIdle {
+ sheetState.hide()
+ }
+
+ advanceClock()
+
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height)
+ }
+
+ @Test
+ fun modalBottomSheet_showAndHide_manually_tallBottomSheet() {
+ val sheetState = ModalBottomSheetState(ModalBottomSheetValue.Hidden, clock)
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = sheetState,
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ val height = rule.rootHeight()
+
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height)
+
+ rule.runOnIdle {
+ sheetState.show()
+ }
+
+ advanceClock()
+
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height / 2)
+
+ rule.runOnIdle {
+ sheetState.hide()
+ }
+
+ advanceClock()
+
+ rule.onNodeWithTag(sheetTag)
+ .assertTopPositionInRootIsEqualTo(height)
+ }
+
+ @Test
+ fun modalBottomSheet_hideBySwiping() {
+ val sheetState = ModalBottomSheetState(ModalBottomSheetValue.Expanded, clock)
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = sheetState,
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .preferredHeight(sheetHeight)
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ rule.runOnIdle {
+ assertThat(sheetState.value).isEqualTo(ModalBottomSheetValue.Expanded)
+ }
+
+ rule.onNodeWithTag(sheetTag)
+ .performGesture { swipeDown() }
+
+ advanceClock()
+
+ rule.runOnIdle {
+ assertThat(sheetState.value).isEqualTo(ModalBottomSheetValue.Hidden)
+ }
+ }
+
+ @Test
+ fun modalBottomSheet_hideBySwiping_tallBottomSheet() {
+ val sheetState = ModalBottomSheetState(ModalBottomSheetValue.Expanded, clock)
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = sheetState,
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ rule.runOnIdle {
+ assertThat(sheetState.value).isEqualTo(ModalBottomSheetValue.Expanded)
+ }
+
+ rule.onNodeWithTag(sheetTag)
+ .performGesture { swipeDown() }
+
+ advanceClock()
+
+ rule.runOnIdle {
+ assertThat(sheetState.value).isEqualTo(ModalBottomSheetValue.Hidden)
+ }
+ }
+
+ @Test
+ fun modalBottomSheet_expandBySwiping() {
+ val sheetState = ModalBottomSheetState(ModalBottomSheetValue.HalfExpanded, clock)
+ rule.setMaterialContent {
+ ModalBottomSheetLayout(
+ sheetState = sheetState,
+ content = emptyContent(),
+ sheetContent = {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag(sheetTag)
+ )
+ }
+ )
+ }
+
+ rule.runOnIdle {
+ assertThat(sheetState.value).isEqualTo(ModalBottomSheetValue.HalfExpanded)
+ }
+
+ rule.onNodeWithTag(sheetTag)
+ .performGesture { swipeUp() }
+
+ advanceClock()
+
+ rule.runOnIdle {
+ assertThat(sheetState.value).isEqualTo(ModalBottomSheetValue.Expanded)
+ }
+ }
+}
\ No newline at end of file
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
index e8deecf..9290b83 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
@@ -80,7 +80,7 @@
* @param clock The animation clock that will be used to drive the animations.
* @param animationSpec The default animation that will be used to animate to a new state.
* @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
- * @param snackbarHostState The [SnackbarHostState] used to show snackbars inside the backdrop.
+ * @param snackbarHostState The [SnackbarHostState] used to show snackbars inside the scaffold.
*/
@ExperimentalMaterialApi
class BackdropScaffoldState(
@@ -161,23 +161,25 @@
* Create and [remember] a [BackdropScaffoldState] with the default animation clock.
*
* @param initialValue The initial value of the state.
+ * @param clock The animation clock that will be used to drive the animations.
* @param animationSpec The default animation that will be used to animate to a new state.
* @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
- * @param snackbarHostState The [SnackbarHostState] used to show snackbars inside the backdrop.
+ * @param snackbarHostState The [SnackbarHostState] used to show snackbars inside the scaffold.
*/
@Composable
@ExperimentalMaterialApi
-fun rememberBackdropState(
+fun rememberBackdropScaffoldState(
initialValue: BackdropValue,
+ clock: AnimationClockObservable = AnimationClockAmbient.current,
animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
confirmStateChange: (BackdropValue) -> Boolean = { true },
snackbarHostState: SnackbarHostState = SnackbarHostState()
): BackdropScaffoldState {
- val clock = AnimationClockAmbient.current.asDisposableClock()
+ val disposableClock = clock.asDisposableClock()
return rememberSavedInstanceState(
- clock,
+ disposableClock,
saver = BackdropScaffoldState.Saver(
- clock = clock,
+ clock = disposableClock,
animationSpec = animationSpec,
confirmStateChange = confirmStateChange,
snackbarHostState = snackbarHostState
@@ -185,7 +187,7 @@
) {
BackdropScaffoldState(
initialValue = initialValue,
- clock = clock,
+ clock = disposableClock,
animationSpec = animationSpec,
confirmStateChange = confirmStateChange,
snackbarHostState = snackbarHostState
@@ -201,6 +203,8 @@
* This component provides an API to put together several material components to construct your
* screen. For a similar component which implements the basic material design layout strategy
* with app bars, floating action buttons and navigation drawers, use the standard [Scaffold].
+ * For similar component that uses a bottom sheet as the centerpiece of the screen, use
+ * [BottomSheetScaffold].
*
* Either the back layer or front layer can be active at a time. When the front layer is active,
* it sits at an offset below the top of the screen. This is the [peekHeight] and defaults to
@@ -210,21 +214,18 @@
* To switch between the back layer and front layer, you can either swipe on the front layer if
* [gesturesEnabled] is set to `true` or use any of the methods in [BackdropScaffoldState].
*
- * The backdrop also contains an app bar, which by default is placed above the back layer's
+ * The scaffold also contains an app bar, which by default is placed above the back layer's
* content. If [persistentAppBar] is set to `false`, then the backdrop will not show the app bar
* when the back layer is revealed; instead it will switch between the app bar and the provided
* content with an animation. For best results, the [peekHeight] should match the app bar height.
+ * To show a snackbar, use the method `showSnackbar` of [BackdropScaffoldState.snackbarHostState].
*
- * A simple example of a backdrop looks like this:
+ * A simple example of a backdrop scaffold looks like this:
*
- * @sample androidx.compose.material.samples.BackdropSample
+ * @sample androidx.compose.material.samples.BackdropScaffoldSample
*
- * To show a snackbar, use [BackdropScaffoldState.snackbarHostState]:
- *
- * @sample androidx.compose.material.samples.BackdropWithSnackbarSample
- *
- * @param modifier Optional [Modifier] for the entire backdrop.
- * @param backdropScaffoldState The state of the backdrop.
+ * @param modifier Optional [Modifier] for the root of the scaffold.
+ * @param scaffoldState The state of the scaffold.
* @param gesturesEnabled Whether or not the backdrop can be interacted with by gestures.
* @param peekHeight The height of the visible part of the back layer when it is concealed.
* @param headerHeight The minimum height of the front layer when it is inactive.
@@ -245,7 +246,7 @@
* @param frontLayerScrimColor The color of the scrim applied to the front layer when the back
* layer is revealed. If you set this to `Color.Transparent`, then a scrim will not be applied
* and interaction with the front layer will not be blocked when the back layer is revealed.
- * @param snackbarHost The component hosting the snackbars shown inside the backdrop.
+ * @param snackbarHost The component hosting the snackbars shown inside the scaffold.
* @param appBar App bar for the back layer. Make sure that the [peekHeight] is equal to the
* height of the app bar, so that the app bar is fully visible. Consider using [TopAppBar] but
* set the elevation to 0dp and background color to transparent as a surface is already provided.
@@ -256,19 +257,19 @@
@ExperimentalMaterialApi
fun BackdropScaffold(
modifier: Modifier = Modifier,
- backdropScaffoldState: BackdropScaffoldState = rememberBackdropState(Concealed),
+ scaffoldState: BackdropScaffoldState = rememberBackdropScaffoldState(Concealed),
gesturesEnabled: Boolean = true,
- peekHeight: Dp = BackdropConstants.DefaultPeekHeight,
- headerHeight: Dp = BackdropConstants.DefaultHeaderHeight,
+ peekHeight: Dp = BackdropScaffoldConstants.DefaultPeekHeight,
+ headerHeight: Dp = BackdropScaffoldConstants.DefaultHeaderHeight,
persistentAppBar: Boolean = true,
stickyFrontLayer: Boolean = true,
backLayerBackgroundColor: Color = MaterialTheme.colors.primary,
backLayerContentColor: Color = contentColorFor(backLayerBackgroundColor),
- frontLayerShape: Shape = BackdropConstants.DefaultFrontLayerShape,
- frontLayerElevation: Dp = BackdropConstants.DefaultFrontLayerElevation,
+ frontLayerShape: Shape = BackdropScaffoldConstants.DefaultFrontLayerShape,
+ frontLayerElevation: Dp = BackdropScaffoldConstants.DefaultFrontLayerElevation,
frontLayerBackgroundColor: Color = MaterialTheme.colors.surface,
frontLayerContentColor: Color = contentColorFor(frontLayerBackgroundColor),
- frontLayerScrimColor: Color = BackdropConstants.DefaultFrontLayerScrimColor,
+ frontLayerScrimColor: Color = BackdropScaffoldConstants.DefaultFrontLayerScrimColor,
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
appBar: @Composable () -> Unit,
backLayerContent: @Composable () -> Unit,
@@ -284,7 +285,7 @@
backLayerContent()
}
} else {
- BackLayerTransition(backdropScaffoldState.targetValue, appBar, backLayerContent)
+ BackLayerTransition(scaffoldState.targetValue, appBar, backLayerContent)
}
}
val calculateBackLayerConstraints: (Constraints) -> Constraints = {
@@ -296,7 +297,7 @@
color = backLayerBackgroundColor,
contentColor = backLayerContentColor
) {
- WithBackLayerHeight(
+ BackdropStack(
modifier.fillMaxSize(),
backLayer,
calculateBackLayerConstraints
@@ -308,7 +309,7 @@
}
val swipeable = Modifier.swipeable(
- state = backdropScaffoldState,
+ state = scaffoldState,
anchors = mapOf(
peekHeightPx to Concealed,
revealedHeight to Revealed
@@ -320,7 +321,7 @@
// Front layer
Surface(
- Modifier.offsetPx(y = backdropScaffoldState.offset).then(swipeable),
+ Modifier.offsetPx(y = scaffoldState.offset).then(swipeable),
shape = frontLayerShape,
elevation = frontLayerElevation,
color = frontLayerBackgroundColor,
@@ -331,8 +332,8 @@
Scrim(
color = frontLayerScrimColor,
- onDismiss = { backdropScaffoldState.conceal() },
- visible = backdropScaffoldState.targetValue == Revealed
+ onDismiss = { scaffoldState.conceal() },
+ visible = scaffoldState.targetValue == Revealed
)
}
}
@@ -341,10 +342,10 @@
Box(
Modifier.zIndex(Float.POSITIVE_INFINITY),
gravity = ContentGravity.BottomCenter,
- paddingBottom = if (backdropScaffoldState.isRevealed &&
+ paddingBottom = if (scaffoldState.isRevealed &&
revealedHeight == fullHeight - headerHeightPx) headerHeight else 0.dp
) {
- snackbarHost(backdropScaffoldState.snackbarHostState)
+ snackbarHost(scaffoldState.snackbarHostState)
}
}
}
@@ -409,12 +410,9 @@
}
}
-/**
- * A composable that defines its own content according to the height of one of its children.
- */
@Composable
@OptIn(ExperimentalSubcomposeLayoutApi::class)
-private fun WithBackLayerHeight(
+private fun BackdropStack(
modifier: Modifier,
backLayer: @Composable () -> Unit,
calculateBackLayerConstraints: (Constraints) -> Constraints,
@@ -451,7 +449,7 @@
/**
* Contains useful constants for [BackdropScaffold].
*/
-object BackdropConstants {
+object BackdropScaffoldConstants {
/**
* The default peek height of the back layer.
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
new file mode 100644
index 0000000..0a6fe8f
--- /dev/null
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
@@ -0,0 +1,430 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.material
+
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.asDisposableClock
+import androidx.compose.animation.core.AnimationClockObservable
+import androidx.compose.animation.core.AnimationEndReason
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Stack
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.savedinstancestate.Saver
+import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Layout
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.WithConstraints
+import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.onPositioned
+import androidx.compose.ui.platform.AnimationClockAmbient
+import androidx.compose.ui.platform.DensityAmbient
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
+import kotlin.math.roundToInt
+
+/**
+ * Possible values of [BottomSheetState].
+ */
+@ExperimentalMaterialApi
+enum class BottomSheetValue {
+ /**
+ * The bottom sheet is visible, but only showing its peek height.
+ */
+ Collapsed,
+
+ /**
+ * The bottom sheet is visible at its maximum height.
+ */
+ Expanded
+}
+
+/**
+ * State of the persistent bottom sheet in [BottomSheetScaffold].
+ *
+ * @param initialValue The initial value of the state.
+ * @param clock The animation clock that will be used to drive the animations.
+ * @param animationSpec The default animation that will be used to animate to a new state.
+ * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
+ */
+@ExperimentalMaterialApi
+class BottomSheetState(
+ initialValue: BottomSheetValue,
+ clock: AnimationClockObservable,
+ animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+ confirmStateChange: (BottomSheetValue) -> Boolean = { true }
+) : SwipeableState<BottomSheetValue>(
+ initialValue = initialValue,
+ clock = clock,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+) {
+ /**
+ * Whether the bottom sheet is expanded.
+ */
+ val isExpanded: Boolean
+ get() = value == BottomSheetValue.Expanded
+
+ /**
+ * Whether the bottom sheet is collapsed.
+ */
+ val isCollapsed: Boolean
+ get() = value == BottomSheetValue.Collapsed
+
+ /**
+ * Expand the bottom sheet, with an animation.
+ *
+ * @param onExpanded Optional callback invoked when the bottom sheet has been expanded.
+ */
+ fun expand(onExpanded: (() -> Unit)? = null) {
+ animateTo(BottomSheetValue.Expanded, onEnd = { endReason, _ ->
+ if (endReason == AnimationEndReason.TargetReached) {
+ onExpanded?.invoke()
+ }
+ })
+ }
+
+ /**
+ * Collapse the bottom sheet, with an animation.
+ *
+ * @param onCollapsed Optional callback invoked when the bottom sheet has been collapsed.
+ */
+ fun collapse(onCollapsed: (() -> Unit)? = null) {
+ animateTo(BottomSheetValue.Collapsed, onEnd = { endReason, _ ->
+ if (endReason == AnimationEndReason.TargetReached) {
+ onCollapsed?.invoke()
+ }
+ })
+ }
+
+ companion object {
+ /**
+ * The default [Saver] implementation for [BottomSheetState].
+ */
+ fun Saver(
+ clock: AnimationClockObservable,
+ animationSpec: AnimationSpec<Float>,
+ confirmStateChange: (BottomSheetValue) -> Boolean
+ ): Saver<BottomSheetState, *> = Saver(
+ save = { it.value },
+ restore = {
+ BottomSheetState(
+ initialValue = it,
+ clock = clock,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+ )
+ }
+ )
+ }
+}
+
+/**
+ * Create a [BottomSheetState] and [remember] it against the [clock]. If a clock is not
+ * specified, the default animation clock will be used, as provided by [AnimationClockAmbient].
+ *
+ * @param initialValue The initial value of the state.
+ * @param clock The animation clock that will be used to drive the animations.
+ * @param animationSpec The default animation that will be used to animate to a new state.
+ * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
+ */
+@Composable
+@ExperimentalMaterialApi
+fun rememberBottomSheetState(
+ initialValue: BottomSheetValue,
+ clock: AnimationClockObservable = AnimationClockAmbient.current,
+ animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+ confirmStateChange: (BottomSheetValue) -> Boolean = { true }
+): BottomSheetState {
+ val disposableClock = clock.asDisposableClock()
+ return rememberSavedInstanceState(
+ disposableClock,
+ saver = BottomSheetState.Saver(
+ clock = disposableClock,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+ )
+ ) {
+ BottomSheetState(
+ initialValue = initialValue,
+ clock = disposableClock,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+ )
+ }
+}
+
+/**
+ * State of the [BottomSheetScaffold] composable.
+ *
+ * @param drawerState The state of the navigation drawer.
+ * @param bottomSheetState The state of the persistent bottom sheet.
+ * @param snackbarHostState The [SnackbarHostState] used to show snackbars inside the scaffold.
+ */
+@ExperimentalMaterialApi
+class BottomSheetScaffoldState(
+ val drawerState: DrawerState,
+ val bottomSheetState: BottomSheetState,
+ val snackbarHostState: SnackbarHostState
+)
+
+/**
+ * Create and [remember] a [BottomSheetScaffoldState].
+ *
+ * @param drawerState The state of the navigation drawer.
+ * @param bottomSheetState The state of the persistent bottom sheet.
+ * @param snackbarHostState The [SnackbarHostState] used to show snackbars inside the scaffold.
+ */
+@Composable
+@ExperimentalMaterialApi
+fun rememberBottomSheetScaffoldState(
+ drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
+ bottomSheetState: BottomSheetState = rememberBottomSheetState(BottomSheetValue.Collapsed),
+ snackbarHostState: SnackbarHostState = SnackbarHostState()
+): BottomSheetScaffoldState {
+ return remember(drawerState, bottomSheetState, snackbarHostState) {
+ BottomSheetScaffoldState(
+ drawerState = drawerState,
+ bottomSheetState = bottomSheetState,
+ snackbarHostState = snackbarHostState
+ )
+ }
+}
+
+/**
+ * Standard bottom sheets co-exist with the screen’s main UI region and allow for simultaneously
+ * viewing and interacting with both regions. They are commonly used to keep a feature or
+ * secondary content visible on screen when content in main UI region is frequently scrolled or
+ * panned.
+ *
+ * This component provides an API to put together several material components to construct your
+ * screen. For a similar component which implements the basic material design layout strategy
+ * with app bars, floating action buttons and navigation drawers, use the standard [Scaffold].
+ * For similar component that uses a backdrop as the centerpiece of the screen, use
+ * [BackdropScaffold].
+ *
+ * A simple example of a bottom sheet scaffold looks like this:
+ *
+ * @sample androidx.compose.material.samples.BottomSheetScaffoldSample
+ *
+ * @param sheetContent The content of the bottom sheet.
+ * @param modifier An optional [Modifier] for the root of the scaffold.
+ * @param scaffoldState The state of the scaffold.
+ * @param topBar An optional top app bar.
+ * @param snackbarHost The composable hosting the snackbars shown inside the scaffold.
+ * @param floatingActionButton An optional floating action button.
+ * @param floatingActionButtonPosition The position of the floating action button.
+ * @param sheetGesturesEnabled Whether the bottom sheet can be interacted with by gestures.
+ * @param sheetShape The shape of the bottom sheet.
+ * @param sheetElevation The elevation of the bottom sheet.
+ * @param sheetBackgroundColor The background color of the bottom sheet.
+ * @param sheetContentColor The preferred content color provided by the bottom sheet to its
+ * children. Defaults to the matching `onFoo` color for [sheetBackgroundColor], or if that is
+ * not a color from the theme, this will keep the same content color set above the bottom sheet.
+ * @param sheetPeekHeight The height of the bottom sheet when it is collapsed.
+ * @param drawerContent The content of the drawer sheet.
+ * @param drawerGesturesEnabled Whether the drawer sheet can be interacted with by gestures.
+ * @param drawerShape The shape of the drawer sheet.
+ * @param drawerElevation The elevation of the drawer sheet.
+ * @param drawerBackgroundColor The background color of the drawer sheet.
+ * @param drawerContentColor The preferred content color provided by the drawer sheet to its
+ * children. Defaults to the matching `onFoo` color for [drawerBackgroundColor], or if that is
+ * not a color from the theme, this will keep the same content color set above the drawer sheet.
+ * @param drawerScrimColor The color of the scrim that is applied when the drawer is open.
+ * @param bodyContent The main content of the screen. You should use the provided [PaddingValues]
+ * to properly offset the content, so that it is not obstructed by the bottom sheet when collapsed.
+ */
+@Composable
+@ExperimentalMaterialApi
+@OptIn(ExperimentalAnimationApi::class)
+fun BottomSheetScaffold(
+ sheetContent: @Composable ColumnScope.() -> Unit,
+ modifier: Modifier = Modifier,
+ scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(),
+ topBar: (@Composable () -> Unit)? = null,
+ snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
+ floatingActionButton: (@Composable () -> Unit)? = null,
+ floatingActionButtonPosition: FabPosition = FabPosition.End,
+ sheetGesturesEnabled: Boolean = true,
+ sheetShape: Shape = MaterialTheme.shapes.large,
+ sheetElevation: Dp = BottomSheetScaffoldConstants.DefaultSheetElevation,
+ sheetBackgroundColor: Color = MaterialTheme.colors.surface,
+ sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
+ sheetPeekHeight: Dp = BottomSheetScaffoldConstants.DefaultSheetPeekHeight,
+ drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
+ drawerGesturesEnabled: Boolean = true,
+ drawerShape: Shape = MaterialTheme.shapes.large,
+ drawerElevation: Dp = DrawerConstants.DefaultElevation,
+ drawerBackgroundColor: Color = MaterialTheme.colors.surface,
+ drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
+ drawerScrimColor: Color = DrawerConstants.defaultScrimColor,
+ backgroundColor: Color = MaterialTheme.colors.background,
+ contentColor: Color = contentColorFor(backgroundColor),
+ bodyContent: @Composable (PaddingValues) -> Unit
+) {
+ WithConstraints(modifier) {
+ val fullHeight = constraints.maxHeight.toFloat()
+ val peekHeightPx = with(DensityAmbient.current) { sheetPeekHeight.toPx() }
+ var bottomSheetHeight by remember { mutableStateOf(fullHeight) }
+
+ val swipeable = Modifier.swipeable(
+ state = scaffoldState.bottomSheetState,
+ anchors = mapOf(
+ fullHeight - peekHeightPx to BottomSheetValue.Collapsed,
+ fullHeight - bottomSheetHeight to BottomSheetValue.Expanded
+ ),
+ thresholds = { _, _ -> FixedThreshold(56.dp) },
+ orientation = Orientation.Vertical,
+ enabled = sheetGesturesEnabled,
+ resistance = null
+ )
+
+ val child = @Composable {
+ BottomSheetScaffoldStack(
+ body = {
+ Surface(
+ color = backgroundColor,
+ contentColor = contentColor
+ ) {
+ Column(Modifier.fillMaxSize()) {
+ topBar?.invoke()
+ bodyContent(PaddingValues(bottom = sheetPeekHeight))
+ }
+ }
+ },
+ bottomSheet = {
+ Surface(
+ swipeable
+ .fillMaxWidth()
+ .heightIn(min = sheetPeekHeight)
+ .onPositioned { bottomSheetHeight = it.size.height.toFloat() },
+ shape = sheetShape,
+ elevation = sheetElevation,
+ color = sheetBackgroundColor,
+ contentColor = sheetContentColor,
+ content = { Column(children = sheetContent) }
+ )
+ },
+ floatingActionButton = {
+ Stack(Modifier.zIndex(FabZIndex)) {
+ floatingActionButton?.invoke()
+ }
+ },
+ snackbarHost = {
+ Stack(Modifier.zIndex(SnackbarZIndex)) {
+ snackbarHost(scaffoldState.snackbarHostState)
+ }
+ },
+ bottomSheetOffset = scaffoldState.bottomSheetState.offset,
+ floatingActionButtonPosition = floatingActionButtonPosition
+ )
+ }
+ if (drawerContent == null) {
+ child()
+ } else {
+ ModalDrawerLayout(
+ drawerContent = drawerContent,
+ drawerState = scaffoldState.drawerState,
+ gesturesEnabled = drawerGesturesEnabled,
+ drawerShape = drawerShape,
+ drawerElevation = drawerElevation,
+ drawerBackgroundColor = drawerBackgroundColor,
+ drawerContentColor = drawerContentColor,
+ scrimColor = drawerScrimColor,
+ bodyContent = child
+ )
+ }
+ }
+}
+
+@Composable
+private fun BottomSheetScaffoldStack(
+ body: @Composable () -> Unit,
+ bottomSheet: @Composable () -> Unit,
+ floatingActionButton: @Composable () -> Unit,
+ snackbarHost: @Composable () -> Unit,
+ bottomSheetOffset: State<Float>,
+ floatingActionButtonPosition: FabPosition
+) {
+ Layout(children = {
+ body()
+ bottomSheet()
+ floatingActionButton()
+ snackbarHost()
+ }) { measurables, constraints ->
+ val placeable = measurables.first().measure(constraints)
+
+ layout(placeable.width, placeable.height) {
+ placeable.placeRelative(0, 0)
+
+ val (sheetPlaceable, fabPlaceable, snackbarPlaceable) =
+ measurables.drop(1).map {
+ it.measure(constraints.copy(minWidth = 0, minHeight = 0))
+ }
+
+ val sheetOffsetY = bottomSheetOffset.value.roundToInt()
+
+ sheetPlaceable.placeRelative(0, sheetOffsetY)
+
+ val fabOffsetX = when (floatingActionButtonPosition) {
+ FabPosition.Center -> (placeable.width - fabPlaceable.width) / 2
+ FabPosition.End -> placeable.width - fabPlaceable.width - FabEndSpacing.toIntPx()
+ }
+ val fabOffsetY = sheetOffsetY - fabPlaceable.height / 2
+
+ fabPlaceable.placeRelative(fabOffsetX, fabOffsetY)
+
+ val snackbarOffsetX = (placeable.width - snackbarPlaceable.width) / 2
+ val snackbarOffsetY = placeable.height - snackbarPlaceable.height
+
+ snackbarPlaceable.placeRelative(snackbarOffsetX, snackbarOffsetY)
+ }
+ }
+}
+
+private val FabEndSpacing = 16.dp
+private val FabZIndex = 8f
+private val SnackbarZIndex = Float.POSITIVE_INFINITY
+
+/**
+ * Contains useful constants for [BottomSheetScaffold].
+ */
+object BottomSheetScaffoldConstants {
+
+ /**
+ * The default elevation used by [BottomSheetScaffold].
+ */
+ val DefaultSheetElevation = 8.dp
+
+ /**
+ * The default peek height used by [BottomSheetScaffold].
+ */
+ val DefaultSheetPeekHeight = 56.dp
+}
\ No newline at end of file
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
index 4c9f769..b30d451 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
@@ -326,8 +326,7 @@
drawerElevation: Dp = DrawerConstants.DefaultElevation,
drawerBackgroundColor: Color = MaterialTheme.colors.surface,
drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
- scrimColor: Color = MaterialTheme.colors.onSurface
- .copy(alpha = DrawerConstants.ScrimDefaultOpacity),
+ scrimColor: Color = DrawerConstants.defaultScrimColor,
bodyContent: @Composable () -> Unit
) {
WithConstraints(modifier.fillMaxSize()) {
@@ -422,8 +421,7 @@
drawerElevation: Dp = DrawerConstants.DefaultElevation,
drawerBackgroundColor: Color = MaterialTheme.colors.surface,
drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
- scrimColor: Color = MaterialTheme.colors.onSurface
- .copy(alpha = DrawerConstants.ScrimDefaultOpacity),
+ scrimColor: Color = DrawerConstants.defaultScrimColor,
bodyContent: @Composable () -> Unit
) {
WithConstraints(modifier.fillMaxSize()) {
@@ -507,6 +505,10 @@
*/
val DefaultElevation = 16.dp
+ @Composable
+ val defaultScrimColor: Color
+ get() = MaterialTheme.colors.onSurface.copy(alpha = ScrimDefaultOpacity)
+
/**
* Default alpha for scrim color
*/
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
new file mode 100644
index 0000000..ee22a16
--- /dev/null
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
@@ -0,0 +1,335 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.material
+
+import androidx.compose.animation.animate
+import androidx.compose.animation.asDisposableClock
+import androidx.compose.animation.core.AnimationClockObservable
+import androidx.compose.animation.core.AnimationEndReason
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Stack
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.offsetPx
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.savedinstancestate.Saver
+import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.gesture.tapGestureFilter
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.layout.ExperimentalSubcomposeLayoutApi
+import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.platform.AnimationClockAmbient
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import kotlin.math.max
+
+/**
+ * Possible values of [ModalBottomSheetState].
+ */
+@ExperimentalMaterialApi
+enum class ModalBottomSheetValue {
+ /**
+ * The bottom sheet is not visible.
+ */
+ Hidden,
+
+ /**
+ * The bottom sheet is visible at full height.
+ */
+ Expanded,
+
+ /**
+ * The bottom sheet is partially visible at 50% of the screen height. This state is only
+ * enabled if the height of the bottom sheet is more than 50% of the screen height.
+ */
+ HalfExpanded
+}
+
+/**
+ * State of the [ModalBottomSheetLayout] composable.
+ *
+ * @param initialValue The initial value of the state.
+ * @param clock The animation clock that will be used to drive the animations.
+ * @param animationSpec The default animation that will be used to animate to a new state.
+ * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
+ */
+@ExperimentalMaterialApi
+class ModalBottomSheetState(
+ initialValue: ModalBottomSheetValue,
+ clock: AnimationClockObservable,
+ animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+ confirmStateChange: (ModalBottomSheetValue) -> Boolean = { true }
+) : SwipeableState<ModalBottomSheetValue>(
+ initialValue = initialValue,
+ clock = clock,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+) {
+ /**
+ * Whether the bottom sheet is visible.
+ */
+ val isVisible: Boolean
+ get() = value != ModalBottomSheetValue.Hidden
+
+ private val isHalfExpandedEnabled: Boolean
+ get() = anchors.values.contains(ModalBottomSheetValue.HalfExpanded)
+
+ /**
+ * Show the bottom sheet, with an animation.
+ *
+ * @param onShown Optional callback invoked when the bottom sheet has been shown.
+ */
+ fun show(onShown: (() -> Unit)? = null) {
+ val targetValue =
+ if (isHalfExpandedEnabled) ModalBottomSheetValue.HalfExpanded
+ else ModalBottomSheetValue.Expanded
+ animateTo(targetValue = targetValue, onEnd = { endReason, _ ->
+ if (endReason == AnimationEndReason.TargetReached) {
+ onShown?.invoke()
+ }
+ })
+ }
+
+ /**
+ * Hide the bottom sheet, with an animation.
+ *
+ * @param onHidden Optional callback invoked when the bottom sheet has been hidden.
+ */
+ fun hide(onHidden: (() -> Unit)? = null) {
+ animateTo(targetValue = ModalBottomSheetValue.Hidden, onEnd = { endReason, _ ->
+ if (endReason == AnimationEndReason.TargetReached) {
+ onHidden?.invoke()
+ }
+ })
+ }
+
+ companion object {
+ /**
+ * The default [Saver] implementation for [ModalBottomSheetState].
+ */
+ fun Saver(
+ clock: AnimationClockObservable,
+ animationSpec: AnimationSpec<Float>,
+ confirmStateChange: (ModalBottomSheetValue) -> Boolean
+ ): Saver<ModalBottomSheetState, *> = Saver(
+ save = { it.value },
+ restore = {
+ ModalBottomSheetState(
+ initialValue = it,
+ clock = clock,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+ )
+ }
+ )
+ }
+}
+
+/**
+ * Create a [ModalBottomSheetState] and [remember] it against the [clock]. If a clock is not
+ * specified, the default animation clock will be used, as provided by [AnimationClockAmbient].
+ *
+ * @param initialValue The initial value of the state.
+ * @param clock The animation clock that will be used to drive the animations.
+ * @param animationSpec The default animation that will be used to animate to a new state.
+ * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change.
+ */
+@Composable
+@ExperimentalMaterialApi
+fun rememberModalBottomSheetState(
+ initialValue: ModalBottomSheetValue,
+ clock: AnimationClockObservable = AnimationClockAmbient.current,
+ animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+ confirmStateChange: (ModalBottomSheetValue) -> Boolean = { true }
+): ModalBottomSheetState {
+ val disposableClock = clock.asDisposableClock()
+ return rememberSavedInstanceState(
+ disposableClock,
+ saver = ModalBottomSheetState.Saver(
+ clock = disposableClock,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+ )
+ ) {
+ ModalBottomSheetState(
+ initialValue = initialValue,
+ clock = disposableClock,
+ animationSpec = animationSpec,
+ confirmStateChange = confirmStateChange
+ )
+ }
+}
+
+/**
+ * Modal bottom sheets present a set of choices while blocking interaction with the rest of the
+ * screen. They are an alternative to inline menus and simple dialogs on mobile, providing
+ * additional room for content, iconography, and actions.
+ *
+ * A simple example of a modal bottom sheet looks like this:
+ *
+ * @sample androidx.compose.material.samples.ModalBottomSheetSample
+ *
+ * @param sheetContent The content of the bottom sheet.
+ * @param modifier Optional [Modifier] for the entire component.
+ * @param sheetState The state of the bottom sheet.
+ * @param sheetShape The shape of the bottom sheet.
+ * @param sheetElevation The elevation of the bottom sheet.
+ * @param sheetBackgroundColor The background color of the bottom sheet.
+ * @param sheetContentColor The preferred content color provided by the bottom sheet to its
+ * children. Defaults to the matching `onFoo` color for [sheetBackgroundColor], or if that is not
+ * a color from the theme, this will keep the same content color set above the bottom sheet.
+ * @param scrimColor The color of the scrim that is applied to the rest of the screen when the
+ * bottom sheet is visible. If you set this to `Color.Transparent`, then a scrim will no longer be
+ * applied and the bottom sheet will not block interaction with the rest of the screen when visible.
+ * @param content The content of rest of the screen.
+ */
+@Composable
+@ExperimentalMaterialApi
+fun ModalBottomSheetLayout(
+ sheetContent: @Composable ColumnScope.() -> Unit,
+ modifier: Modifier = Modifier,
+ sheetState: ModalBottomSheetState =
+ rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
+ sheetShape: Shape = MaterialTheme.shapes.large,
+ sheetElevation: Dp = ModalBottomSheetConstants.DefaultElevation,
+ sheetBackgroundColor: Color = MaterialTheme.colors.surface,
+ sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
+ scrimColor: Color = ModalBottomSheetConstants.DefaultScrimColor,
+ content: @Composable () -> Unit
+) = BottomSheetStack(
+ modifier = modifier,
+ sheetContent = {
+ Surface(
+ Modifier
+ .fillMaxWidth()
+ .offsetPx(y = sheetState.offset),
+ shape = sheetShape,
+ elevation = sheetElevation,
+ color = sheetBackgroundColor,
+ contentColor = sheetContentColor
+ ) {
+ Column(children = sheetContent)
+ }
+ },
+ content = { constraints, sheetHeight ->
+ val fullHeight = constraints.maxHeight.toFloat()
+ val anchors = if (sheetHeight < fullHeight / 2) {
+ mapOf(
+ fullHeight to ModalBottomSheetValue.Hidden,
+ fullHeight - sheetHeight to ModalBottomSheetValue.Expanded
+ )
+ } else {
+ mapOf(
+ fullHeight to ModalBottomSheetValue.Hidden,
+ fullHeight / 2 to ModalBottomSheetValue.HalfExpanded,
+ max(0f, fullHeight - sheetHeight) to ModalBottomSheetValue.Expanded
+ )
+ }
+ val swipeable = Modifier.swipeable(
+ state = sheetState,
+ anchors = anchors,
+ thresholds = { _, _ -> FixedThreshold(56.dp) },
+ orientation = Orientation.Vertical,
+ enabled = sheetState.value != ModalBottomSheetValue.Hidden,
+ resistance = null
+ )
+
+ Stack(Modifier.fillMaxSize().then(swipeable)) {
+ content()
+
+ Scrim(
+ color = scrimColor,
+ onDismiss = { sheetState.hide() },
+ visible = sheetState.targetValue != ModalBottomSheetValue.Hidden
+ )
+ }
+ }
+)
+
+@Composable
+private fun Scrim(
+ color: Color,
+ onDismiss: () -> Unit,
+ visible: Boolean
+) {
+ if (color != Color.Transparent) {
+ val alpha = animate(target = if (visible) 1f else 0f, animSpec = TweenSpec())
+ val dismissModifier = if (visible) Modifier.tapGestureFilter { onDismiss() } else Modifier
+
+ Canvas(
+ Modifier
+ .fillMaxSize()
+ .then(dismissModifier)
+ ) {
+ drawRect(color = color, alpha = alpha)
+ }
+ }
+}
+
+@Composable
+@OptIn(ExperimentalSubcomposeLayoutApi::class)
+private fun BottomSheetStack(
+ modifier: Modifier,
+ sheetContent: @Composable () -> Unit,
+ content: @Composable (constraints: Constraints, sheetHeight: Float) -> Unit
+) {
+ SubcomposeLayout<BottomSheetStackSlot>(modifier) { constraints ->
+ val sheetPlaceable =
+ subcompose(BottomSheetStackSlot.SheetContent, sheetContent)
+ .first().measure(constraints.copy(minWidth = 0, minHeight = 0))
+
+ val sheetHeight = sheetPlaceable.height.toFloat()
+
+ val placeable =
+ subcompose(BottomSheetStackSlot.Content) { content(constraints, sheetHeight) }
+ .first().measure(constraints)
+
+ layout(placeable.width, placeable.height) {
+ placeable.placeRelative(0, 0)
+ sheetPlaceable.placeRelative(0, 0)
+ }
+ }
+}
+
+private enum class BottomSheetStackSlot { SheetContent, Content }
+
+/**
+ * Contains useful constants for [ModalBottomSheetLayout].
+ */
+object ModalBottomSheetConstants {
+
+ /**
+ * The default elevation used by [ModalBottomSheetLayout].
+ */
+ val DefaultElevation = 16.dp
+
+ /**
+ * The default scrim color used by [ModalBottomSheetLayout].
+ */
+ @Composable
+ val DefaultScrimColor: Color
+ get() = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
+}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
index edac09c..3ff001d 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
@@ -127,8 +127,9 @@
* screen, by ensuring proper layout strategy for them and collecting necessary data so these
* components will work together correctly.
*
- * For a similar API which uses a backdrop as the centerpiece of the screen, use the experimental
- * [BackdropScaffold] component.
+ * For similar components that implement different layout structures, see [BackdropScaffold],
+ * which uses a backdrop as the centerpiece of the screen, and [BottomSheetScaffold], which uses
+ * a persistent bottom sheet as the centerpiece of the screen.
*
* Simple example of a Scaffold with [TopAppBar], [FloatingActionButton] and drawer:
*
@@ -193,8 +194,7 @@
drawerElevation: Dp = DrawerConstants.DefaultElevation,
drawerBackgroundColor: Color = MaterialTheme.colors.surface,
drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
- drawerScrimColor: Color = MaterialTheme.colors.onSurface
- .copy(alpha = DrawerConstants.ScrimDefaultOpacity),
+ drawerScrimColor: Color = DrawerConstants.defaultScrimColor,
backgroundColor: Color = MaterialTheme.colors.background,
contentColor: Color = contentColorFor(backgroundColor),
bodyContent: @Composable (PaddingValues) -> Unit
diff --git a/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt b/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt
index 2411c19..55c2720 100644
--- a/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt
+++ b/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt
@@ -16,6 +16,18 @@
package androidx.compose.runtime.dispatch
+import kotlinx.coroutines.yield
+
+// TODO(demin): how to implement this clock in case we have multiple windows on different
+// monitors? This clock is using by Recomposer to sync composition with frame rendering.
+// Also this clock is available to use in coroutines in client code.
actual val DefaultMonotonicFrameClock: MonotonicFrameClock by lazy {
- DesktopUiDispatcher.Dispatcher.frameClock
-}
+ object : MonotonicFrameClock {
+ override suspend fun <R> withFrameNanos(
+ onFrame: (Long) -> R
+ ): R {
+ yield()
+ return onFrame(System.nanoTime())
+ }
+ }
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/DesktopUiDispatcher.kt b/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/DesktopUiDispatcher.kt
deleted file mode 100644
index e1922fd..0000000
--- a/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/DesktopUiDispatcher.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.compose.runtime.dispatch
-
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Runnable
-import kotlinx.coroutines.suspendCancellableCoroutine
-import java.awt.event.ActionListener
-import javax.swing.SwingUtilities.invokeLater
-import javax.swing.Timer
-import kotlin.coroutines.CoroutineContext
-
-private typealias Action = (Long) -> Unit
-private typealias Queue = ArrayList<Action>
-
-/**
- * Ticks scheduler for Desktop. It tries to mimic Android's Choreographer.
- *
- * There are some plans to make possible redrawing based on composition snapshots,
- * so maybe some requirements for dispatcher will be mitigated in the future.
- **/
-class DesktopUiDispatcher : CoroutineDispatcher() {
- private val lock = Any()
- @PublishedApi internal val callbackLock = Any()
- private var callbacks = Queue()
-
- @Volatile
- private var scheduled = false
-
- private fun scheduleIfNeeded() {
- synchronized(lock) {
- if (!scheduled && hasPendingChanges()) {
- invokeLater { tick() }
- scheduled = true
- }
- }
- }
-
- val frameClock: MonotonicFrameClock = object :
- MonotonicFrameClock {
- override suspend fun <R> withFrameNanos(
- onFrame: (Long) -> R
- ): R {
- return suspendCancellableCoroutine { co ->
- val callback = { now: Long ->
- val res = runCatching {
- onFrame(now)
- }
- co.resumeWith(res)
- }
- scheduleCallback(callback)
- co.invokeOnCancellation {
- removeCallback(callback)
- }
- }
- }
- }
-
- fun hasPendingChanges() = callbacks.isNotEmpty()
-
- fun runAllCallbacks() {
- val now = System.nanoTime()
- runCallbacks(now, callbacks)
- scheduleIfNeeded()
- }
-
- private fun tick() {
- scheduled = false
- runAllCallbacks()
- scheduleIfNeeded()
- }
-
- fun scheduleCallback(action: Action) {
- synchronized(lock) {
- callbacks.add(action)
- scheduleIfNeeded()
- }
- }
-
- fun removeCallback(action: (Long) -> Unit) {
- synchronized(lock) {
- callbacks.remove(action)
- }
- }
-
- fun scheduleCallbackWithDelay(delay: Int, action: Action) {
- Timer(delay,
- ActionListener {
- scheduleCallback { action(System.nanoTime()) }
- }).apply {
- isRepeats = false
- start()
- }
- }
-
- /**
- * Prevent execution of callbacks while we execute [block] on other thread.
- */
- inline fun lockCallbacks(block: () -> Unit) = synchronized(callbackLock, block)
-
- private fun runCallbacks(now: Long, callbacks: Queue) {
- synchronized(lock) {
- callbacks.toList().also { callbacks.clear() }
- }.forEach {
- synchronized(callbackLock) {
- it(now)
- }
- }
- }
-
- override fun dispatch(context: CoroutineContext, block: Runnable) {
- scheduleCallback { block.run() }
- }
-
- companion object {
- val Dispatcher: DesktopUiDispatcher by lazy { DesktopUiDispatcher() }
- val Main: CoroutineContext by lazy {
- Dispatcher + Dispatcher.frameClock
- }
- }
-}
diff --git a/compose/runtime/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index 180c8a2..46b9aae 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -56,6 +56,9 @@
dependsOn jvmMain
}
desktopMain {
+ dependencies {
+ api(KOTLIN_COROUTINES_SWING)
+ }
dependsOn jvmMain
}
diff --git a/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
index c610e73..efcc40f 100644
--- a/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
+++ b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
@@ -16,7 +16,9 @@
package androidx.compose.runtime
-import androidx.compose.runtime.dispatch.DesktopUiDispatcher
+import androidx.compose.runtime.dispatch.DefaultMonotonicFrameClock
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.swing.Swing
import javax.swing.SwingUtilities
import kotlin.coroutines.CoroutineContext
@@ -29,7 +31,7 @@
}
override fun mainThreadCompositionContext(): CoroutineContext {
- return DesktopUiDispatcher.Main
+ return Dispatchers.Swing + DefaultMonotonicFrameClock
}
}
diff --git a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt
index c322ca1..010c9da 100644
--- a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt
+++ b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt
@@ -24,7 +24,7 @@
top,
right,
bottom
- )
+ )!!
}
fun org.jetbrains.skija.Rect.toComposeRect() =
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt
index ad2cecf..39abd69 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt
@@ -19,6 +19,8 @@
import android.content.Context
import android.content.res.Configuration
import android.view.View
+import androidx.compose.animation.core.InternalAnimationApi
+import androidx.compose.animation.core.rootAnimationClockFactory
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.Providers
@@ -64,9 +66,11 @@
val ViewModelStoreOwnerAmbient = staticAmbientOf<ViewModelStoreOwner>()
@Composable
+@OptIn(InternalAnimationApi::class)
internal fun ProvideAndroidAmbients(owner: AndroidOwner, content: @Composable () -> Unit) {
val view = owner.view
val context = view.context
+ val rootAnimationClock = remember { rootAnimationClockFactory() }
var configuration by remember {
mutableStateOf(
@@ -100,6 +104,7 @@
) {
ProvideCommonAmbients(
owner = owner,
+ animationClock = rootAnimationClock,
uriHandler = uriHandler,
content = content
)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Ambients.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Ambients.kt
index a8eca12..32b538a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Ambients.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Ambients.kt
@@ -17,11 +17,8 @@
package androidx.compose.ui.platform
import androidx.compose.animation.core.AnimationClockObservable
-import androidx.compose.animation.core.InternalAnimationApi
-import androidx.compose.animation.core.rootAnimationClockFactory
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Providers
-import androidx.compose.runtime.remember
import androidx.compose.runtime.staticAmbientOf
import androidx.compose.ui.autofill.Autofill
import androidx.compose.ui.autofill.AutofillTree
@@ -98,20 +95,17 @@
val LayoutDirectionAmbient = staticAmbientOf<LayoutDirection>()
@Composable
-@OptIn(InternalAnimationApi::class)
internal fun ProvideCommonAmbients(
owner: Owner,
+ animationClock: AnimationClockObservable,
uriHandler: UriHandler,
content: @Composable () -> Unit
) {
- val rootAnimationClock = remember { rootAnimationClockFactory() }
-
Providers(
- AnimationClockAmbient provides rootAnimationClock,
+ AnimationClockAmbient provides animationClock,
AutofillAmbient provides owner.autofill,
AutofillTreeAmbient provides owner.autofillTree,
ClipboardManagerAmbient provides owner.clipboardManager,
- @Suppress("DEPRECATION")
DensityAmbient provides owner.density,
FontLoaderAmbient provides owner.fontLoader,
HapticFeedBackAmbient provides owner.hapticFeedBack,
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopAnimationClock.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopAnimationClock.kt
new file mode 100644
index 0000000..f790d2c
--- /dev/null
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopAnimationClock.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.ui.platform
+
+import androidx.compose.animation.core.AnimationClockObservable
+import androidx.compose.animation.core.AnimationClockObserver
+import androidx.compose.animation.core.ManualAnimationClock
+
+internal class DesktopAnimationClock(
+ private val invalidate: () -> Unit
+) : AnimationClockObservable {
+ private val manual = ManualAnimationClock(0, dispatchOnSubscribe = false)
+
+ fun onFrame(nanoTime: Long) {
+ manual.clockTimeMillis = nanoTime / 1_000_000L
+ if (manual.hasObservers) {
+ invalidate()
+ }
+ }
+
+ override fun subscribe(observer: AnimationClockObserver) {
+ manual.subscribe(observer)
+ invalidate()
+ }
+
+ override fun unsubscribe(observer: AnimationClockObserver) {
+ manual.unsubscribe(observer)
+ }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt
index 6c0e93a..a948e9c 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt
@@ -15,8 +15,6 @@
*/
package androidx.compose.ui.platform
-import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.dispatch.DesktopUiDispatcher
import androidx.compose.runtime.staticAmbientOf
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.mouse.MouseScrollEvent
@@ -36,16 +34,14 @@
@OptIn(InternalCoreApi::class)
class DesktopOwners(
component: Component,
- private val redraw: () -> Unit
+ val invalidate: () -> Unit
) {
val list = LinkedHashSet<DesktopOwner>()
- // Optimization: we don't need more than one redrawing per tick
- private var redrawingScheduled = false
-
private var pointerId = 0L
private var isMousePressed = false
+ internal val animationClock = DesktopAnimationClock(invalidate)
internal val platformInputService: DesktopPlatformInput = DesktopPlatformInput(component)
fun register(desktopOwner: DesktopOwner) {
@@ -58,7 +54,8 @@
invalidate()
}
- fun onRender(canvas: Canvas, width: Int, height: Int) {
+ fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
+ animationClock.onFrame(nanoTime)
for (owner in list) {
owner.setSize(width, height)
owner.draw(canvas)
@@ -121,18 +118,4 @@
)
)
}
-
- fun invalidate() {
- if (!redrawingScheduled) {
- DesktopUiDispatcher.Dispatcher.scheduleCallback {
- redrawingScheduled = false
- if (Recomposer.current().hasPendingChanges()) {
- invalidate()
- } else {
- redraw()
- }
- }
- redrawingScheduled = true
- }
- }
}
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
index 233b986..3d98db3 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
@@ -47,6 +47,7 @@
) {
ProvideCommonAmbients(
owner = owner,
+ animationClock = owner.container.animationClock,
uriHandler = DesktopUriHandler(),
content = content
)
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
index b571ab0..37a2e36 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
@@ -21,12 +21,10 @@
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.node.ExperimentalLayoutNodeApi
import androidx.compose.ui.node.LayoutNode
-import com.nhaarman.mockitokotlin2.doAnswer
-import com.nhaarman.mockitokotlin2.doReturn
-import com.nhaarman.mockitokotlin2.mock
import org.jetbrains.skiko.Library
import org.junit.Assert.assertEquals
import org.junit.Test
+import java.awt.Component
@OptIn(ExperimentalLayoutNodeApi::class, ExperimentalComposeApi::class)
class DesktopOwnerTest {
@@ -36,10 +34,12 @@
var invalidateCount = 0
- val owners = mock<DesktopOwners> {
- on { platformInputService } doReturn mock()
- on { invalidate() }.doAnswer { invalidateCount++; Unit }
- }
+ val owners = DesktopOwners(
+ component = object : Component() {},
+ invalidate = {
+ invalidateCount++
+ }
+ )
val owner = DesktopOwner(owners)
val node = LayoutNode()
val state = mutableStateOf(2)
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/test/TestComposeWindow.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
index bd0cd7e1..e8538a4 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
@@ -37,7 +37,7 @@
val surface = Surface.makeRasterN32Premul(width, height)
val canvas = surface.canvas
val component = object : Component() {}
- val owners = DesktopOwners(component = component, redraw = {})
+ val owners = DesktopOwners(component = component, invalidate = {})
fun setContent(content: @Composable () -> Unit): DesktopOwners {
val owner = DesktopOwner(owners, density)
diff --git a/room/runtime/src/main/java/androidx/room/InvalidationTracker.java b/room/runtime/src/main/java/androidx/room/InvalidationTracker.java
index 980aba5..45d819e 100644
--- a/room/runtime/src/main/java/androidx/room/InvalidationTracker.java
+++ b/room/runtime/src/main/java/androidx/room/InvalidationTracker.java
@@ -249,6 +249,9 @@
* <p>
* If one of the tables in the Observer does not exist in the database, this method throws an
* {@link IllegalArgumentException}.
+ * <p>
+ * This method should be called on a background/worker thread as it performs database
+ * operations.
*
* @param observer The observer which listens the database for changes.
*/
@@ -322,6 +325,9 @@
/**
* Removes the observer from the observers list.
+ * <p>
+ * This method should be called on a background/worker thread as it performs database
+ * operations.
*
* @param observer The observer to remove.
*/
diff --git a/settings.gradle b/settings.gradle
index ff95a5c..052dbb5 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -336,6 +336,7 @@
includeProject(":viewpager2:integration-tests:testapp", "viewpager2/integration-tests/testapp")
includeProject(":wear:wear", "wear/wear")
includeProject(":wear:wear-complications-data", "wear/wear-complications-data")
+includeProject(":wear:wear-complications-data-samples", "wear/wear-complications-data/samples")
includeProject(":wear:wear-complications-rendering", "wear/wear-complications-rendering")
includeProject(":wear:wear-input", "wear/wear-input")
includeProject(":wear:wear-input-testing", "wear/wear-input-testing")
diff --git a/textclassifier/integration-tests/testapp/src/main/java/androidx/textclassifier/integration/testapp/experimental/widget/FloatingToolbar.java b/textclassifier/integration-tests/testapp/src/main/java/androidx/textclassifier/integration/testapp/experimental/widget/FloatingToolbar.java
deleted file mode 100644
index fa8f88a..0000000
--- a/textclassifier/integration-tests/testapp/src/main/java/androidx/textclassifier/integration/testapp/experimental/widget/FloatingToolbar.java
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.textclassifier.integration.testapp.experimental.widget;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Build;
-import android.text.TextUtils;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.PopupWindow;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.view.menu.MenuItemImpl;
-import androidx.core.internal.view.SupportMenu;
-import androidx.core.internal.view.SupportMenuItem;
-import androidx.core.util.Preconditions;
-import androidx.core.view.MenuItemCompat;
-import androidx.core.view.ViewCompat;
-import androidx.textclassifier.integration.testapp.R;
-import androidx.textclassifier.widget.IFloatingToolbar;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * An experimental implementation of floating toolbar that supports slice.
- */
-@RequiresApi(api = Build.VERSION_CODES.M)
-public final class FloatingToolbar implements IFloatingToolbar {
-
- private static final SupportMenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
- new SupportMenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(android.view.MenuItem item) {
- return false;
- }
- };
-
- @VisibleForTesting
- static final Object FLOATING_TOOLBAR_TAG = "floating_toolbar";
- @VisibleForTesting
- static final Object MAIN_PANEL_TAG = "main_panel";
-
- private final FloatingToolbarPopup mPopup;
- private final Rect mContentRect = new Rect();
-
- private SupportMenu mMenu;
- private List<SupportMenuItem> mShowingMenuItems = new ArrayList<>();
- private SupportMenuItem.OnMenuItemClickListener mMenuItemClickListener =
- NO_OP_MENUITEM_CLICK_LISTENER;
-
- /* Item click listeners */
- private SupportMenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
-
- private final Comparator<SupportMenuItem> mMenuItemComparator =
- new Comparator<SupportMenuItem>() {
- @Override
- public int compare(SupportMenuItem menuItem1, SupportMenuItem menuItem2) {
- int customOrder1 = getCustomOrder(menuItem1);
- int customOrder2 = getCustomOrder(menuItem2);
- if (customOrder1 == customOrder2) {
- return compareOrder(menuItem1, menuItem2);
- }
- return Integer.compare(customOrder1, customOrder2);
- }
-
- private int getCustomOrder(SupportMenuItem menuItem) {
- if (menuItem.getItemId() == MENU_ID_SMART_ACTION) {
- return 0;
- }
- if (requiresActionButton(menuItem)) {
- return 1;
- }
- if (requiresOverflow(menuItem)) {
- return 3;
- }
- return 2;
- }
-
- private int compareOrder(SupportMenuItem menuItem1, SupportMenuItem menuItem2) {
- return menuItem1.getOrder() - menuItem2.getOrder();
- }
- };
-
- public FloatingToolbar(View view) {
- mPopup = new FloatingToolbarPopup(view.getRootView());
- }
-
- @Override
- public void setMenu(@NonNull SupportMenu menu) {
- mMenu = Preconditions.checkNotNull(menu);
- }
-
- @Nullable
- @Override
- public SupportMenu getMenu() {
- return mMenu;
- }
-
- @Override
- public void setSuggestedWidth(int suggestedWidth) {}
-
- @Override
- public void show() {
- doShow();
- }
-
- @Override
- public void setContentRect(@NonNull Rect rect) {
-
- }
-
- private void doShow() {
- List<SupportMenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
- Collections.sort(menuItems, mMenuItemComparator);
- if (!isCurrentlyShowing(menuItems)) {
- mPopup.layoutMenuItems(menuItems, mOnMenuItemClickListener);
- }
- mPopup.show(mContentRect);
- }
-
- /**
- * Returns true if this floating toolbar is currently showing the specified menu items.
- */
- private boolean isCurrentlyShowing(List<SupportMenuItem> menuItems) {
- if (mShowingMenuItems == null || menuItems.size() != mShowingMenuItems.size()) {
- return false;
- }
-
- final int size = menuItems.size();
- for (int i = 0; i < size; i++) {
- final SupportMenuItem menuItem = menuItems.get(i);
- final SupportMenuItem showingItem = mShowingMenuItems.get(i);
- if (menuItem.getItemId() != showingItem.getItemId()
- || !TextUtils.equals(menuItem.getTitle(), showingItem.getTitle())
- || !Objects.equals(menuItem.getIcon(), showingItem.getIcon())
- || menuItem.getGroupId() != showingItem.getGroupId()) {
- return false;
- }
- }
-
- return true;
- }
-
- @Override
- public void updateLayout() {}
-
- @Override
- public void dismiss() {
- mPopup.dismiss();
- }
-
- @Override
- public void hide() {}
-
- @Override
- public boolean isShowing() {
- return false;
- }
-
- @Override
- public boolean isHidden() {
- return false;
- }
-
- @Override
- public void setOnDismissListener(@Nullable PopupWindow.OnDismissListener onDismiss) {}
-
- @Override
- public void setDismissOnMenuItemClick(boolean dismiss) {}
-
- @Override
- public void setOnMenuItemClickListener(
- @Nullable SupportMenuItem.OnMenuItemClickListener menuItemClickListener) {
- mMenuItemClickListener = mMenuItemClickListener == null
- ? NO_OP_MENUITEM_CLICK_LISTENER : menuItemClickListener;
- }
-
- /**
- * Returns the visible and enabled menu items in the specified menu.
- * This method is recursive.
- */
- private static List<SupportMenuItem> getVisibleAndEnabledMenuItems(SupportMenu menu) {
- List<SupportMenuItem> menuItems = new ArrayList<>();
- for (int i = 0; (menu != null) && (i < menu.size()); i++) {
- SupportMenuItem menuItem = (SupportMenuItem) menu.getItem(i);
- if (menuItem.isVisible() && menuItem.isEnabled()) {
- SupportMenu subMenu = (SupportMenu) menuItem.getSubMenu();
- if (subMenu != null) {
- menuItems.addAll(getVisibleAndEnabledMenuItems(subMenu));
- } else {
- menuItems.add(menuItem);
- }
- }
- }
- return menuItems;
- }
-
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- static boolean requiresOverflow(SupportMenuItem menuItem) {
- if (menuItem instanceof MenuItemImpl) {
- final MenuItemImpl impl = (MenuItemImpl) menuItem;
- return !impl.requiresActionButton() && !impl.requestsActionButton();
- }
- return false;
- }
-
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- static boolean requiresActionButton(SupportMenuItem menuItem) {
- return menuItem instanceof MenuItemImpl
- && menuItem.requiresActionButton();
- }
-
- private static final class FloatingToolbarPopup {
-
- final View mHost; // Host for the popup window.
- final Context mContext;
- final PopupWindow mPopupWindow;
-
- /* View components */
- private final ViewGroup mContentContainer; // holds all contents.
- private final MainPanel mMainPanel; // holds menu items that are initially displayed.
-
- /* Item click listeners */
- private SupportMenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
- private final View.OnClickListener mMenuItemButtonOnClickListener =
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (v.getTag() instanceof SupportMenuItem) {
- if (mOnMenuItemClickListener != null) {
- mOnMenuItemClickListener.onMenuItemClick(
- (SupportMenuItem) v.getTag());
- }
- }
- }
- };
-
- FloatingToolbarPopup(View host) {
- mHost = Preconditions.checkNotNull(host);
- mContext = host.getContext();
- mPopupWindow = createPopupWindow(mContext);
- mContentContainer = createContentContainer(
- mContext, (ViewGroup) mPopupWindow.getContentView());
- mMainPanel = new MainPanel(
- mContentContainer.findViewById(R.id.mainPanel),
- mContext,
- mMenuItemButtonOnClickListener);
- }
-
- /**
- * Shows this popup at the specified coordinates.
- * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
- */
- public void show(Rect contentRectOnScreen) {
- Preconditions.checkNotNull(contentRectOnScreen);
- mPopupWindow.showAtLocation(mHost, Gravity.NO_GRAVITY, 0, 0);
- }
-
- /**
- * Lays out buttons for the specified menu items.
- * Requires a subsequent call to {@link #show()} to show the items.
- */
- public void layoutMenuItems(
- List<SupportMenuItem> menuItems,
- SupportMenuItem.OnMenuItemClickListener menuItemClickListener) {
- mOnMenuItemClickListener = menuItemClickListener;
- menuItems = mMainPanel.layoutMenuItems(menuItems);
- if (!menuItems.isEmpty()) {
- // Add remaining items to the overflow.
- }
- }
-
- public void dismiss() {
-
- }
-
- /**
- * Clears out the panels and their container. Resets their calculated sizes.
- */
- @SuppressWarnings("unchecked")
- void clearPanels() {
- mMainPanel.clear();
- mContentContainer.removeAllViews();
- }
-
- static ViewGroup createContentContainer(Context context, ViewGroup parent) {
- ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
- .inflate(R.layout.floating_popup_container, parent);
- contentContainer.setTag(FLOATING_TOOLBAR_TAG);
- contentContainer.setClipToOutline(true);
- return contentContainer;
- }
-
- static PopupWindow createPopupWindow(Context context) {
- ViewGroup popupContentHolder = new LinearLayout(context);
- popupContentHolder.setSoundEffectsEnabled(false);
- PopupWindow popupWindow = new PopupWindow(popupContentHolder);
- popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
- popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
- popupWindow.setClippingEnabled(false);
- popupWindow.setOutsideTouchable(true);
- popupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
- popupWindow.setAnimationStyle(0);
- // Set the next line to Color.TRANSPARENT for a translucent popup.
- int color = Color.argb(50, 50, 0, 0);
- popupWindow.setBackgroundDrawable(new ColorDrawable(color));
- return popupWindow;
- }
-
- /**
- * This class is responsible for layout of the main panel.
- */
- private static final class MainPanel {
-
- private final ViewGroup mWidget;
- private final Context mContext;
- private final int mIconTextSpacing;
- private final int mOverflowButtonWidth;
- private final int mToolbarWidth;
-
- private int mAvailableWidth;
-
- /* Item click listeners */
- private final View.OnClickListener mMenuItemButtonOnClickListener;
-
- MainPanel(ViewGroup widget,
- Context context,
- View.OnClickListener menuItemButtonOnClickListener) {
- mWidget = Preconditions.checkNotNull(widget);
- mWidget.setTag(MAIN_PANEL_TAG);
- mContext = Preconditions.checkNotNull(context);
- mMenuItemButtonOnClickListener =
- Preconditions.checkNotNull(menuItemButtonOnClickListener);
- mIconTextSpacing = mContext.getResources()
- .getDimensionPixelSize(R.dimen.floating_toolbar_icon_text_spacing);
- mOverflowButtonWidth = mContext.getResources()
- .getDimensionPixelSize(R.dimen.floating_toolbar_overflow_button_width);
- mToolbarWidth = mContext.getResources()
- .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
- mAvailableWidth = mToolbarWidth;
- }
-
- /**
- * Fits as many menu items in the main panel and returns a list of the menu items that
- * were not fit in.
- *
- * @return The menu items that are not included in this main panel.
- */
- List<SupportMenuItem> layoutMenuItems(List<SupportMenuItem> menuItems) {
- Preconditions.checkNotNull(menuItems);
- final List<SupportMenuItem> mainPanelMenuItems = new ArrayList<>();
- // add the overflow menu items to the end of the items list.
- final List<SupportMenuItem> overflowMenuItems = new ArrayList<>();
- for (SupportMenuItem menuItem : menuItems) {
- if (menuItem.getItemId() != MENU_ID_SMART_ACTION
- && requiresOverflow(menuItem)) {
- overflowMenuItems.add(menuItem);
- } else {
- mainPanelMenuItems.add(menuItem);
- }
- }
- mainPanelMenuItems.addAll(overflowMenuItems);
-
- clear();
- mWidget.setPaddingRelative(0, 0, 0, 0);
- int index;
- boolean isFirst = true;
- boolean isLast = menuItems.size() == 1;
- int itemCount = mainPanelMenuItems.size();
- for (index = 0; index < itemCount; index++) {
- isLast = index == itemCount - 1;
- boolean added = addItem(
- mainPanelMenuItems.get(index),
- isFirst,
- isLast);
- if (!added) {
- break;
- }
- isFirst = false;
- }
- if (!isLast) {
- // Reserve space for overflowButton.
- mWidget.setPaddingRelative(0, 0, mOverflowButtonWidth, 0);
- }
- return mainPanelMenuItems.subList(index, itemCount);
- }
-
- void clear() {
- mWidget.removeAllViews();
- mAvailableWidth = mToolbarWidth;
- }
-
- /**
- * Returns true if the given menu item is successfully added to the main panel ,
- * otherwise, returns false.
- */
- private boolean addItem(final SupportMenuItem item, boolean isFirst, boolean isLast) {
- // if this is the first item, regardless of requiresOverflow(), it should be
- // displayed on the main panel. Otherwise all items including this one will be
- // overflow items, and should be displayed in overflow panel.
- if (!isFirst && requiresOverflow(item)) {
- return false;
- }
-
- final boolean showIcon = isFirst
- && item.getItemId() == MENU_ID_SMART_ACTION;
- final View menuItemButton = PanelUtils.createMenuItemButton(
- mContext, item, mIconTextSpacing, showIcon);
- if (!showIcon) {
- ((LinearLayout) menuItemButton).setGravity(Gravity.CENTER);
- }
-
- // Adding additional start padding for the first button to even out button
- // spacing.
- if (isFirst) {
- menuItemButton.setPaddingRelative(
- (int) (1.5 * menuItemButton.getPaddingStart()),
- menuItemButton.getPaddingTop(),
- menuItemButton.getPaddingEnd(),
- menuItemButton.getPaddingBottom());
- }
-
- // Adding additional end padding for the last button to even out button spacing.
- if (isLast) {
- menuItemButton.setPaddingRelative(
- menuItemButton.getPaddingStart(),
- menuItemButton.getPaddingTop(),
- (int) (1.5 * menuItemButton.getPaddingEnd()),
- menuItemButton.getPaddingBottom());
- }
-
- menuItemButton.measure(
- View.MeasureSpec.UNSPECIFIED,
- View.MeasureSpec.UNSPECIFIED);
- final int menuItemButtonWidth = Math.min(
- menuItemButton.getMeasuredWidth(), mToolbarWidth);
-
- // Check if we can fit an item while reserving space for the overflowButton.
- final boolean canFitWithOverflow =
- menuItemButtonWidth <= mAvailableWidth - mOverflowButtonWidth;
- final boolean canFitNoOverflow =
- isLast && menuItemButtonWidth <= mAvailableWidth;
- if (canFitWithOverflow || canFitNoOverflow) {
- PanelUtils.setButtonTagAndClickListener(
- menuItemButton, item, mMenuItemButtonOnClickListener);
- // Set tooltips for main panel items, but not overflow items (b/35726766).
- CharSequence tooltip = item.getTooltipText() == null
- ? item.getTitle()
- : item.getTooltipText();
- ViewCompat.setTooltipText(menuItemButton, tooltip);
- mWidget.addView(menuItemButton);
- final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
- params.width = menuItemButtonWidth;
- menuItemButton.setLayoutParams(params);
- mAvailableWidth -= menuItemButtonWidth;
- return true;
- }
- return false;
- }
- }
-
- /**
- * A helper class that contains the helper methods which are shared by different panels.
- */
- private static final class PanelUtils{
-
- /**
- * Creates and returns a menu button for the specified menu item.
- */
- static View createMenuItemButton(
- Context context,
- SupportMenuItem menuItem,
- int iconTextSpacing,
- boolean showIcon) {
- final View menuItemButton = LayoutInflater.from(context)
- .inflate(R.layout.floating_popup_menu_button, null);
- if (menuItem != null) {
- updateMenuItemButton(menuItemButton, menuItem, iconTextSpacing, showIcon);
- }
- return menuItemButton;
- }
-
- /**
- * Updates the specified menu item button with the specified menu item data.
- */
- static void updateMenuItemButton(
- View menuItemButton,
- SupportMenuItem menuItem,
- int iconTextSpacing,
- boolean showIcon) {
- final TextView buttonText = menuItemButton.findViewById(
- androidx.textclassifier.R.id.floating_toolbar_menu_item_text);
- buttonText.setEllipsize(null);
- if (TextUtils.isEmpty(menuItem.getTitle())) {
- buttonText.setVisibility(View.GONE);
- } else {
- buttonText.setVisibility(View.VISIBLE);
- buttonText.setText(menuItem.getTitle());
- }
- final ImageView buttonIcon = menuItemButton.findViewById(
- androidx.textclassifier.R.id.floating_toolbar_menu_item_image);
- if (menuItem.getIcon() == null || !showIcon) {
- buttonIcon.setVisibility(View.GONE);
- buttonText.setPaddingRelative(0, 0, 0, 0);
- } else {
- buttonIcon.setVisibility(View.VISIBLE);
- buttonIcon.setImageDrawable(menuItem.getIcon());
- buttonText.setPaddingRelative(iconTextSpacing, 0, 0, 0);
- }
- final CharSequence contentDescription =
- MenuItemCompat.getContentDescription(menuItem);
- if (TextUtils.isEmpty(contentDescription)) {
- menuItemButton.setContentDescription(menuItem.getTitle());
- } else {
- menuItemButton.setContentDescription(contentDescription);
- }
- }
-
- static void setButtonTagAndClickListener(
- View menuItemButton,
- SupportMenuItem menuItem,
- View.OnClickListener menuItemButtonOnClickListener) {
- menuItemButton.setTag(menuItem);
- menuItemButton.setOnClickListener(menuItemButtonOnClickListener);
- }
- }
- }
-}
diff --git a/textclassifier/integration-tests/testapp/src/main/res/layout/floating_popup_container.xml b/textclassifier/integration-tests/testapp/src/main/res/layout/floating_popup_container.xml
deleted file mode 100644
index a5e375f..0000000
--- a/textclassifier/integration-tests/testapp/src/main/res/layout/floating_popup_container.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="350dp"
- android:layout_height="200dp"
- android:padding="0dp"
- android:background="@android:color/background_light"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:elevation="2dp"
- android:focusable="true"
- android:focusableInTouchMode="true">
-
- <LinearLayout
- android:id="@+id/slicePanel"
- android:layout_width="350dp"
- android:layout_height="200dp"
- android:layout_alignParentTop="true"
- android:layout_alignParentEnd="true"
- android:paddingBottom="48dp"
- android:background="@android:color/holo_blue_dark"
- android:orientation="horizontal">
- </LinearLayout>
-
- <ListView
- android:id="@+id/overflowPanel"
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:background="@android:color/black"/>
-
- <LinearLayout
- android:id="@+id/mainPanel"
- android:layout_width="350dp"
- android:layout_height="48dp"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:background="@android:color/holo_red_dark"
- android:orientation="horizontal" />
-
- <ImageButton
- android:id="@+id/overflowButton"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:background="@android:color/holo_green_light"/>
-</RelativeLayout>
diff --git a/textclassifier/integration-tests/testapp/src/main/res/layout/floating_popup_menu_button.xml b/textclassifier/integration-tests/testapp/src/main/res/layout/floating_popup_menu_button.xml
deleted file mode 100644
index da17c0e..0000000
--- a/textclassifier/integration-tests/testapp/src/main/res/layout/floating_popup_menu_button.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:minWidth="@dimen/floating_toolbar_menu_button_minimum_width"
- android:minHeight="@dimen/floating_toolbar_height"
- android:paddingStart="@dimen/floating_toolbar_menu_button_side_padding"
- android:paddingEnd="@dimen/floating_toolbar_menu_button_side_padding"
- android:paddingTop="0dp"
- android:paddingBottom="0dp"
- android:background="?attr/selectableItemBackground">
- <ImageView
- android:id="@+id/floating_toolbar_menu_item_image"
- android:layout_width="@dimen/floating_toolbar_menu_image_width"
- android:layout_height="@dimen/floating_toolbar_height"
- android:paddingTop="@dimen/floating_toolbar_menu_image_button_vertical_padding"
- android:paddingStart="0dp"
- android:paddingBottom="@dimen/floating_toolbar_menu_image_button_vertical_padding"
- android:paddingEnd="0dp"
- android:layout_margin="0dp"
- android:scaleType="centerInside"
- android:background="@null"
- android:focusable="false"
- android:focusableInTouchMode="false"
- android:importantForAccessibility="no" />
- <!-- TODO: need to change the text color to be "?attr/floatingToolbarForegroundColor"-->
- <TextView
- android:id="@+id/floating_toolbar_menu_item_text"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/floating_toolbar_height"
- android:textAppearance="?android:attr/textAppearanceListItemSmall"
- android:padding="0dp"
- android:paddingStart="@dimen/floating_toolbar_menu_button_side_padding"
- android:paddingLeft="@dimen/floating_toolbar_menu_button_side_padding"
- android:layout_margin="0dp"
- android:gravity="center"
- android:singleLine="true"
- android:ellipsize="end"
- android:fontFamily="sans-serif-medium"
- android:textSize="@dimen/floating_toolbar_text_size"
- android:textAllCaps="@bool/floating_toolbar_all_caps"
- android:background="@null"
- android:focusable="false"
- android:focusableInTouchMode="false"
- android:importantForAccessibility="no"
- android:textColor="@color/colorPrimaryDark" />
-</LinearLayout>
diff --git a/textclassifier/integration-tests/testapp/src/main/res/values/dimens.xml b/textclassifier/integration-tests/testapp/src/main/res/values/dimens.xml
index 25ed43d..4af50b5 100644
--- a/textclassifier/integration-tests/testapp/src/main/res/values/dimens.xml
+++ b/textclassifier/integration-tests/testapp/src/main/res/values/dimens.xml
@@ -16,14 +16,4 @@
-->
<resources>
- <!-- Floating toolbar dimensions -->
- <dimen name="floating_toolbar_preferred_width">350dp</dimen>
- <dimen name="floating_toolbar_overflow_button_width">48dp</dimen>
- <dimen name="floating_toolbar_icon_text_spacing">8dp</dimen>
- <dimen name="floating_toolbar_menu_button_minimum_width">48dp</dimen>
- <dimen name="floating_toolbar_height">48dp</dimen>
- <dimen name="floating_toolbar_menu_button_side_padding">16dp</dimen>
- <dimen name="floating_toolbar_menu_image_width">24dp</dimen>
- <dimen name="floating_toolbar_menu_image_button_vertical_padding">12dp</dimen>
- <dimen name="floating_toolbar_text_size">14sp</dimen>
</resources>
diff --git a/ui/ui-test/src/desktopMain/kotlin/androidx/ui/test/DesktopComposeTestRule.kt b/ui/ui-test/src/desktopMain/kotlin/androidx/ui/test/DesktopComposeTestRule.kt
index cea37ec..04cba4d 100644
--- a/ui/ui-test/src/desktopMain/kotlin/androidx/ui/test/DesktopComposeTestRule.kt
+++ b/ui/ui-test/src/desktopMain/kotlin/androidx/ui/test/DesktopComposeTestRule.kt
@@ -21,7 +21,6 @@
import androidx.compose.runtime.EmbeddingContextFactory
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.dispatch.DesktopUiDispatcher
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.platform.DesktopOwner
import androidx.compose.ui.platform.DesktopOwners
@@ -88,13 +87,11 @@
@OptIn(ExperimentalComposeApi::class)
private fun isIdle() =
- !DesktopUiDispatcher.Dispatcher.hasPendingChanges() &&
!Snapshot.current.hasPendingChanges() &&
!Recomposer.current().hasPendingChanges()
override fun waitForIdle() {
while (!isIdle()) {
- DesktopUiDispatcher.Dispatcher.runAllCallbacks()
runExecutionQueue()
Thread.sleep(10)
}
@@ -114,7 +111,7 @@
val surface = Surface.makeRasterN32Premul(displaySize.width, displaySize.height)
val canvas = surface.canvas
val component = object : Component() {}
- val owners = DesktopOwners(component = component, redraw = {}).also {
+ val owners = DesktopOwners(component = component, invalidate = {}).also {
owners = it
}
val owner = DesktopOwner(owners)
diff --git a/ui/ui-test/src/desktopMain/kotlin/androidx/ui/test/TestSkiaWindow.kt b/ui/ui-test/src/desktopMain/kotlin/androidx/ui/test/TestSkiaWindow.kt
index 6e1680b..8c3cac7 100644
--- a/ui/ui-test/src/desktopMain/kotlin/androidx/ui/test/TestSkiaWindow.kt
+++ b/ui/ui-test/src/desktopMain/kotlin/androidx/ui/test/TestSkiaWindow.kt
@@ -49,7 +49,7 @@
fun setContent(content: @Composable () -> Unit) {
val component = object : Component() {}
- val owners = DesktopOwners(component = component, redraw = {})
+ val owners = DesktopOwners(component = component, invalidate = {})
val owner = DesktopOwner(owners)
owner.setContent(content)
owner.setSize(width, height)
diff --git a/wear/wear-complications-data/samples/build.gradle b/wear/wear-complications-data/samples/build.gradle
new file mode 100644
index 0000000..1f38a2e
--- /dev/null
+++ b/wear/wear-complications-data/samples/build.gradle
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+dependencies {
+ implementation("androidx.concurrent:concurrent-futures:1.0.0")
+ implementation("androidx.core:core:1.1.0")
+ api(project(":wear:wear-complications-data"))
+ api(GUAVA_ANDROID)
+ api(KOTLIN_STDLIB)
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 28
+ }
+}
\ No newline at end of file
diff --git a/wear/wear-complications-data/samples/src/main/AndroidManifest.xml b/wear/wear-complications-data/samples/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6ec6811
--- /dev/null
+++ b/wear/wear-complications-data/samples/src/main/AndroidManifest.xml
@@ -0,0 +1,74 @@
+<?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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.wear.complications.samples">
+ <application
+ android:label="@string/app_name"
+ android:icon="@drawable/circle"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar">
+ <service
+ android:name=".AsynchronousProviderService"
+ android:label="@string/asynchronous_provider_name"
+ android:exported="true"
+ android:icon="@drawable/circle"
+ android:permission="com.google.android.wearable.permission.BIND_COMPLICATION_PROVIDER">
+ <meta-data
+ android:name="android.support.wearable.complications.SUPPORTED_TYPES"
+ android:value="SHORT_TEXT,LONG_TEXT"/>
+ <meta-data
+ android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
+ android:value="0"/>
+ <intent-filter>
+ <action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST"/>
+ </intent-filter>
+ </service>
+
+ <service
+ android:name=".BackgroundProviderService"
+ android:label="@string/background_provider_name"
+ android:exported="true"
+ android:icon="@drawable/circle"
+ android:permission="com.google.android.wearable.permission.BIND_COMPLICATION_PROVIDER">
+ <meta-data
+ android:name="android.support.wearable.complications.SUPPORTED_TYPES"
+ android:value="SHORT_TEXT,LONG_TEXT"/>
+ <meta-data
+ android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
+ android:value="0"/>
+ <intent-filter>
+ <action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST"/>
+ </intent-filter>
+ </service>
+
+ <service
+ android:name=".SynchronousProviderService"
+ android:label="@string/synchronous_provider_name"
+ android:exported="true"
+ android:icon="@drawable/circle"
+ android:permission="com.google.android.wearable.permission.BIND_COMPLICATION_PROVIDER">
+ <meta-data
+ android:name="android.support.wearable.complications.SUPPORTED_TYPES"
+ android:value="SHORT_TEXT,LONG_TEXT"/>
+ <meta-data
+ android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
+ android:value="10000"/>
+ <intent-filter>
+ <action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST"/>
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/wear/wear-complications-data/samples/src/main/java/androidx/wear/complications/samples/AsynchronousProviderService.kt b/wear/wear-complications-data/samples/src/main/java/androidx/wear/complications/samples/AsynchronousProviderService.kt
new file mode 100644
index 0000000..3dcfb50
--- /dev/null
+++ b/wear/wear-complications-data/samples/src/main/java/androidx/wear/complications/samples/AsynchronousProviderService.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package androidx.wear.complications.samples
+
+import android.support.wearable.complications.ComplicationData
+import android.support.wearable.complications.ComplicationText
+import androidx.wear.complications.ComplicationProviderService
+import java.util.concurrent.Executors
+
+/** A minimal complication provider which reports the ID of the complication asynchronously. */
+class AsynchronousProviderService : ComplicationProviderService() {
+ val executor = Executors.newFixedThreadPool(5)
+
+ override fun onComplicationUpdate(
+ complicationId: Int,
+ type: Int,
+ provideMockData: Boolean,
+ callback: ComplicationUpdateCallback
+ ) {
+ executor.execute {
+ callback.onUpdateComplication(
+ when (type) {
+ ComplicationData.TYPE_SHORT_TEXT ->
+ ComplicationData.Builder(type)
+ .setShortText(ComplicationText.plainText("# $complicationId"))
+ .build()
+
+ ComplicationData.TYPE_LONG_TEXT ->
+ ComplicationData.Builder(type)
+ .setLongText(ComplicationText.plainText("hello $complicationId"))
+ .build()
+
+ else -> null
+ }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/wear/wear-complications-data/samples/src/main/java/androidx/wear/complications/samples/BackgroundProviderService.kt b/wear/wear-complications-data/samples/src/main/java/androidx/wear/complications/samples/BackgroundProviderService.kt
new file mode 100644
index 0000000..afe8eab
--- /dev/null
+++ b/wear/wear-complications-data/samples/src/main/java/androidx/wear/complications/samples/BackgroundProviderService.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package androidx.wear.complications.samples
+
+import android.content.ComponentName
+import android.os.Handler
+import android.os.Looper
+import android.support.wearable.complications.ComplicationData
+import android.support.wearable.complications.ComplicationText
+import androidx.wear.complications.ComplicationProviderService
+import androidx.wear.complications.ProviderUpdateRequester
+
+const val UPDATE_CADEANCE_MS = 10000L
+
+var counter = 0
+var updateRequester: ProviderUpdateRequester? = null
+
+/** Example where we push updates to a counter every 10 seconds. */
+class BackgroundProviderService : ComplicationProviderService() {
+
+ private val handler = Handler(Looper.getMainLooper())
+
+ override fun onCreate() {
+ if (updateRequester == null) {
+ updateRequester = ProviderUpdateRequester(
+ this,
+ ComponentName(this, BackgroundProviderService::class.java)
+ )
+ }
+ }
+
+ override fun onComplicationActivated(complicationId: Int, type: Int) {
+ // Start requesting background updates.
+ backgroundUpdate()
+ }
+
+ private fun backgroundUpdate() {
+ counter++
+ updateRequester?.requestUpdateAll()
+ handler.postDelayed(this::backgroundUpdate, UPDATE_CADEANCE_MS)
+ }
+
+ override fun onComplicationUpdate(
+ complicationId: Int,
+ type: Int,
+ provideMockData: Boolean,
+ callback: ComplicationUpdateCallback
+ ) {
+ callback.onUpdateComplication(
+ when (type) {
+ ComplicationData.TYPE_SHORT_TEXT ->
+ ComplicationData.Builder(type)
+ .setShortText(ComplicationText.plainText("# $counter"))
+ .build()
+
+ ComplicationData.TYPE_LONG_TEXT ->
+ ComplicationData.Builder(type)
+ .setLongText(ComplicationText.plainText("Count $counter"))
+ .build()
+
+ else -> null
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/wear/wear-complications-data/samples/src/main/java/androidx/wear/complications/samples/SynchronousProviderService.kt b/wear/wear-complications-data/samples/src/main/java/androidx/wear/complications/samples/SynchronousProviderService.kt
new file mode 100644
index 0000000..bba72ec
--- /dev/null
+++ b/wear/wear-complications-data/samples/src/main/java/androidx/wear/complications/samples/SynchronousProviderService.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package androidx.wear.complications.samples
+
+import android.support.wearable.complications.ComplicationData
+import android.support.wearable.complications.ComplicationText
+import androidx.wear.complications.ComplicationProviderService
+
+/** A minimal complication provider which reports the ID of the complication immediately. */
+class SynchronousProviderService : ComplicationProviderService() {
+
+ override fun onComplicationUpdate(
+ complicationId: Int,
+ type: Int,
+ provideMockData: Boolean,
+ callback: ComplicationUpdateCallback
+ ) {
+ callback.onUpdateComplication(
+ when (type) {
+ ComplicationData.TYPE_SHORT_TEXT ->
+ ComplicationData.Builder(type)
+ .setShortText(ComplicationText.plainText("# $complicationId"))
+ .build()
+
+ ComplicationData.TYPE_LONG_TEXT ->
+ ComplicationData.Builder(type)
+ .setLongText(ComplicationText.plainText("hello $complicationId"))
+ .build()
+
+ else -> null
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/wear/wear-complications-data/samples/src/main/res/drawable/circle.xml b/wear/wear-complications-data/samples/src/main/res/drawable/circle.xml
new file mode 100644
index 0000000..d5dc5fa
--- /dev/null
+++ b/wear/wear-complications-data/samples/src/main/res/drawable/circle.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid />
+</shape>
\ No newline at end of file
diff --git a/wear/wear-complications-data/samples/src/main/res/values/strings.xml b/wear/wear-complications-data/samples/src/main/res/values/strings.xml
new file mode 100644
index 0000000..b9290d8
--- /dev/null
+++ b/wear/wear-complications-data/samples/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<resources>
+ <string name="app_name" translatable="false">Example Complication Provider</string>
+ <string name="asynchronous_provider_name" translatable="false">Asynchronous</string>
+ <string name="background_provider_name" translatable="false">Background</string>
+ <string name="synchronous_provider_name" translatable="false">Synchronous</string>
+</resources>
diff --git a/wear/wear-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java b/wear/wear-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
index 886ef20..68c933b 100644
--- a/wear/wear-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
+++ b/wear/wear-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
@@ -31,7 +31,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.wear.complications.ComplicationHelperActivity;
-import androidx.wear.complications.ComplicationManager;
import androidx.wear.complications.ComplicationProviderService;
import java.lang.annotation.Retention;
@@ -41,10 +40,10 @@
* Container for complication data of all types.
*
* <p>A {@link ComplicationProviderService} should create instances of this class using {@link
- * ComplicationData.Builder} and send them to the complication system by calling {@link
- * ComplicationManager#updateComplicationData}. Depending on the type of complication data, some
- * fields will be required and some will be optional - see the documentation for each type, and for
- * the builder's set methods, for details.
+ * ComplicationData.Builder} and send them to the complication system in response to {@link
+ * ComplicationProviderService#onComplicationUpdate}. Depending on the type of complication data,
+ * some fields will be required and some will be optional - see the documentation for each type, and
+ * for the builder's set methods, for details.
*
* <p>A watch face will receive instances of this class as long as providers are configured.
*
@@ -821,14 +820,17 @@
}
/**
- * Returns the start time for this complication data, this may be 0.
+ * Returns the start time for this complication data (i.e. the first time at which it should
+ * be considered active and displayed), this may be 0. See also {@link #isActiveAt(long)}.
*/
public long getStartDateTimeMillis() {
return mFields.getLong(FIELD_START_TIME, 0);
}
/**
- * Returns the end time for this complication data, this may be {@link Long#MAX_VALUE}.
+ * Returns the end time for this complication data (i.e. the last time at which it should be
+ * considered active and displayed), this may be {@link Long#MAX_VALUE}. See also {@link
+ * #isActiveAt(long)}.
*/
public long getEndDateTimeMillis() {
return mFields.getLong(FIELD_END_TIME, Long.MAX_VALUE);
@@ -992,7 +994,9 @@
/**
* Sets the <i>value</i> field. This is required for the {@link #TYPE_RANGED_VALUE} type,
- * and is not valid for any other type.
+ * and is not valid for any other type. A {@link #TYPE_RANGED_VALUE} complication
+ * visually presents a single value, which is usually a percentage. E.g. you
+ * have completed 70% of today's target of 10000 steps.
*
* <p>Returns this Builder to allow chaining.
*
@@ -1006,7 +1010,9 @@
/**
* Sets the <i>min value</i> field. This is required for the {@link #TYPE_RANGED_VALUE}
- * type, and is not valid for any other type.
+ * type, and is not valid for any other type. A {@link #TYPE_RANGED_VALUE} complication
+ * visually presents a single value, which is usually a percentage. E.g. you have
+ * completed 70% of today's target of 10000 steps.
*
* <p>Returns this Builder to allow chaining.
*
@@ -1020,7 +1026,9 @@
/**
* Sets the <i>max value</i> field. This is required for the {@link #TYPE_RANGED_VALUE}
- * type, and is not valid for any other type.
+ * type, and is not valid for any other type.A {@link #TYPE_RANGED_VALUE} complication
+ * visually presents a single value, which is usually a percentage. E.g. you have
+ * completed 70% of today's target of 10000 steps.
*
* <p>Returns this Builder to allow chaining.
*
diff --git a/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationManager.java b/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationManager.java
deleted file mode 100644
index c0ecee3..0000000
--- a/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationManager.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.wear.complications;
-
-import android.os.RemoteException;
-import android.support.wearable.complications.ComplicationData;
-import android.support.wearable.complications.IComplicationManager;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-/**
- * Allows providers to interact with the complications system.
- *
- * <p>Providers will receive an instance of this class in calls to {@link
- * ComplicationProviderService#onComplicationActivated onComplicationActivated} and {@link
- * ComplicationProviderService#onComplicationUpdate onComplicationUpdate}.
- */
-public class ComplicationManager {
-
- private static final String TAG = "ComplicationManager";
-
- @NonNull private final IComplicationManager mService;
-
- /** @hide */
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- public ComplicationManager(@NonNull IComplicationManager service) {
- mService = service;
- }
-
- /**
- * Updates complication data for the given {@code complicationId}. The type of data provided
- * must either match the configured type for the given complication, or should be of {@link
- * ComplicationData#TYPE_NO_DATA TYPE_NO_DATA}.
- *
- * <p>If no update is required (meaning that the complication will continue to display the
- * previous value, which may be TYPE_NO_DATA if no data has ever been sent), then null may be
- * passed as the data ({@link #noUpdateRequired} may be called to have the same effect).
- *
- * <p>A provider service should always call either this method or {@link #noUpdateRequired} in
- * response to an update request from the system.
- *
- * <p>This method is often called in response to {@link
- * ComplicationProviderService#onComplicationUpdate} but it can also be called when you have new
- * data to push.
- *
- * <p> If {@link ComplicationProviderService#inRetailMode} is true then representative mock data
- * should be returned rather than the real data.
- *
- * @param complicationId The ID of the complication to update
- * @param data The {@link ComplicationData} to send to the watch face
- * @throws RemoteException If it failed to send complication data
- */
- public void updateComplicationData(int complicationId, @NonNull ComplicationData data)
- throws RemoteException {
- if (data.getType() == ComplicationData.TYPE_NOT_CONFIGURED
- || data.getType() == ComplicationData.TYPE_EMPTY) {
- throw new IllegalArgumentException(
- "Cannot send data of TYPE_NOT_CONFIGURED or "
- + "TYPE_EMPTY. Use TYPE_NO_DATA instead.");
- }
- mService.updateComplicationData(complicationId, data);
- }
-
- /**
- * Informs the system that no update is required after an update request. This means that the
- * complication will continue to display the previous value, which may be TYPE_NO_DATA if no
- * data has ever been sent.
- *
- * <p>A provider service should always call either this method or {@link
- * #updateComplicationData} in response to an update request from the system.
- *
- * @param complicationId The ID of the complication
- * @throws RemoteException If it failed to send complication data
- */
- public void noUpdateRequired(int complicationId) throws RemoteException {
- mService.updateComplicationData(complicationId, null);
- }
-}
diff --git a/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationProviderService.java b/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationProviderService.java
index b2e0ab4..e738810 100644
--- a/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationProviderService.java
+++ b/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationProviderService.java
@@ -16,6 +16,8 @@
package androidx.wear.complications;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Service;
@@ -25,6 +27,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
import android.support.wearable.complications.ComplicationData;
import android.support.wearable.complications.ComplicationProviderInfo;
import android.support.wearable.complications.IComplicationManager;
@@ -33,6 +36,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
/**
* Class for providers of complication data.
@@ -265,6 +270,36 @@
private IComplicationProviderWrapper mWrapper;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+ /** @hide */
+ @RestrictTo(LIBRARY_GROUP)
+ interface RetailModeProvider {
+ /**
+ * Returns true if the device is currently running in retail mode (e.g. the watch is being
+ * demonstrated in a store, or the watch face is being configured by the system UI).
+ */
+ boolean inRetailMode();
+ }
+
+ private RetailModeProvider mRetailModeProvider = new RetailModeProvider() {
+ /**
+ * Returns true if the device is currently running in retail mode (e.g. the watch is being
+ * demonstrated in a store, or the watch face is being configured by the system UI).
+ */
+ @Override
+ public boolean inRetailMode() {
+ ComponentName component = new ComponentName(RETAIL_PACKAGE, RETAIL_CLASS);
+ return (getPackageManager().getComponentEnabledSetting(component)
+ == PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+ }
+ };
+
+ /** @hide */
+ @RestrictTo(LIBRARY_GROUP)
+ @VisibleForTesting
+ public void setRetailModeProvider(@NonNull RetailModeProvider retailModeProvider) {
+ mRetailModeProvider = retailModeProvider;
+ }
+
@SuppressLint("SyntheticAccessor")
@Override
@Nullable
@@ -284,35 +319,53 @@
* <p>This occurs when the watch face calls setActiveComplications, or when this provider is
* chosen for a complication which is already active.
*
- * <p>Once this has been called, complication data may be sent for the given {@code
- * complicationId}, until {@link #onComplicationDeactivated} is called for that id.
- *
* <p>This will usually be followed by a call to {@link #onComplicationUpdate}.
*
* <p>This will be called on the main thread.
*/
- public void onComplicationActivated(
- int complicationId, int type, @NonNull ComplicationManager manager) {
+ @UiThread
+ public void onComplicationActivated(int complicationId, int type) {
}
/**
* Called when a complication data update is requested for the given complication id.
*
- * <p>In response to this request, {@link ComplicationManager#updateComplicationData} should be
- * called on the provided {@link ComplicationManager} instance with the data to be displayed.
- * Or, if no update is needed, {@link ComplicationManager#noUpdateRequired} may be called
- * instead. One of these methods must be called so that the system knows when the provider has
- * finished responding to the request.
+ * <p>In response to this request the result callback should be called with the data to be
+ * displayed. If the request can not be fulfilled or no update is needed then null should be
+ * passed to the callback.
*
- * <p>This call does not need to happen from within this method, but it should be made
- * reasonably soon after the call to this method occurred. If a call does not occur within
- * around 20 seconds (exact timeout length subject to change), then the system will unbind from
- * this service which may cause your eventual update to not be received.
+ * <p>If provideMockData is true then representative mock data should be returned rather than
+ * the real data. E.g. A date could always be August 1st.
*
- * <p>This will be called on the main thread.
+ * <p>The callback doesn't have be called within onComplicationUpdate but it should be called
+ * soon after. If this does not occur within around 20 seconds (exact timeout length subject
+ * to change), then the system will unbind from this service which may cause your eventual
+ * update to not be received.
+ *
+ * @param complicationId The id of the requested complication. Note this ID is distinct from
+ * ids
+ * used by the watch face itself.
+ * @param type The type of complication data requested.
+ * @param provideMockData Whether or not mock or real data should be returned.
+ * @param resultCallback The callback to pass the result to the system.
*/
+ @UiThread
public abstract void onComplicationUpdate(
- int complicationId, int type, @NonNull ComplicationManager manager);
+ int complicationId,
+ @ComplicationData.ComplicationType int type,
+ boolean provideMockData,
+ @NonNull ComplicationUpdateCallback resultCallback);
+
+ /** Callback for {@link #onComplicationUpdate}. */
+ public interface ComplicationUpdateCallback {
+ /**
+ * Sends the complicationData to the system. If null is passed then any
+ * previous complication data will not be overwritten. Can be called on any thread. Should
+ * only be called once.
+ */
+ void onUpdateComplication(@Nullable ComplicationData complicationData)
+ throws RemoteException;
+ }
/**
* Called when a complication is deactivated.
@@ -321,34 +374,37 @@
* setActiveComplications and does not include the given complication (usually because the watch
* face has stopped displaying it).
*
- * <p>Once this has been called, no complication data should be sent for the given {@code
- * complicationId}, until {@link #onComplicationActivated} is called again for that id.
- *
* <p>This will be called on the main thread.
*/
+ @UiThread
public void onComplicationDeactivated(int complicationId) {
}
- /**
- * Returns true if the device is currently running in retail mode (e.g. the watch is being
- * demonstrated in a store, or the watch face is being configured by the system UI). If it's in
- * retail mode then representative mock data should be returned via
- * {@link ComplicationManager#updateComplicationData}.
- */
- protected boolean inRetailMode() {
- ComponentName component = new ComponentName(RETAIL_PACKAGE, RETAIL_CLASS);
- return (getPackageManager().getComponentEnabledSetting(component)
- == PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
- }
-
private class IComplicationProviderWrapper extends IComplicationProvider.Stub {
@SuppressLint("SyntheticAccessor")
@Override
public void onUpdate(final int complicationId, final int type, IBinder manager) {
- final ComplicationManager complicationManager =
- new ComplicationManager(IComplicationManager.Stub.asInterface(manager));
+ final IComplicationManager iComplicationManager =
+ IComplicationManager.Stub.asInterface(manager);
+
mMainThreadHandler.post(
- () -> onComplicationUpdate(complicationId, type, complicationManager));
+ () -> onComplicationUpdate(complicationId, type,
+ mRetailModeProvider.inRetailMode(),
+ complicationData -> {
+ // This can be run on an arbitrary thread, but that's OK.
+ int dataType =
+ complicationData != null ? complicationData.getType() :
+ ComplicationData.TYPE_NO_DATA;
+ if (dataType == ComplicationData.TYPE_NOT_CONFIGURED
+ || dataType == ComplicationData.TYPE_EMPTY) {
+ throw new IllegalArgumentException(
+ "Cannot send data of TYPE_NOT_CONFIGURED or "
+ + "TYPE_EMPTY. Use TYPE_NO_DATA instead.");
+ }
+
+ iComplicationManager.updateComplicationData(
+ complicationId, complicationData);
+ }));
}
@Override
@@ -363,11 +419,9 @@
@SuppressLint("SyntheticAccessor")
public void onComplicationActivated(
final int complicationId, final int type, IBinder manager) {
- final ComplicationManager complicationManager =
- new ComplicationManager(IComplicationManager.Stub.asInterface(manager));
mMainThreadHandler.post(
() -> ComplicationProviderService.this.onComplicationActivated(
- complicationId, type, complicationManager));
+ complicationId, type));
}
}
}
diff --git a/wear/wear-complications-data/src/main/java/androidx/wear/complications/ProviderUpdateRequester.java b/wear/wear-complications-data/src/main/java/androidx/wear/complications/ProviderUpdateRequester.java
index 203c1e5..31303bd 100644
--- a/wear/wear-complications-data/src/main/java/androidx/wear/complications/ProviderUpdateRequester.java
+++ b/wear/wear-complications-data/src/main/java/androidx/wear/complications/ProviderUpdateRequester.java
@@ -34,7 +34,9 @@
/** The package of the service that accepts provider requests. */
private static final String UPDATE_REQUEST_RECEIVER_PACKAGE = "com.google.android.wearable.app";
- private static final String ACTION_REQUEST_UPDATE =
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public static final String ACTION_REQUEST_UPDATE =
"android.support.wearable.complications.ACTION_REQUEST_UPDATE";
/** @hide */
diff --git a/wear/wear-complications-data/src/test/java/androidx/wear/complications/ComplicationManagerTest.java b/wear/wear-complications-data/src/test/java/androidx/wear/complications/ComplicationManagerTest.java
deleted file mode 100644
index e5c7d4f..0000000
--- a/wear/wear-complications-data/src/test/java/androidx/wear/complications/ComplicationManagerTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.wear.complications;
-
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import android.os.RemoteException;
-import android.support.wearable.complications.ComplicationData;
-import android.support.wearable.complications.ComplicationText;
-import android.support.wearable.complications.IComplicationManager;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.internal.DoNotInstrument;
-
-/** Tests for {@link ComplicationManager}. */
-@RunWith(ComplicationsTestRunner.class)
-@DoNotInstrument
-public class ComplicationManagerTest {
-
- @Mock private IComplicationManager mRemoteManager;
- private ComplicationManager mManagerUnderTest;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mManagerUnderTest = new ComplicationManager(mRemoteManager);
- }
-
- @Test
- public void testUpdateComplicationData() throws RemoteException {
- // GIVEN a complication id and a ComplicationData object...
- int id = 5;
- ComplicationData data =
- new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(ComplicationText.plainText("hello"))
- .build();
-
- // WHEN updateComplicationManager is called with that id and data...
- mManagerUnderTest.updateComplicationData(id, data);
- try {
- // THEN updateComplicationData is called on the wrapped IComplicationManager instance
- // for the same id and with the expected data.
- verify(mRemoteManager).updateComplicationData(eq(id), eq(data));
- } catch (RemoteException e) {
- fail("RemoteException");
- }
- }
-}
diff --git a/wear/wear-complications-data/src/test/java/androidx/wear/complications/ComplicationProviderServiceTest.java b/wear/wear-complications-data/src/test/java/androidx/wear/complications/ComplicationProviderServiceTest.java
new file mode 100644
index 0000000..8667c45
--- /dev/null
+++ b/wear/wear-complications-data/src/test/java/androidx/wear/complications/ComplicationProviderServiceTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package androidx.wear.complications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.os.RemoteException;
+import android.support.wearable.complications.ComplicationData;
+import android.support.wearable.complications.ComplicationText;
+import android.support.wearable.complications.IComplicationManager;
+import android.support.wearable.complications.IComplicationProvider;
+import android.util.Log;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.internal.DoNotInstrument;
+import org.robolectric.shadows.ShadowLooper;
+
+/** Tests for {@link ComplicationProviderService}. */
+@RunWith(ComplicationsTestRunner.class)
+@DoNotInstrument
+public class ComplicationProviderServiceTest {
+
+ private static final String TAG = "ComplicationProviderServiceTest";
+
+ @Mock
+ private IComplicationManager mRemoteManager;
+ private IComplicationManager.Stub mLocalManager = new IComplicationManager.Stub() {
+ @Override
+ public void updateComplicationData(int complicationId, ComplicationData data)
+ throws RemoteException {
+ mRemoteManager.updateComplicationData(complicationId, data);
+ }
+ };
+
+ private IComplicationProvider.Stub mComplicationProvider;
+
+ private ComplicationProviderService mTestService = new ComplicationProviderService() {
+ private CharSequence mText = "Hello";
+
+ @Override
+ public void onComplicationUpdate(int complicationId, int type,
+ boolean provideMockData, ComplicationUpdateCallback callback) {
+ try {
+ callback.onUpdateComplication(
+ new ComplicationData.Builder(ComplicationData.TYPE_LONG_TEXT)
+ .setLongText(ComplicationText.plainText("hello " + complicationId))
+ .build());
+ } catch (RemoteException e) {
+ Log.e(TAG, "onComplicationUpdate failed with error: ", e);
+ }
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mComplicationProvider =
+ (IComplicationProvider.Stub) mTestService.onBind(
+ new Intent(ComplicationProviderService.ACTION_COMPLICATION_UPDATE_REQUEST));
+ mTestService.setRetailModeProvider(() -> false);
+ }
+
+ @Test
+ public void testOnComplicationUpdate() throws Exception {
+ int id = 123;
+ mComplicationProvider.onUpdate(id, ComplicationData.TYPE_LONG_TEXT, mLocalManager);
+ ShadowLooper.runUiThreadTasks();
+ ArgumentCaptor<ComplicationData> data = ArgumentCaptor.forClass(ComplicationData.class);
+ verify(mRemoteManager).updateComplicationData(eq(id), data.capture());
+ assertThat(data.getValue().getLongText().getTextAt(null, 0)).isEqualTo(
+ "hello " + id
+ );
+ }
+}
diff --git a/wear/wear-complications-rendering/src/test/java/androidx/wear/complications/rendering/ComplicationDrawableTest.java b/wear/wear-complications-rendering/src/test/java/androidx/wear/complications/rendering/ComplicationDrawableTest.java
index 0da01ad..8e63b02 100644
--- a/wear/wear-complications-rendering/src/test/java/androidx/wear/complications/rendering/ComplicationDrawableTest.java
+++ b/wear/wear-complications-rendering/src/test/java/androidx/wear/complications/rendering/ComplicationDrawableTest.java
@@ -58,7 +58,7 @@
import androidx.wear.watchface.WatchFaceService;
import androidx.wear.watchface.WatchFaceType;
import androidx.wear.watchface.WatchState;
-import androidx.wear.watchface.style.UserStyleManager;
+import androidx.wear.watchface.style.UserStyleRepository;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
@@ -702,13 +702,13 @@
@NotNull SurfaceHolder surfaceHolder,
@NotNull WatchFaceHost watchFaceHost,
@NotNull WatchState watchState) {
- UserStyleManager styleManager = new UserStyleManager(new ArrayList<>());
+ UserStyleRepository userStyleRepository = new UserStyleRepository(new ArrayList<>());
return new WatchFace.Builder(
WatchFaceType.ANALOG,
100,
- styleManager,
+ userStyleRepository,
new ComplicationsHolder(new ArrayList<>()),
- new Renderer(surfaceHolder, styleManager, watchState) {
+ new Renderer(surfaceHolder, userStyleRepository, watchState) {
@NotNull
@Override
public Bitmap takeScreenshot$wear_watchface_debug(
diff --git a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleCategory.kt b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleCategory.kt
index 1ac9f9f..3515775 100644
--- a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleCategory.kt
+++ b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleCategory.kt
@@ -90,7 +90,7 @@
bundle.getString(KEY_DISPLAY_NAME)!!,
bundle.getString(KEY_DESCRIPTION)!!,
bundle.getParcelable(KEY_ICON),
- UserStyleManager.readOptionsListFromBundle(bundle),
+ UserStyleRepository.readOptionsListFromBundle(bundle),
Option.createFromBundle(bundle.getBundle(KEY_DEFAULT_OPTION)!!)
)
@@ -105,7 +105,7 @@
KEY_DEFAULT_OPTION,
Bundle().apply { defaultOption.writeToBundle(this) }
)
- UserStyleManager.writeOptionListToBundle(options, bundle)
+ UserStyleRepository.writeOptionListToBundle(options, bundle)
}
/**
diff --git a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleManager.kt b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleRepository.kt
similarity index 99%
rename from wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleManager.kt
rename to wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleRepository.kt
index 7fdf822..3d3372b 100644
--- a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleManager.kt
+++ b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleRepository.kt
@@ -24,7 +24,7 @@
* In memory storage for user style choices which allows listeners to be registered to observe
* style changes.
*/
-class UserStyleManager(
+class UserStyleRepository(
/**
* The style categories (i.e the style schema) associated with this watch face, that the user
* can configure. May be empty. The first entry in each Option list is that category's default
diff --git a/wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleManagerTest.kt b/wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleRepositoryTest.kt
similarity index 82%
rename from wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleManagerTest.kt
rename to wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleRepositoryTest.kt
index e588a15..63672f9 100644
--- a/wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleManagerTest.kt
+++ b/wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleRepositoryTest.kt
@@ -36,7 +36,7 @@
}
@RunWith(UserStyleManagerTestRunner::class)
-class UserStyleManagerTest {
+class UserStyleRepositoryTest {
private val redStyleOption =
ListUserStyleCategory.ListOption("red_style", "Red", icon = null)
@@ -76,11 +76,12 @@
watchHandStyleList
)
- private val mockListener1 = Mockito.mock(UserStyleManager.UserStyleListener::class.java)
- private val mockListener2 = Mockito.mock(UserStyleManager.UserStyleListener::class.java)
- private val mockListener3 = Mockito.mock(UserStyleManager.UserStyleListener::class.java)
+ private val mockListener1 = Mockito.mock(UserStyleRepository.UserStyleListener::class.java)
+ private val mockListener2 = Mockito.mock(UserStyleRepository.UserStyleListener::class.java)
+ private val mockListener3 = Mockito.mock(UserStyleRepository.UserStyleListener::class.java)
- private val styleManager = UserStyleManager(listOf(colorStyleCategory, watchHandStyleCategory))
+ private val userStyleRepository =
+ UserStyleRepository(listOf(colorStyleCategory, watchHandStyleCategory))
private val icon1 = Icon.createWithContentUri("icon1")
private val icon2 = Icon.createWithContentUri("icon2")
@@ -93,19 +94,19 @@
@Test
fun addUserStyleListener_firesImmediately() {
- styleManager.addUserStyleListener(mockListener1)
- Mockito.verify(mockListener1).onUserStyleChanged(styleManager.userStyle)
+ userStyleRepository.addUserStyleListener(mockListener1)
+ Mockito.verify(mockListener1).onUserStyleChanged(userStyleRepository.userStyle)
}
@Test
fun assigning_userStyle_firesListeners() {
- styleManager.addUserStyleListener(mockListener1)
- styleManager.addUserStyleListener(mockListener2)
- styleManager.addUserStyleListener(mockListener3)
+ userStyleRepository.addUserStyleListener(mockListener1)
+ userStyleRepository.addUserStyleListener(mockListener2)
+ userStyleRepository.addUserStyleListener(mockListener3)
- Mockito.verify(mockListener1).onUserStyleChanged(styleManager.userStyle)
- Mockito.verify(mockListener2).onUserStyleChanged(styleManager.userStyle)
- Mockito.verify(mockListener3).onUserStyleChanged(styleManager.userStyle)
+ Mockito.verify(mockListener1).onUserStyleChanged(userStyleRepository.userStyle)
+ Mockito.verify(mockListener2).onUserStyleChanged(userStyleRepository.userStyle)
+ Mockito.verify(mockListener3).onUserStyleChanged(userStyleRepository.userStyle)
val newStyle: Map<UserStyleCategory, UserStyleCategory.Option> = mapOf(
colorStyleCategory to greenStyleOption,
@@ -116,11 +117,11 @@
Mockito.reset(mockListener2)
Mockito.reset(mockListener3)
- styleManager.userStyle = newStyle
+ userStyleRepository.userStyle = newStyle
- Mockito.verify(mockListener1).onUserStyleChanged(styleManager.userStyle)
- Mockito.verify(mockListener2).onUserStyleChanged(styleManager.userStyle)
- Mockito.verify(mockListener3).onUserStyleChanged(styleManager.userStyle)
+ Mockito.verify(mockListener1).onUserStyleChanged(userStyleRepository.userStyle)
+ Mockito.verify(mockListener2).onUserStyleChanged(userStyleRepository.userStyle)
+ Mockito.verify(mockListener3).onUserStyleChanged(userStyleRepository.userStyle)
}
@Test
@@ -130,10 +131,10 @@
watchHandStyleCategory to gothicStyleOption
)
- styleManager.userStyle = newStyle
+ userStyleRepository.userStyle = newStyle
- assertThat(styleManager.userStyle[colorStyleCategory]).isEqualTo(greenStyleOption)
- assertThat(styleManager.userStyle[watchHandStyleCategory])
+ assertThat(userStyleRepository.userStyle[colorStyleCategory]).isEqualTo(greenStyleOption)
+ assertThat(userStyleRepository.userStyle[watchHandStyleCategory])
.isEqualTo(gothicStyleOption)
}
@@ -172,9 +173,9 @@
@Test
fun bundleAndUnbundleOptionList() {
val bundle = Bundle()
- UserStyleManager.writeOptionListToBundle(listOf(option1, option2, option3), bundle)
+ UserStyleRepository.writeOptionListToBundle(listOf(option1, option2, option3), bundle)
- val unbundled = UserStyleManager.readOptionsListFromBundle(bundle)
+ val unbundled = UserStyleRepository.readOptionsListFromBundle(bundle)
val optionArray = unbundled.filterIsInstance<ListUserStyleCategory.ListOption>()
.toTypedArray()
@@ -201,11 +202,11 @@
"id2", "displayName2", "description2", categoryIcon2, listOf(option3, option4)
)
- val bundles = UserStyleManager.userStyleCategoriesToBundles(
+ val bundles = UserStyleRepository.userStyleCategoriesToBundles(
listOf(styleCategory1, styleCategory2)
)
- val unbundled = UserStyleManager.bundlesToUserStyleCategoryList(bundles)
+ val unbundled = UserStyleRepository.bundlesToUserStyleCategoryList(bundles)
assert(unbundled[0] is ListUserStyleCategory)
assertThat(unbundled[0].id).isEqualTo("id1")
@@ -255,9 +256,9 @@
styleCategory1 as UserStyleCategory to option2 as UserStyleCategory.Option,
styleCategory2 as UserStyleCategory to option3 as UserStyleCategory.Option
)
- val bundle = UserStyleManager.styleMapToBundle(styleMap)
+ val bundle = UserStyleRepository.styleMapToBundle(styleMap)
- val unbundled = UserStyleManager.bundleToStyleMap(bundle, schema)
+ val unbundled = UserStyleRepository.bundleToStyleMap(bundle, schema)
assertThat(unbundled.size).isEqualTo(2)
assertThat(unbundled[styleCategory1]!!.id).isEqualTo(option2.id)
assertThat(unbundled[styleCategory2]!!.id).isEqualTo(option3.id)
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt
index 4ac16c6..37b30c4 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt
@@ -43,7 +43,7 @@
import androidx.wear.watchface.style.DoubleRangeUserStyleCategory
import androidx.wear.watchface.style.ListUserStyleCategory
import androidx.wear.watchface.style.UserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import kotlin.math.cos
import kotlin.math.sin
@@ -108,7 +108,7 @@
1.0,
0.75
)
- val styleManager = UserStyleManager(
+ val userStyleRepository = UserStyleRepository(
listOf(colorStyleCategory, drawHourPipsStyleCategory, watchHandLengthStyleCategory)
)
val complicationSlots = ComplicationsHolder(
@@ -147,7 +147,7 @@
surfaceHolder,
this,
watchFaceStyle,
- styleManager,
+ userStyleRepository,
watchState,
colorStyleCategory,
drawHourPipsStyleCategory,
@@ -159,7 +159,7 @@
WatchFaceType.ANALOG,
/** mInteractiveUpdateRateMillis */
16,
- styleManager,
+ userStyleRepository,
complicationSlots,
renderer,
watchFaceHost,
@@ -175,13 +175,13 @@
surfaceHolder: SurfaceHolder,
private val context: Context,
private var watchFaceColorStyle: WatchFaceColorStyle,
- userStyleManager: UserStyleManager,
+ userStyleRepository: UserStyleRepository,
private val watchState: WatchState,
private val colorStyleCategory: ListUserStyleCategory,
private val drawPipsStyleCategory: BooleanUserStyleCategory,
private val watchHandLengthStyleCategoryDouble: DoubleRangeUserStyleCategory,
private val complicationsHolder: ComplicationsHolder
-) : CanvasRenderer(surfaceHolder, userStyleManager, watchState, CanvasType.HARDWARE) {
+) : CanvasRenderer(surfaceHolder, userStyleRepository, watchState, CanvasType.HARDWARE) {
private val clockHandPaint = Paint().apply {
isAntiAlias = true
@@ -225,8 +225,8 @@
private var watchHandScale = 1.0f
init {
- userStyleManager.addUserStyleListener(
- object : UserStyleManager.UserStyleListener {
+ userStyleRepository.addUserStyleListener(
+ object : UserStyleRepository.UserStyleListener {
@SuppressLint("SyntheticAccessor")
override fun onUserStyleChanged(
userStyle: Map<UserStyleCategory, UserStyleCategory.Option>
@@ -234,8 +234,9 @@
watchFaceColorStyle =
WatchFaceColorStyle.create(context, userStyle[colorStyleCategory]!!.id)
- // Apply the userStyle to the complications. ComplicationDrawables for each of the
- // styles are defined in XML so we need to replace the complication's drawables.
+ // Apply the userStyle to the complications. ComplicationDrawables for each of
+ // the styles are defined in XML so we need to replace the complication's
+ // drawables.
for ((_, complication) in complicationsHolder.complications) {
complication.renderer =
watchFaceColorStyle.getComplicationDrawableRenderer(context, watchState)
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
index 499c6de..e658b9d 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
@@ -34,7 +34,7 @@
import androidx.wear.watchface.WatchFaceType
import androidx.wear.watchface.WatchState
import androidx.wear.watchface.style.ListUserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
@@ -85,18 +85,18 @@
)
)
)
- val styleManager = UserStyleManager(listOf(colorStyleCategory))
+ val userStyleRepository = UserStyleRepository(listOf(colorStyleCategory))
val complicationSlots = ComplicationsHolder(emptyList())
val renderer = ExampleOpenGLRenderer(
surfaceHolder,
- styleManager,
+ userStyleRepository,
watchState,
colorStyleCategory
)
return WatchFace.Builder(
WatchFaceType.ANALOG,
FRAME_PERIOD_MS,
- styleManager,
+ userStyleRepository,
complicationSlots,
renderer,
watchFaceHost,
@@ -107,10 +107,10 @@
class ExampleOpenGLRenderer(
surfaceHolder: SurfaceHolder,
- private val userStyleManager: UserStyleManager,
+ private val userStyleRepository: UserStyleRepository,
watchState: WatchState,
private val colorStyleCategory: ListUserStyleCategory
-) : Gles2Renderer(surfaceHolder, userStyleManager, watchState) {
+) : Gles2Renderer(surfaceHolder, userStyleRepository, watchState) {
/** Projection transformation matrix. Converts from 3D to 2D. */
private val projectionMatrix = FloatArray(16)
@@ -445,7 +445,7 @@
GLES20.glClearColor(0f, 0f, 0f, 1f)
ambientVpMatrix
} else {
- when (userStyleManager.userStyle[colorStyleCategory]!!.id) {
+ when (userStyleRepository.userStyle[colorStyleCategory]!!.id) {
"red_style" -> GLES20.glClearColor(0.5f, 0.2f, 0.2f, 1f)
"green_style" -> GLES20.glClearColor(0.2f, 0.5f, 0.2f, 1f)
}
@@ -491,7 +491,7 @@
modelMatrices[secIndex],
0
)
- secondHandTriangleMap[userStyleManager.userStyle[colorStyleCategory]!!.id]
+ secondHandTriangleMap[userStyleRepository.userStyle[colorStyleCategory]!!.id]
?.draw(mvpMatrix)
}
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
index 813b5b4..df05369 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
@@ -39,7 +39,7 @@
import androidx.wear.watchface.WatchState
import androidx.wear.watchface.style.ListUserStyleCategory
import androidx.wear.watchface.style.UserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
@Sampled
fun kDocCreateExampleWatchFaceService(): WatchFaceService {
@@ -50,7 +50,7 @@
watchFaceHost: WatchFaceHost,
watchState: WatchState
): WatchFace {
- val styleManager = UserStyleManager(
+ val userStyleRepository = UserStyleRepository(
listOf(
ListUserStyleCategory(
"color_style_category",
@@ -137,19 +137,20 @@
val renderer = object : CanvasRenderer(
surfaceHolder,
- styleManager,
+ userStyleRepository,
watchState,
CanvasType.HARDWARE
) {
init {
- styleManager.addUserStyleListener(object : UserStyleManager.UserStyleListener {
- override fun onUserStyleChanged(
- userStyle: Map<UserStyleCategory, UserStyleCategory.Option>
- ) {
- // `userStyle` will contain two userStyle categories with options from
- // the lists above. ...
- }
- })
+ userStyleRepository.addUserStyleListener(
+ object : UserStyleRepository.UserStyleListener {
+ override fun onUserStyleChanged(
+ userStyle: Map<UserStyleCategory, UserStyleCategory.Option>
+ ) {
+ // `userStyle` will contain two userStyle categories with options
+ // from the lists above. ...
+ }
+ })
}
override fun render(
@@ -164,7 +165,7 @@
return WatchFace.Builder(
WatchFaceType.ANALOG,
/* interactiveUpdateRateMillis */ 16,
- styleManager,
+ userStyleRepository,
complicationSlots,
renderer,
watchFaceHost,
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt
index 3ec41bd..2887919 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt
@@ -39,7 +39,7 @@
import androidx.wear.watchface.style.BooleanUserStyleCategory
import androidx.wear.watchface.style.DoubleRangeUserStyleCategory
import androidx.wear.watchface.style.ListUserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
/** A simple canvas test watch face for integration tests. */
internal class TestCanvasWatchFaceService(
@@ -97,7 +97,7 @@
1.0,
1.0
)
- val styleManager = UserStyleManager(
+ val userStyleRepository = UserStyleRepository(
listOf(colorStyleCategory, drawHourPipsStyleCategory, watchHandLengthStyleCategory)
)
val complicationSlots = ComplicationsHolder(
@@ -136,7 +136,7 @@
surfaceHolder,
this,
watchFaceStyle,
- styleManager,
+ userStyleRepository,
watchState,
colorStyleCategory,
drawHourPipsStyleCategory,
@@ -147,7 +147,7 @@
return WatchFace.Builder(
WatchFaceType.ANALOG,
16,
- styleManager,
+ userStyleRepository,
complicationSlots,
renderer,
// We want full control over when frames are produced.
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGles2WatchFaceService.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGles2WatchFaceService.kt
index c00c0e6..b6945db 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGles2WatchFaceService.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGles2WatchFaceService.kt
@@ -30,7 +30,7 @@
import androidx.wear.watchface.samples.ExampleOpenGLRenderer
import androidx.wear.watchface.samples.R
import androidx.wear.watchface.style.ListUserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
/** A simple OpenGL test watch face for integration tests. */
internal class TestGles2WatchFaceService(
@@ -69,11 +69,11 @@
)
)
)
- val styleManager = UserStyleManager(listOf(colorStyleCategory))
+ val userStyleRepository = UserStyleRepository(listOf(colorStyleCategory))
val complicationSlots = ComplicationsHolder(emptyList())
val renderer = ExampleOpenGLRenderer(
surfaceHolder,
- styleManager,
+ userStyleRepository,
watchState,
colorStyleCategory
)
@@ -81,7 +81,7 @@
return WatchFace.Builder(
WatchFaceType.ANALOG,
16,
- styleManager,
+ userStyleRepository,
complicationSlots,
renderer,
// We want full control over when frames are produced.
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasRenderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasRenderer.kt
index caf9661..be7f6ab 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasRenderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasRenderer.kt
@@ -24,7 +24,7 @@
import android.view.SurfaceHolder
import androidx.annotation.IntDef
import androidx.annotation.UiThread
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
/** @hide */
@IntDef(
@@ -51,15 +51,15 @@
/** The {@link SurfaceHolder} that {@link onDraw} will draw into. */
surfaceHolder: SurfaceHolder,
- /** The associated {@link UserStyleManager}. */
- userStyleManager: UserStyleManager,
+ /** The associated {@link UserStyleRepository}. */
+ userStyleRepository: UserStyleRepository,
/** The associated {@link WatchState}. */
watchState: WatchState,
/** The type of canvas to use. */
@CanvasType private val canvasType: Int
-) : Renderer(surfaceHolder, userStyleManager, watchState) {
+) : Renderer(surfaceHolder, userStyleRepository, watchState) {
internal override fun renderInternal(
calendar: Calendar
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
index 872747c..33f83bd 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
@@ -308,6 +308,7 @@
private var _enabled = true
var enabled: Boolean
+ @JvmName("isEnabled")
@UiThread
get() = _enabled
@UiThread
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Gles2Renderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Gles2Renderer.kt
index 1f47904..aa543ba 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Gles2Renderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Gles2Renderer.kt
@@ -28,7 +28,7 @@
import android.view.SurfaceHolder
import androidx.annotation.CallSuper
import androidx.annotation.UiThread
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import java.nio.ByteBuffer
@@ -56,12 +56,12 @@
/** The {@link SurfaceHolder} that {@link onDraw} will draw into. */
surfaceHolder: SurfaceHolder,
- /** The associated {@link UserStyleManager}. */
- userStyleManager: UserStyleManager,
+ /** The associated {@link UserStyleRepository}. */
+ userStyleRepository: UserStyleRepository,
/** The associated {@link WatchState}. */
watchState: WatchState
-) : Renderer(surfaceHolder, userStyleManager, watchState) {
+) : Renderer(surfaceHolder, userStyleRepository, watchState) {
private companion object {
private const val TAG = "Gles2WatchFace"
}
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
index 8266444..f7f6461 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
@@ -22,15 +22,15 @@
import android.view.SurfaceHolder
import androidx.annotation.CallSuper
import androidx.annotation.UiThread
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
/** The base class for {@link CanvasRenderer} and {@link Gles2Renderer}. */
abstract class Renderer(
/** The {@link SurfaceHolder} that {@link onDraw} will draw into. */
_surfaceHolder: SurfaceHolder,
- /** The associated {@link UserStyleManager}. */
- internal val userStyleManager: UserStyleManager,
+ /** The associated {@link UserStyleRepository}. */
+ internal val userStyleRepository: UserStyleRepository,
/** The associated {@link WatchState}. */
internal val watchState: WatchState
@@ -70,7 +70,7 @@
/**
* Renders the watch face into the {@link #surfaceHolder} using the current {@link #drawMode}
- * with the user style specified by the {@link #userStyleManager}.
+ * with the user style specified by the {@link #userStyleRepository}.
*
* @param calendar The Calendar to use when rendering the watch face
* @return A {@link Bitmap} containing a screenshot of the watch face
@@ -80,7 +80,7 @@
/**
* Renders the watch face into a Bitmap with the user style specified by the
- * {@link #userStyleManager}.
+ * {@link #userStyleRepository}.
*
* @param calendar The Calendar to use when rendering the watch face
* @param drawMode The {@link DrawMode} to use when rendering the watch face
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 abff28e..c5707bf 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
@@ -41,7 +41,7 @@
import androidx.annotation.VisibleForTesting
import androidx.wear.complications.SystemProviders
import androidx.wear.watchface.style.UserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import androidx.wear.watchface.ui.WatchFaceConfigActivity
import androidx.wear.watchface.ui.WatchFaceConfigDelegate
import java.io.FileNotFoundException
@@ -112,7 +112,7 @@
class WatchFace private constructor(
@WatchFaceType private val watchFaceType: Int,
private var interactiveUpdateRateMillis: Long,
- internal val userStyleManager: UserStyleManager,
+ internal val userStyleRepository: UserStyleRepository,
internal var complicationsHolder: ComplicationsHolder,
internal val renderer: Renderer,
private val watchFaceHostApi: WatchFaceHostApi,
@@ -149,8 +149,8 @@
*/
private var interactiveUpdateRateMillis: Long,
- /** The {@UserStyleManager} for this WatchFace. */
- internal val userStyleManager: UserStyleManager,
+ /** The {@UserStyleRepository} for this WatchFace. */
+ internal val userStyleRepository: UserStyleRepository,
/** The {@link ComplicationsHolder} for this WatchFace. */
internal var complicationsHolder: ComplicationsHolder,
@@ -273,7 +273,7 @@
return WatchFace(
watchFaceType,
interactiveUpdateRateMillis,
- userStyleManager,
+ userStyleRepository,
complicationsHolder,
renderer,
watchFaceHost.api!!,
@@ -415,33 +415,34 @@
// If the system has a stored user style then Home/SysUI is in charge of style
// persistence, otherwise we need to do our own.
val storedUserStyle =
- watchFaceHostApi.getStoredUserStyle(userStyleManager.userStyleCategories)
+ watchFaceHostApi.getStoredUserStyle(userStyleRepository.userStyleCategories)
if (storedUserStyle != null) {
- userStyleManager.userStyle = storedUserStyle
+ userStyleRepository.userStyle = storedUserStyle
} else {
// The system doesn't support preference persistence we need to do it ourselves.
val preferencesFile =
"watchface_prefs_${watchFaceHostApi.getContext().javaClass.typeName}.txt"
- userStyleManager.userStyle = UserStyleManager.idMapToStyleMap(
+ userStyleRepository.userStyle = UserStyleRepository.idMapToStyleMap(
readPrefs(watchFaceHostApi.getContext(), preferencesFile),
- userStyleManager.userStyleCategories
+ userStyleRepository.userStyleCategories
)
- userStyleManager.addUserStyleListener(object : UserStyleManager.UserStyleListener {
- @SuppressLint("SyntheticAccessor")
- override fun onUserStyleChanged(
- userStyle: Map<UserStyleCategory, UserStyleCategory.Option>
- ) {
- writePrefs(watchFaceHostApi.getContext(), preferencesFile, userStyle)
- }
- })
+ userStyleRepository.addUserStyleListener(
+ object : UserStyleRepository.UserStyleListener {
+ @SuppressLint("SyntheticAccessor")
+ override fun onUserStyleChanged(
+ userStyle: Map<UserStyleCategory, UserStyleCategory.Option>
+ ) {
+ writePrefs(watchFaceHostApi.getContext(), preferencesFile, userStyle)
+ }
+ })
}
}
private var inOnSetStyle = false
- private inner class WfUserStyleListener : UserStyleManager.UserStyleListener {
+ private inner class WfUserStyleListener : UserStyleRepository.UserStyleListener {
@SuppressWarnings("SyntheticAccessor")
override fun onUserStyleChanged(
userStyle: Map<UserStyleCategory, UserStyleCategory.Option>
@@ -523,16 +524,19 @@
WatchFaceConfigActivity.registerWatchFace(componentName, object : WatchFaceConfigDelegate {
override fun getUserStyleSchema() =
- UserStyleManager.userStyleCategoriesToBundles(
- userStyleManager.userStyleCategories
+ UserStyleRepository.userStyleCategoriesToBundles(
+ userStyleRepository.userStyleCategories
)
override fun getUserStyle() =
- UserStyleManager.styleMapToBundle(userStyleManager.userStyle)
+ UserStyleRepository.styleMapToBundle(userStyleRepository.userStyle)
override fun setUserStyle(style: Bundle) {
- userStyleManager.userStyle =
- UserStyleManager.bundleToStyleMap(style, userStyleManager.userStyleCategories)
+ userStyleRepository.userStyle =
+ UserStyleRepository.bundleToStyleMap(
+ style,
+ userStyleRepository.userStyleCategories
+ )
}
override fun getBackgroundComplicationId() =
@@ -566,11 +570,11 @@
})
watchFaceHostApi.registerWatchFaceType(watchFaceType)
- watchFaceHostApi.registerUserStyleSchema(userStyleManager.userStyleCategories)
+ watchFaceHostApi.registerUserStyleSchema(userStyleRepository.userStyleCategories)
watchState.addListener(systemStateListener)
- userStyleManager.addUserStyleListener(styleListener)
- sendCurrentUserStyle(userStyleManager.userStyle)
+ userStyleRepository.addUserStyleListener(styleListener)
+ sendCurrentUserStyle(userStyleRepository.userStyle)
initFinished = true
}
@@ -581,7 +585,7 @@
internal fun onSetStyleInternal(style: Map<UserStyleCategory, UserStyleCategory.Option>) {
// No need to echo the userStyle back.
inOnSetStyle = true
- userStyleManager.userStyle = style
+ userStyleRepository.userStyle = style
inOnSetStyle = false
}
@@ -591,7 +595,7 @@
pendingPostDoubleTap.cancel()
renderer.onDestroy()
watchState.removeListener(systemStateListener)
- userStyleManager.removeUserStyleListener(styleListener)
+ userStyleRepository.removeUserStyleListener(styleListener)
WatchFaceConfigActivity.unregisterWatchFace(componentName)
}
@@ -711,7 +715,7 @@
/** @hide */
@UiThread
internal fun computeDelayTillNextFrame(beginFrameTimeMillis: Long, currentTimeMillis: Long):
- Long {
+ Long {
// Limit update rate to conserve power when the battery is low and not charging.
val updateRateMillis =
if (watchState.isBatteryLowAndNotCharging) {
@@ -719,7 +723,10 @@
} else {
interactiveUpdateRateMillis
}
- var nextFrameTimeMillis = beginFrameTimeMillis + updateRateMillis
+ // Note beginFrameTimeMillis could be in the future if the user adjusted the time so we need
+ // to compute min(beginFrameTimeMillis, currentTimeMillis).
+ var nextFrameTimeMillis =
+ Math.min(beginFrameTimeMillis, currentTimeMillis) + updateRateMillis
// Drop frames if needed (happens when onDraw is slow).
if (nextFrameTimeMillis <= currentTimeMillis) {
// Compute the next runtime after currentTimeMillis with the same phase as
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 66e2696..96a0cae4 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
@@ -45,7 +45,7 @@
import androidx.annotation.IntDef
import androidx.wear.complications.SystemProviders.ProviderId
import androidx.wear.watchface.style.UserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import java.util.concurrent.CountDownLatch
/**
@@ -444,9 +444,9 @@
override fun setUserStyle(userStyle: Bundle) {
runOnUiThread {
watchFace.onSetStyleInternal(
- UserStyleManager.bundleToStyleMap(
+ UserStyleRepository.bundleToStyleMap(
userStyle,
- watchFace.userStyleManager.userStyleCategories
+ watchFace.userStyleRepository.userStyleCategories
)
)
}
@@ -960,7 +960,7 @@
override fun registerUserStyleSchema(styleSchema: List<UserStyleCategory>) {
if (systemApiVersion >= 3) {
iWatchFaceService.registerUserStyleSchema(
- UserStyleManager.userStyleCategoriesToBundles(styleSchema)
+ UserStyleRepository.userStyleCategoriesToBundles(styleSchema)
)
}
}
@@ -969,7 +969,9 @@
userStyle: Map<UserStyleCategory, UserStyleCategory.Option>
) {
if (systemApiVersion >= 3) {
- iWatchFaceService.setCurrentUserStyle(UserStyleManager.styleMapToBundle(userStyle))
+ iWatchFaceService.setCurrentUserStyle(
+ UserStyleRepository.styleMapToBundle(userStyle)
+ )
}
}
@@ -979,7 +981,7 @@
if (systemApiVersion < 3) {
return null
}
- return UserStyleManager.bundleToStyleMap(
+ return UserStyleRepository.bundleToStyleMap(
iWatchFaceService.storedUserStyle ?: Bundle(),
schema
)
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt
index de2db2c..fd50b91 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt
@@ -38,7 +38,7 @@
import androidx.recyclerview.widget.RecyclerView
import androidx.wear.complications.ProviderInfoRetriever
import androidx.wear.watchface.R
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import androidx.wear.widget.SwipeDismissFrameLayout
import androidx.wear.widget.WearableLinearLayoutManager
import androidx.wear.widget.WearableRecyclerView
@@ -195,7 +195,7 @@
watchFaceConfigActivity.fragmentController.showStyleConfigFragment(
configKey,
watchFaceConfigActivity.styleSchema,
- UserStyleManager.bundleToStyleMap(
+ UserStyleRepository.bundleToStyleMap(
watchFaceConfigActivity.watchFaceConfigDelegate.getUserStyle(),
watchFaceConfigActivity.styleSchema
)
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/StyleConfigFragment.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/StyleConfigFragment.kt
index b545795..3322c8a 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/StyleConfigFragment.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/StyleConfigFragment.kt
@@ -36,7 +36,7 @@
import androidx.wear.watchface.style.DoubleRangeUserStyleCategory
import androidx.wear.watchface.style.ListUserStyleCategory
import androidx.wear.watchface.style.UserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import androidx.wear.widget.SwipeDismissFrameLayout
import androidx.wear.widget.WearableLinearLayoutManager
import androidx.wear.widget.WearableRecyclerView
@@ -73,7 +73,7 @@
STYLE_SCHEMA,
ArrayList(styleSchema.map { Bundle().apply { it.writeToBundle(this) } })
)
- putBundle(STYLE_MAP, UserStyleManager.styleMapToBundle(styleMap))
+ putBundle(STYLE_MAP, UserStyleRepository.styleMapToBundle(styleMap))
}
}
}
@@ -185,7 +185,7 @@
(requireArguments().getParcelableArrayList<Bundle>(STYLE_SCHEMA))!!
.map { UserStyleCategory.createFromBundle(it) }
- styleMap = UserStyleManager.bundleToStyleMap(
+ styleMap = UserStyleRepository.bundleToStyleMap(
requireArguments().getBundle(STYLE_MAP)!!,
styleSchema
)
@@ -198,7 +198,7 @@
// These will become IPCs eventually, hence the use of Bundles.
watchFaceConfigActivity.watchFaceConfigDelegate.setUserStyle(
- UserStyleManager.styleMapToBundle(styleMap)
+ UserStyleRepository.styleMapToBundle(styleMap)
)
}
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/WatchFaceConfigActivity.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/WatchFaceConfigActivity.kt
index 52848c1..442ddbe 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/WatchFaceConfigActivity.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/WatchFaceConfigActivity.kt
@@ -33,7 +33,7 @@
import androidx.fragment.app.FragmentActivity
import androidx.wear.complications.ComplicationHelperActivity
import androidx.wear.watchface.style.UserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
/** @hide */
@RestrictTo(LIBRARY)
@@ -208,7 +208,7 @@
}
styleSchema =
- UserStyleManager.bundlesToUserStyleCategoryList(
+ UserStyleRepository.bundlesToUserStyleCategoryList(
watchFaceConfigDelegate.getUserStyleSchema()
)
@@ -250,7 +250,7 @@
fragmentController.showStyleConfigFragment(
onlyStyleCategory.id,
styleSchema,
- UserStyleManager.bundleToStyleMap(
+ UserStyleRepository.bundleToStyleMap(
watchFaceConfigDelegate.getUserStyle(),
styleSchema
)
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
index 72a714b..0ab6953 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
@@ -32,7 +32,7 @@
import android.view.SurfaceHolder
import androidx.test.core.app.ApplicationProvider
import androidx.wear.watchface.style.UserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import org.junit.runners.model.FrameworkMethod
import org.robolectric.RobolectricTestRunner
import org.robolectric.internal.bytecode.InstrumentationConfiguration
@@ -41,7 +41,7 @@
@WatchFaceType private val watchFaceType: Int,
private val complicationsHolder: ComplicationsHolder,
private val renderer: TestRenderer,
- private val userStyleManager: UserStyleManager,
+ private val userStyleRepository: UserStyleRepository,
private val watchState: WatchState,
private val handler: Handler,
private val interactiveFrameRateMs: Long
@@ -53,8 +53,8 @@
var lastUserStyle: Map<UserStyleCategory, UserStyleCategory.Option>? = null
init {
- userStyleManager.addUserStyleListener(
- object : UserStyleManager.UserStyleListener {
+ userStyleRepository.addUserStyleListener(
+ object : UserStyleRepository.UserStyleListener {
override fun onUserStyleChanged(
userStyle: Map<UserStyleCategory, UserStyleCategory.Option>
) {
@@ -101,7 +101,7 @@
watchFace = WatchFace.Builder(
watchFaceType,
interactiveFrameRateMs,
- userStyleManager,
+ userStyleRepository,
complicationsHolder,
renderer,
watchFaceHost,
@@ -205,10 +205,10 @@
open class TestRenderer(
surfaceHolder: SurfaceHolder,
- userStyleManager: UserStyleManager,
+ userStyleRepository: UserStyleRepository,
watchState: WatchState
) :
- CanvasRenderer(surfaceHolder, userStyleManager, watchState, CanvasType.HARDWARE) {
+ CanvasRenderer(surfaceHolder, userStyleRepository, watchState, CanvasType.HARDWARE) {
var lastOnDrawCalendar: Calendar? = null
var lastDrawMode = DrawMode.INTERACTIVE
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 33a074a..aec9c5f 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -39,7 +39,7 @@
import androidx.wear.complications.rendering.ComplicationDrawable
import androidx.wear.watchface.style.ListUserStyleCategory
import androidx.wear.watchface.style.UserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import androidx.wear.watchface.ui.WatchFaceConfigActivity
import com.google.common.truth.Truth.assertThat
import org.junit.After
@@ -181,7 +181,7 @@
private lateinit var renderer: TestRenderer
private lateinit var complicationsHolder: ComplicationsHolder
- private lateinit var userStyleManager: UserStyleManager
+ private lateinit var userStyleRepository: UserStyleRepository
private lateinit var watchFace: WatchFace
private lateinit var testWatchFaceService: TestWatchFaceService
private lateinit var engineWrapper: WatchFaceService.EngineWrapper
@@ -209,14 +209,14 @@
apiVersion: Int = 2
) {
this.complicationsHolder = ComplicationsHolder(complications)
- userStyleManager =
- UserStyleManager(userStyleCategories)
- renderer = TestRenderer(surfaceHolder, userStyleManager, systemState)
+ userStyleRepository =
+ UserStyleRepository(userStyleCategories)
+ renderer = TestRenderer(surfaceHolder, userStyleRepository, systemState)
testWatchFaceService = TestWatchFaceService(
watchFaceType,
this.complicationsHolder,
renderer,
- userStyleManager,
+ userStyleRepository,
systemState,
handler,
INTERACTIVE_UPDATE_RATE_MS
@@ -678,8 +678,7 @@
beginFrameTimeMillis = 0,
currentTimeMillis = INTERACTIVE_UPDATE_RATE_MS
)
- )
- .isEqualTo(INTERACTIVE_UPDATE_RATE_MS)
+ ).isEqualTo(INTERACTIVE_UPDATE_RATE_MS)
}
@Test
@@ -694,8 +693,19 @@
beginFrameTimeMillis = 2,
currentTimeMillis = INTERACTIVE_UPDATE_RATE_MS + 3
)
- )
- .isEqualTo(INTERACTIVE_UPDATE_RATE_MS - 1)
+ ).isEqualTo(INTERACTIVE_UPDATE_RATE_MS - 1)
+ }
+
+ @Test
+ fun computeDelayTillNextFrame_beginFrameTimeInTheFuture() {
+ initEngine(WatchFaceType.ANALOG, listOf(leftComplication, rightComplication), emptyList())
+
+ assertThat(
+ watchFace.computeDelayTillNextFrame(
+ beginFrameTimeMillis = 100,
+ currentTimeMillis = 10
+ )
+ ).isEqualTo(INTERACTIVE_UPDATE_RATE_MS)
}
@Test
@@ -744,21 +754,21 @@
)
// This should get persisted.
- userStyleManager.userStyle = mapOf(
+ userStyleRepository.userStyle = mapOf(
colorStyleCategory to blueStyleOption,
watchHandStyleCategory to gothicStyleOption
)
- val styleManager2 = UserStyleManager(
+ val userStyleRepository2 = UserStyleRepository(
listOf(colorStyleCategory, watchHandStyleCategory)
)
- val testRenderer2 = TestRenderer(surfaceHolder, styleManager2, systemState)
+ val testRenderer2 = TestRenderer(surfaceHolder, userStyleRepository2, systemState)
val service2 = TestWatchFaceService(
WatchFaceType.ANALOG,
ComplicationsHolder(emptyList()),
testRenderer2,
- styleManager2,
+ userStyleRepository2,
systemState,
handler,
INTERACTIVE_UPDATE_RATE_MS
@@ -769,10 +779,10 @@
engine2.onSurfaceChanged(surfaceHolder, 0, 100, 100)
sendBinder(engine2, apiVersion = 2)
- assertThat(styleManager2.userStyle[colorStyleCategory]).isEqualTo(
+ assertThat(userStyleRepository2.userStyle[colorStyleCategory]).isEqualTo(
blueStyleOption
)
- assertThat(styleManager2.userStyle[watchHandStyleCategory]).isEqualTo(
+ assertThat(userStyleRepository2.userStyle[watchHandStyleCategory]).isEqualTo(
gothicStyleOption
)
}
@@ -799,21 +809,21 @@
)
// This should get persisted.
- userStyleManager.userStyle = mapOf(
+ userStyleRepository.userStyle = mapOf(
colorStyleCategory to blueStyleOption,
watchHandStyleCategory to gothicStyleOption
)
- val styleManager2 = UserStyleManager(
+ val userStyleRepository2 = UserStyleRepository(
listOf(colorStyleCategory, watchHandStyleCategory)
)
- val testRenderer2 = TestRenderer(surfaceHolder, styleManager2, systemState)
+ val testRenderer2 = TestRenderer(surfaceHolder, userStyleRepository2, systemState)
val service2 = TestWatchFaceService(
WatchFaceType.ANALOG,
ComplicationsHolder(emptyList()),
testRenderer2,
- styleManager2,
+ userStyleRepository2,
systemState,
handler,
INTERACTIVE_UPDATE_RATE_MS
@@ -824,10 +834,10 @@
engine2.onSurfaceChanged(surfaceHolder, 0, 100, 100)
sendBinder(engine2, apiVersion = 3)
- assertThat(styleManager2.userStyle[colorStyleCategory]).isEqualTo(
+ assertThat(userStyleRepository2.userStyle[colorStyleCategory]).isEqualTo(
blueStyleOption
)
- assertThat(styleManager2.userStyle[watchHandStyleCategory]).isEqualTo(
+ assertThat(userStyleRepository2.userStyle[watchHandStyleCategory]).isEqualTo(
gothicStyleOption
)
}
@@ -835,7 +845,7 @@
@Test
fun persistedStyleOptionMismatchIgnored() {
`when`(iWatchFaceService.getStoredUserStyle()).thenReturn(
- UserStyleManager.styleMapToBundle(mapOf(watchHandStyleCategory to badStyleOption))
+ UserStyleRepository.styleMapToBundle(mapOf(watchHandStyleCategory to badStyleOption))
)
initEngine(
@@ -1016,16 +1026,16 @@
@Test
fun requestStyleBeforeSetBinder() {
- var styleManager =
- UserStyleManager(emptyList())
- var testRenderer = TestRenderer(surfaceHolder, styleManager, systemState)
+ var userStyleRepository =
+ UserStyleRepository(emptyList())
+ var testRenderer = TestRenderer(surfaceHolder, userStyleRepository, systemState)
val service = TestWatchFaceService(
WatchFaceType.ANALOG,
ComplicationsHolder(
listOf(leftComplication, rightComplication, backgroundComplication)
),
testRenderer,
- UserStyleManager(emptyList()),
+ UserStyleRepository(emptyList()),
systemState,
handler,
INTERACTIVE_UPDATE_RATE_MS
@@ -1112,8 +1122,8 @@
@Test
fun shouldAnimateOverrideControlsEnteringAmbientMode() {
- var styleManager = UserStyleManager(emptyList())
- var testRenderer = object : TestRenderer(surfaceHolder, styleManager, systemState) {
+ var userStyleRepository = UserStyleRepository(emptyList())
+ var testRenderer = object : TestRenderer(surfaceHolder, userStyleRepository, systemState) {
var animate = true
override fun shouldAnimate() = animate
}
@@ -1121,7 +1131,7 @@
WatchFaceType.ANALOG,
ComplicationsHolder(emptyList()),
testRenderer,
- UserStyleManager(emptyList()),
+ UserStyleRepository(emptyList()),
systemState,
handler,
INTERACTIVE_UPDATE_RATE_MS
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt
index 9d78007..2b5af7c 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt
@@ -38,7 +38,7 @@
import androidx.wear.watchface.createComplicationData
import androidx.wear.watchface.style.ListUserStyleCategory
import androidx.wear.watchface.style.UserStyleCategory
-import androidx.wear.watchface.style.UserStyleManager
+import androidx.wear.watchface.style.UserStyleRepository
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.argumentCaptor
import com.nhaarman.mockitokotlin2.eq
@@ -167,7 +167,7 @@
private val configActivity = WatchFaceConfigActivity()
- private lateinit var userStyleManager: UserStyleManager
+ private lateinit var userStyleRepository: UserStyleRepository
private fun initConfigActivity(
complications: List<Complication>,
@@ -176,12 +176,12 @@
Mockito.`when`(surfaceHolder.surfaceFrame)
.thenReturn(ONE_HUNDRED_BY_ONE_HUNDRED_RECT)
- userStyleManager =
- UserStyleManager(userStyleCategories)
+ userStyleRepository =
+ UserStyleRepository(userStyleCategories)
val complicationSet = ComplicationsHolder(
complications,
- object : Renderer(surfaceHolder, userStyleManager, WatchState()) {
+ object : Renderer(surfaceHolder, userStyleRepository, WatchState()) {
override fun renderInternal(calendar: Calendar) {}
override fun takeScreenshot(calendar: Calendar, drawMode: Int): Bitmap {
@@ -197,14 +197,14 @@
watchFaceComponentName,
object : WatchFaceConfigDelegate {
override fun getUserStyleSchema() =
- UserStyleManager.userStyleCategoriesToBundles(userStyleCategories)
+ UserStyleRepository.userStyleCategoriesToBundles(userStyleCategories)
override fun getUserStyle() =
- UserStyleManager.styleMapToBundle(userStyleManager.userStyle)
+ UserStyleRepository.styleMapToBundle(userStyleRepository.userStyle)
override fun setUserStyle(style: Bundle) {
- userStyleManager.userStyle =
- UserStyleManager.bundleToStyleMap(style, userStyleCategories)
+ userStyleRepository.userStyle =
+ UserStyleRepository.bundleToStyleMap(style, userStyleCategories)
}
override fun getBackgroundComplicationId() =
@@ -355,19 +355,19 @@
styleConfigFragment.readOptionsFromArguments()
styleConfigFragment.watchFaceConfigActivity = configActivity
- assertThat(userStyleManager.userStyle[colorStyleCategory]!!.id).isEqualTo(
+ assertThat(userStyleRepository.userStyle[colorStyleCategory]!!.id).isEqualTo(
redStyleOption.id
)
- assertThat(userStyleManager.userStyle[watchHandStyleCategory]!!.id).isEqualTo(
+ assertThat(userStyleRepository.userStyle[watchHandStyleCategory]!!.id).isEqualTo(
classicStyleOption.id
)
styleConfigFragment.onItemClick(configActivity.styleSchema[categoryIndex].options[1])
- assertThat(userStyleManager.userStyle[colorStyleCategory]!!.id).isEqualTo(
+ assertThat(userStyleRepository.userStyle[colorStyleCategory]!!.id).isEqualTo(
greenStyleOption.id
)
- assertThat(userStyleManager.userStyle[watchHandStyleCategory]!!.id).isEqualTo(
+ assertThat(userStyleRepository.userStyle[watchHandStyleCategory]!!.id).isEqualTo(
classicStyleOption.id
)
}