Merge "Remove invalid owner from text module." into androidx-crane-dev
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index ec393ba..c1b0df4 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -30,6 +30,8 @@
prebuilts(LibraryGroups.APPCOMPAT, "1.1.0-alpha05")
prebuilts(LibraryGroups.ARCH_CORE, "2.1.0-beta01")
prebuilts(LibraryGroups.ASYNCLAYOUTINFLATER, "1.0.0")
+ ignore(LibraryGroups.BENCHMARK.group, "benchmark-gradle-plugin")
+ prebuilts(LibraryGroups.BENCHMARK, "1.0.0-alpha01")
prebuilts(LibraryGroups.BIOMETRIC, "biometric", "1.0.0-alpha04")
prebuilts(LibraryGroups.BROWSER, "1.0.0")
ignore(LibraryGroups.CAMERA.group, "camera-view")
@@ -81,10 +83,6 @@
prebuilts(LibraryGroups.MEDIA, "media-widget", "1.0.0-alpha06")
ignore(LibraryGroups.MEDIA2.group, "media2-widget")
ignore(LibraryGroups.MEDIA2.group, "media2-exoplayer")
- // TODO: Reenable to use media2-{common,player,session} prebuilts (b/130839413)
- ignore(LibraryGroups.MEDIA2.group, "media2-common")
- ignore(LibraryGroups.MEDIA2.group, "media2-player")
- ignore(LibraryGroups.MEDIA2.group, "media2-session")
prebuilts(LibraryGroups.MEDIA2, "1.0.0-beta01")
prebuilts(LibraryGroups.MEDIAROUTER, "1.1.0-beta01")
ignore(LibraryGroups.NAVIGATION.group, "navigation-testing")
@@ -99,18 +97,19 @@
prebuilts(LibraryGroups.RECOMMENDATION, "1.0.0")
prebuilts(LibraryGroups.RECYCLERVIEW, "recyclerview", "1.1.0-alpha05")
prebuilts(LibraryGroups.RECYCLERVIEW, "recyclerview-selection", "1.1.0-alpha05")
- prebuilts(LibraryGroups.REMOTECALLBACK, "1.0.0-alpha01")
+ prebuilts(LibraryGroups.REMOTECALLBACK, "1.0.0-alpha02")
ignore(LibraryGroups.ROOM.group, "room-common-java8")
prebuilts(LibraryGroups.ROOM, "2.1.0-beta01")
prebuilts(LibraryGroups.SAVEDSTATE, "1.0.0-beta01")
+ prebuilts(LibraryGroups.SECURITY, "1.0.0-alpha01")
prebuilts(LibraryGroups.SHARETARGET, "1.0.0-alpha01")
- prebuilts(LibraryGroups.SLICE, "slice-builders", "1.0.0")
- prebuilts(LibraryGroups.SLICE, "slice-builders-ktx", "1.0.0-alpha6")
- prebuilts(LibraryGroups.SLICE, "slice-core", "1.0.0")
+ prebuilts(LibraryGroups.SLICE, "slice-builders", "1.1.0-alpha01")
+ prebuilts(LibraryGroups.SLICE, "slice-builders-ktx", "1.0.0-alpha07")
+ prebuilts(LibraryGroups.SLICE, "slice-core", "1.1.0-alpha01")
// TODO: land prebuilts
// prebuilts(LibraryGroups.SLICE.group, "slice-test", "1.0.0")
ignore(LibraryGroups.SLICE.group, "slice-test")
- prebuilts(LibraryGroups.SLICE, "slice-view", "1.0.0")
+ prebuilts(LibraryGroups.SLICE, "slice-view", "1.1.0-alpha01")
prebuilts(LibraryGroups.SLIDINGPANELAYOUT, "1.0.0")
prebuilts(LibraryGroups.SWIPEREFRESHLAYOUT, "1.1.0-alpha01")
prebuilts(LibraryGroups.TEXTCLASSIFIER, "1.0.0-alpha02")
diff --git a/camera/camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java b/camera/camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
index a7b81cc..819b3b6 100644
--- a/camera/camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
+++ b/camera/camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
@@ -480,7 +480,8 @@
}
}
- @Test
+ // Skipping test due to b/132108192. Add back once test has been fixed.
+ // @Test
public void camera2InteropCaptureSessionCallbacks() throws InterruptedException {
ImageCaptureConfig.Builder configBuilder =
new ImageCaptureConfig.Builder().setCallbackHandler(mHandler);
@@ -506,6 +507,8 @@
requestCaptor.capture(),
any(TotalCaptureResult.class));
CaptureRequest captureRequest = requestCaptor.getValue(); // Obtains the last value.
+ // TODO This method removed temporary due to the side effect of aosp/943904. It's needed
+ // keep tracking.
assertThat(captureRequest.get(CaptureRequest.CONTROL_CAPTURE_INTENT))
.isEqualTo(CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
}
diff --git a/camera/camera2/src/main/java/androidx/camera/camera2/impl/compat/CameraDeviceCompat.java b/camera/camera2/src/main/java/androidx/camera/camera2/impl/compat/CameraDeviceCompat.java
new file mode 100644
index 0000000..d6bc064
--- /dev/null
+++ b/camera/camera2/src/main/java/androidx/camera/camera2/impl/compat/CameraDeviceCompat.java
@@ -0,0 +1,51 @@
+/*
+ * 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.camera.camera2.impl.compat;
+
+import android.annotation.TargetApi;
+import android.hardware.camera2.CameraDevice;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+/**
+ * Helper for accessing features in {@link CameraDevice} in a backwards compatible fashion.
+ *
+ * @hide Will be unhidden once some methods are implemented
+ */
+@RestrictTo(Scope.LIBRARY)
+@TargetApi(21)
+public final class CameraDeviceCompat {
+
+ /**
+ * Standard camera operation mode.
+ *
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public static final int SESSION_OPERATION_MODE_NORMAL =
+ 0; // ICameraDeviceUser.NORMAL_MODE;
+
+ /**
+ * Constrained high-speed operation mode.
+ *
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED =
+ 1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
+}
diff --git a/camera/camera2/src/main/java/androidx/camera/camera2/impl/compat/package-info.java b/camera/camera2/src/main/java/androidx/camera/camera2/impl/compat/package-info.java
new file mode 100644
index 0000000..f6409d6
--- /dev/null
+++ b/camera/camera2/src/main/java/androidx/camera/camera2/impl/compat/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+package androidx.camera.camera2.impl.compat;
+
+import androidx.annotation.RestrictTo;
diff --git a/camera/camera2/src/main/java/androidx/camera/camera2/impl/compat/params/SessionConfigurationCompat.java b/camera/camera2/src/main/java/androidx/camera/camera2/impl/compat/params/SessionConfigurationCompat.java
new file mode 100644
index 0000000..7afc02a
--- /dev/null
+++ b/camera/camera2/src/main/java/androidx/camera/camera2/impl/compat/params/SessionConfigurationCompat.java
@@ -0,0 +1,480 @@
+/*
+ * 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.camera.camera2.impl.compat.params;
+
+import android.annotation.TargetApi;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.InputConfiguration;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.os.Build;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.camera.camera2.impl.compat.CameraDeviceCompat;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper for accessing features in SessionConfiguration in a backwards compatible fashion.
+ */
+@TargetApi(21)
+public final class SessionConfigurationCompat {
+
+ /**
+ * A regular session type containing instances of {@link OutputConfigurationCompat} running
+ * at regular non high speed FPS ranges and optionally {@link InputConfigurationCompat} for
+ * reprocessable sessions.
+ *
+ * @see CameraDevice#createCaptureSession
+ * @see CameraDevice#createReprocessableCaptureSession
+ */
+ public static final int SESSION_REGULAR = CameraDeviceCompat.SESSION_OPERATION_MODE_NORMAL;
+ /**
+ * A high speed session type that can only contain instances of
+ * {@link OutputConfigurationCompat}.
+ * The outputs can run using high speed FPS ranges. Calls to {@link #setInputConfiguration}
+ * are not supported.
+ *
+ * @see CameraDevice#createConstrainedHighSpeedCaptureSession
+ */
+ public static final int SESSION_HIGH_SPEED =
+ CameraDeviceCompat.SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED;
+ private final SessionConfigurationCompatImpl mImpl;
+
+ /**
+ * Create a new {@link SessionConfigurationCompat}.
+ *
+ * @param sessionType The session type.
+ * @param outputsCompat A list of output configurations for the capture session.
+ * @param executor The executor which should be used to invoke the callback. In general
+ * it is
+ * recommended that camera operations are not done on the main (UI) thread.
+ * @param cb A state callback interface implementation.
+ * @see #SESSION_REGULAR
+ * @see #SESSION_HIGH_SPEED
+ */
+ public SessionConfigurationCompat(@SessionMode int sessionType,
+ @NonNull List<OutputConfigurationCompat> outputsCompat,
+ @NonNull /* @CallbackExecutor */ Executor executor,
+ @NonNull CameraCaptureSession.StateCallback cb) {
+ if (Build.VERSION.SDK_INT < 28) {
+ mImpl = new SessionConfigurationCompatBaseImpl(sessionType, outputsCompat, executor,
+ cb);
+ } else {
+ mImpl = new SessionConfigurationCompatApi28Impl(sessionType, outputsCompat, executor,
+ cb);
+ }
+ }
+
+ private SessionConfigurationCompat(@NonNull SessionConfigurationCompatImpl impl) {
+ mImpl = impl;
+ }
+
+ /**
+ * Creates an instance from a framework android.hardware.camera2.params.SessionConfiguration
+ * object.
+ *
+ * <p>This method always returns {@code null} on API <= 27.</p>
+ *
+ * @param sessionConfiguration an android.hardware.camera2.params.SessionConfiguration object,
+ * or {@code null} if none.
+ * @return an equivalent {@link SessionConfigurationCompat} object, or {@code null} if not
+ * supported.
+ */
+ @Nullable
+ public static SessionConfigurationCompat wrap(@Nullable Object sessionConfiguration) {
+ if (sessionConfiguration == null) {
+ return null;
+ }
+ if (Build.VERSION.SDK_INT < 28) {
+ return null;
+ }
+
+ return new SessionConfigurationCompat(
+ new SessionConfigurationCompatApi28Impl(sessionConfiguration));
+ }
+
+ @RequiresApi(24)
+ static List<OutputConfigurationCompat> transformToCompat(
+ @NonNull List<OutputConfiguration> outputConfigurations) {
+ ArrayList<OutputConfigurationCompat> outList = new ArrayList<>(outputConfigurations.size());
+ for (OutputConfiguration outputConfiguration : outputConfigurations) {
+ outList.add(OutputConfigurationCompat.wrap(outputConfiguration));
+ }
+
+ return outList;
+ }
+
+ @RequiresApi(24)
+ static List<OutputConfiguration> transformFromCompat(
+ @NonNull List<OutputConfigurationCompat> outputConfigurations) {
+ ArrayList<OutputConfiguration> outList = new ArrayList<>(outputConfigurations.size());
+ for (OutputConfigurationCompat outputConfiguration : outputConfigurations) {
+ outList.add((OutputConfiguration) outputConfiguration.unwrap());
+ }
+
+ return outList;
+ }
+
+ /**
+ * Retrieve the type of the capture session.
+ *
+ * @return The capture session type.
+ */
+ @SessionMode
+ public int getSessionType() {
+ return mImpl.getSessionType();
+ }
+
+ /**
+ * Retrieve the {@link OutputConfigurationCompat} list for the capture session.
+ *
+ * @return A list of output configurations for the capture session.
+ */
+ public List<OutputConfigurationCompat> getOutputConfigurations() {
+ return mImpl.getOutputConfigurations();
+ }
+
+ /**
+ * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session.
+ *
+ * @return A state callback interface implementation.
+ */
+ public CameraCaptureSession.StateCallback getStateCallback() {
+ return mImpl.getStateCallback();
+ }
+
+ /**
+ * Retrieve the {@link Executor} for the capture session.
+ *
+ * @return The Executor on which the callback will be invoked.
+ */
+ public Executor getExecutor() {
+ return mImpl.getExecutor();
+ }
+
+ /**
+ * Retrieve the {@link InputConfigurationCompat}.
+ *
+ * @return The capture session input configuration.
+ */
+ public InputConfigurationCompat getInputConfiguration() {
+ return mImpl.getInputConfiguration();
+ }
+
+ /**
+ * Sets the {@link InputConfigurationCompat} for a reprocessable session. Input configuration
+ * are not supported for {@link #SESSION_HIGH_SPEED}.
+ *
+ * @param input Input configuration.
+ * @throws UnsupportedOperationException In case it is called for {@link #SESSION_HIGH_SPEED}
+ * type session configuration.
+ */
+ public void setInputConfiguration(@NonNull InputConfigurationCompat input) {
+ mImpl.setInputConfiguration(input);
+ }
+
+ /**
+ * Retrieve the session wide camera parameters (see {@link CaptureRequest}).
+ *
+ * @return A capture request that includes the initial values for any available
+ * session wide capture keys.
+ */
+ public CaptureRequest getSessionParameters() {
+ return mImpl.getSessionParameters();
+ }
+
+ /**
+ * Sets the session wide camera parameters (see {@link CaptureRequest}). This argument can
+ * be set for every supported session type and will be passed to the camera device as part
+ * of the capture session initialization. Session parameters are a subset of the available
+ * capture request parameters (see {@code CameraCharacteristics.getAvailableSessionKeys})
+ * and their application can introduce internal camera delays. To improve camera performance
+ * it is suggested to change them sparingly within the lifetime of the capture session and
+ * to pass their initial values as part of this method.
+ *
+ * @param params A capture request that includes the initial values for any available
+ * session wide capture keys. Tags (see {@link CaptureRequest.Builder#setTag}) and
+ * output targets (see {@link CaptureRequest.Builder#addTarget}) are ignored if
+ * set. Parameter values not part of
+ * {@code CameraCharacteristics.getAvailableSessionKeys} will also be ignored. It
+ * is recommended to build the session parameters using the same template type as
+ * the initial capture request, so that the session and initial request parameters
+ * match as much as possible.
+ */
+ public void setSessionParameters(CaptureRequest params) {
+ mImpl.setSessionParameters(params);
+ }
+
+ /**
+ * Gets the underlying framework android.hardware.camera2.params.SessionConfiguration object.
+ *
+ * <p>This method always returns {@code null} on API <= 27.</p>
+ *
+ * @return an equivalent android.hardware.camera2.params.SessionConfiguration object, or
+ * {@code null} if not supported.
+ */
+ @Nullable
+ public Object unwrap() {
+ return mImpl.getSessionConfiguration();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof SessionConfigurationCompat)) {
+ return false;
+ }
+
+ return mImpl.equals(((SessionConfigurationCompat) obj).mImpl);
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY)
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value =
+ {SESSION_REGULAR, SESSION_HIGH_SPEED})
+ public @interface SessionMode {
+ }
+
+ private interface SessionConfigurationCompatImpl {
+ @SessionMode
+ int getSessionType();
+
+ List<OutputConfigurationCompat> getOutputConfigurations();
+
+ CameraCaptureSession.StateCallback getStateCallback();
+
+ Executor getExecutor();
+
+ InputConfigurationCompat getInputConfiguration();
+
+ void setInputConfiguration(@NonNull InputConfigurationCompat input);
+
+ CaptureRequest getSessionParameters();
+
+ void setSessionParameters(CaptureRequest params);
+
+ @Nullable
+ Object getSessionConfiguration();
+ }
+
+ private static final class SessionConfigurationCompatBaseImpl implements
+ SessionConfigurationCompatImpl {
+
+ private final List<OutputConfigurationCompat> mOutputConfigurations;
+ private final CameraCaptureSession.StateCallback mStateCallback;
+ private final Executor mExecutor;
+ private int mSessionType;
+ private InputConfigurationCompat mInputConfig = null;
+ private CaptureRequest mSessionParameters = null;
+
+ SessionConfigurationCompatBaseImpl(@SessionMode int sessionType,
+ @NonNull List<OutputConfigurationCompat> outputs,
+ @NonNull /* @CallbackExecutor */ Executor executor,
+ @NonNull CameraCaptureSession.StateCallback cb) {
+ mSessionType = sessionType;
+ mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
+ mStateCallback = cb;
+ mExecutor = executor;
+ }
+
+ @Override
+ public int getSessionType() {
+ return mSessionType;
+ }
+
+ @Override
+ public List<OutputConfigurationCompat> getOutputConfigurations() {
+ return mOutputConfigurations;
+ }
+
+ @Override
+ public CameraCaptureSession.StateCallback getStateCallback() {
+ return mStateCallback;
+ }
+
+ @Override
+ public Executor getExecutor() {
+ return mExecutor;
+ }
+
+ @Nullable
+ @Override
+ public InputConfigurationCompat getInputConfiguration() {
+ return mInputConfig;
+ }
+
+ @Override
+ public void setInputConfiguration(@NonNull InputConfigurationCompat input) {
+ if (mSessionType != SESSION_HIGH_SPEED) {
+ mInputConfig = input;
+ } else {
+ throw new UnsupportedOperationException(
+ "Method not supported for high speed session types");
+ }
+ }
+
+ @Override
+ public CaptureRequest getSessionParameters() {
+ return mSessionParameters;
+ }
+
+ @Override
+ public void setSessionParameters(CaptureRequest params) {
+ mSessionParameters = params;
+ }
+
+ @Nullable
+ @Override
+ public Object getSessionConfiguration() {
+ return null;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj instanceof SessionConfigurationCompatBaseImpl) {
+ SessionConfigurationCompatBaseImpl other = (SessionConfigurationCompatBaseImpl) obj;
+ if (mInputConfig != other.mInputConfig
+ || mSessionType != other.mSessionType
+ || mOutputConfigurations.size() != other.mOutputConfigurations.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < mOutputConfigurations.size(); i++) {
+ if (!mOutputConfigurations.get(i).equals(other.mOutputConfigurations.get(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ // Strength reduction; in case the compiler has illusions about divisions being faster
+ h = ((h << 5) - h)
+ ^ mOutputConfigurations.hashCode(); // (h * 31) XOR mOutputConfigurations
+ // .hashCode()
+ h = ((h << 5) - h) ^ (mInputConfig == null ? 0
+ : mInputConfig.hashCode()); // (h * 31) XOR mInputConfig.hashCode()
+ h = ((h << 5) - h) ^ mSessionType; // (h * 31) XOR mSessionType
+
+ return h;
+ }
+ }
+
+ @RequiresApi(28)
+ private static final class SessionConfigurationCompatApi28Impl implements
+ SessionConfigurationCompatImpl {
+
+ private final SessionConfiguration mObject;
+ private final List<OutputConfigurationCompat> mOutputConfigurations;
+
+ SessionConfigurationCompatApi28Impl(@NonNull Object sessionConfiguration) {
+ mObject = (SessionConfiguration) sessionConfiguration;
+ mOutputConfigurations = Collections.unmodifiableList(transformToCompat(
+ ((SessionConfiguration) sessionConfiguration).getOutputConfigurations()));
+ }
+
+ SessionConfigurationCompatApi28Impl(@SessionMode int sessionType,
+ @NonNull List<OutputConfigurationCompat> outputs,
+ @NonNull /* @CallbackExecutor */ Executor executor,
+ @NonNull CameraCaptureSession.StateCallback cb) {
+ this(new SessionConfiguration(sessionType, transformFromCompat(outputs), executor, cb));
+ }
+
+ @Override
+ public int getSessionType() {
+ return mObject.getSessionType();
+ }
+
+ @Override
+ public List<OutputConfigurationCompat> getOutputConfigurations() {
+ // Return cached compat version of list
+ return mOutputConfigurations;
+ }
+
+ @Override
+ public CameraCaptureSession.StateCallback getStateCallback() {
+ return mObject.getStateCallback();
+ }
+
+ @Override
+ public Executor getExecutor() {
+ return mObject.getExecutor();
+ }
+
+ @Override
+ public InputConfigurationCompat getInputConfiguration() {
+ return InputConfigurationCompat.wrap(mObject.getInputConfiguration());
+ }
+
+ @Override
+ public void setInputConfiguration(@NonNull InputConfigurationCompat input) {
+ mObject.setInputConfiguration((InputConfiguration) input.unwrap());
+ }
+
+ @Override
+ public CaptureRequest getSessionParameters() {
+ return mObject.getSessionParameters();
+ }
+
+ @Override
+ public void setSessionParameters(CaptureRequest params) {
+ mObject.setSessionParameters(params);
+ }
+
+ @Nullable
+ @Override
+ public Object getSessionConfiguration() {
+ return mObject;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof SessionConfigurationCompatApi28Impl)) {
+ return false;
+ }
+
+ return Objects.equals(mObject, ((SessionConfigurationCompatApi28Impl) obj).mObject);
+ }
+
+ @Override
+ public int hashCode() {
+ return mObject.hashCode();
+ }
+ }
+}
diff --git a/camera/camera2/src/test/java/androidx/camera/camera2/impl/compat/params/InputConfigurationCompatTest.java b/camera/camera2/src/test/java/androidx/camera/camera2/impl/compat/params/InputConfigurationCompatTest.java
index 3470302..6c421e4 100644
--- a/camera/camera2/src/test/java/androidx/camera/camera2/impl/compat/params/InputConfigurationCompatTest.java
+++ b/camera/camera2/src/test/java/androidx/camera/camera2/impl/compat/params/InputConfigurationCompatTest.java
@@ -34,7 +34,7 @@
@RunWith(RobolectricTestRunner.class)
@DoNotInstrument
@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-public class InputConfigurationCompatTest {
+public final class InputConfigurationCompatTest {
private static final int WIDTH = 1024;
private static final int HEIGHT = 768;
diff --git a/camera/camera2/src/test/java/androidx/camera/camera2/impl/compat/params/SessionConfigurationCompatTest.java b/camera/camera2/src/test/java/androidx/camera/camera2/impl/compat/params/SessionConfigurationCompatTest.java
new file mode 100644
index 0000000..e122ef4
--- /dev/null
+++ b/camera/camera2/src/test/java/androidx/camera/camera2/impl/compat/params/SessionConfigurationCompatTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.camera.camera2.impl.compat.params;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.os.Build;
+import android.view.Surface;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+public final class SessionConfigurationCompatTest {
+
+ private static final int WIDTH = 1024;
+ private static final int HEIGHT = 768;
+ private static final int FORMAT = ImageFormat.YUV_420_888;
+ private static final int DEFAULT_SESSION_TYPE = SessionConfigurationCompat.SESSION_REGULAR;
+
+ private List<OutputConfigurationCompat> mOutputs;
+ private Executor mCallbackExecutor;
+ private CameraCaptureSession.StateCallback mStateCallback;
+
+ @Before
+ public void setUp() {
+ mOutputs = new ArrayList<>();
+ for (int i = 0; i < 3; ++i) {
+ Surface surface = mock(Surface.class);
+ OutputConfigurationCompat outputConfigCompat = new OutputConfigurationCompat(surface);
+ mOutputs.add(outputConfigCompat);
+ }
+
+ mCallbackExecutor = mock(Executor.class);
+
+ mStateCallback = mock(CameraCaptureSession.StateCallback.class);
+ }
+
+ private SessionConfigurationCompat createDefaultSessionConfig() {
+ return new SessionConfigurationCompat(
+ DEFAULT_SESSION_TYPE,
+ mOutputs,
+ mCallbackExecutor,
+ mStateCallback);
+ }
+
+ @Test
+ public void canCreateSessionConfiguration() {
+ SessionConfigurationCompat sessionConfigCompat = createDefaultSessionConfig();
+
+ assertThat(sessionConfigCompat.getSessionType()).isEqualTo(DEFAULT_SESSION_TYPE);
+ assertThat(sessionConfigCompat.getOutputConfigurations()).containsExactlyElementsIn(
+ mOutputs);
+ assertThat(sessionConfigCompat.getExecutor()).isSameInstanceAs(mCallbackExecutor);
+ assertThat(sessionConfigCompat.getStateCallback()).isSameInstanceAs(mStateCallback);
+ }
+
+ @Test
+ @Config(minSdk = 28)
+ public void canWrapAndUnwrapSessionConfiguration() {
+ List<OutputConfiguration> outputConfigs = new ArrayList<>(mOutputs.size());
+ for (OutputConfigurationCompat outputConfigCompat : mOutputs) {
+ outputConfigs.add((OutputConfiguration) outputConfigCompat.unwrap());
+ }
+
+ SessionConfiguration sessionConfig = new SessionConfiguration(
+ SessionConfiguration.SESSION_REGULAR,
+ outputConfigs,
+ mCallbackExecutor,
+ mStateCallback);
+
+ SessionConfigurationCompat sessionConfigCompat = SessionConfigurationCompat.wrap(
+ sessionConfig);
+
+ assertThat(sessionConfigCompat.getSessionType()).isEqualTo(
+ SessionConfigurationCompat.SESSION_REGULAR);
+ assertThat(sessionConfigCompat.getOutputConfigurations()).containsExactlyElementsIn(
+ mOutputs);
+ assertThat(sessionConfigCompat.getExecutor()).isSameInstanceAs(mCallbackExecutor);
+ assertThat(sessionConfigCompat.getStateCallback()).isSameInstanceAs(mStateCallback);
+
+ assertThat(sessionConfigCompat.unwrap()).isSameInstanceAs(sessionConfig);
+ assertThat(
+ ((SessionConfiguration) sessionConfigCompat.unwrap()).getOutputConfigurations())
+ .containsExactlyElementsIn(
+ outputConfigs);
+ }
+
+ @Test
+ public void canSetAndRetrieveInputConfiguration() {
+ SessionConfigurationCompat sessionConfigCompat = createDefaultSessionConfig();
+
+ InputConfigurationCompat inputConfigurationCompat = new InputConfigurationCompat(WIDTH,
+ HEIGHT, FORMAT);
+
+ sessionConfigCompat.setInputConfiguration(inputConfigurationCompat);
+
+ // getInputConfiguration() is not necessarily the same instance, but should be equivalent
+ // by comparison
+ assertThat(sessionConfigCompat.getInputConfiguration()).isEqualTo(inputConfigurationCompat);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void cannotSetInputConfiguration_onHighSpeedSession() {
+ SessionConfigurationCompat sessionConfigCompat = new SessionConfigurationCompat(
+ SessionConfigurationCompat.SESSION_HIGH_SPEED,
+ mOutputs,
+ mCallbackExecutor,
+ mStateCallback);
+
+ InputConfigurationCompat inputConfigurationCompat = new InputConfigurationCompat(WIDTH,
+ HEIGHT, FORMAT);
+
+ sessionConfigCompat.setInputConfiguration(inputConfigurationCompat);
+ }
+
+ @Test
+ @Config(minSdk = 28)
+ public void constantsMatchNonCompatVersion() {
+ assertThat(SessionConfigurationCompat.SESSION_REGULAR).isEqualTo(
+ SessionConfiguration.SESSION_REGULAR);
+ assertThat(SessionConfigurationCompat.SESSION_HIGH_SPEED).isEqualTo(
+ SessionConfiguration.SESSION_HIGH_SPEED);
+ }
+}
diff --git a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
index 89e2ebc..de69fc9 100644
--- a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
+++ b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -1133,6 +1134,23 @@
@Test
@LargeTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
+ public void testSetPlaybackSpeedWithIllegalArguments() throws Throwable {
+ // Zero is not allowed.
+ ListenableFuture<PlayerResult> future = mPlayer.setPlaybackSpeed(0.0f);
+ PlayerResult result = future.get();
+ assertNotNull(result);
+ assertEquals(RESULT_ERROR_BAD_VALUE, result.getResultCode());
+
+ // Negative values are not allowed.
+ future = mPlayer.setPlaybackSpeed(-1.0f);
+ result = future.get();
+ assertNotNull(result);
+ assertEquals(RESULT_ERROR_BAD_VALUE, result.getResultCode());
+ }
+
+ @Test
+ @LargeTest
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
public void testClose() throws Exception {
assertTrue(loadResource(R.raw.testmp3_2));
AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
diff --git a/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java b/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
index de7cc0a..20ac028 100644
--- a/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
+++ b/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
@@ -823,6 +823,9 @@
PendingFuture<PlayerResult> pendingFuture = new PendingFuture<PlayerResult>(mExecutor) {
@Override
List<ResolvableFuture<PlayerResult>> onExecute() {
+ if (playbackSpeed <= 0.0f) {
+ return createFuturesForResultCode(RESULT_ERROR_BAD_VALUE);
+ }
ArrayList<ResolvableFuture<PlayerResult>> futures = new ArrayList<>();
ResolvableFuture<PlayerResult> future = ResolvableFuture.create();
synchronized (mPendingCommands) {
diff --git a/transition/src/main/java/androidx/transition/Transition.java b/transition/src/main/java/androidx/transition/Transition.java
index 7fe90fe..21895ca 100644
--- a/transition/src/main/java/androidx/transition/Transition.java
+++ b/transition/src/main/java/androidx/transition/Transition.java
@@ -1987,16 +1987,21 @@
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
void forceToEnd(ViewGroup sceneRoot) {
- ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+ final ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
int numOldAnims = runningAnimators.size();
- if (sceneRoot != null) {
- WindowIdImpl windowId = ViewUtils.getWindowId(sceneRoot);
- for (int i = numOldAnims - 1; i >= 0; i--) {
- AnimationInfo info = runningAnimators.valueAt(i);
- if (info.mView != null && windowId != null && windowId.equals(info.mWindowId)) {
- Animator anim = runningAnimators.keyAt(i);
- anim.end();
- }
+ if (sceneRoot == null || numOldAnims == 0) {
+ return;
+ }
+
+ WindowIdImpl windowId = ViewUtils.getWindowId(sceneRoot);
+ final ArrayMap<Animator, AnimationInfo> oldAnimators = new ArrayMap(runningAnimators);
+ runningAnimators.clear();
+
+ for (int i = numOldAnims - 1; i >= 0; i--) {
+ AnimationInfo info = oldAnimators.valueAt(i);
+ if (info.mView != null && windowId != null && windowId.equals(info.mWindowId)) {
+ Animator anim = oldAnimators.keyAt(i);
+ anim.end();
}
}
}
diff --git a/ui/animation/src/main/java/androidx/ui/animation/Transition.kt b/ui/animation/src/main/java/androidx/ui/animation/Transition.kt
index 61aa6e3..c1a49d5 100644
--- a/ui/animation/src/main/java/androidx/ui/animation/Transition.kt
+++ b/ui/animation/src/main/java/androidx/ui/animation/Transition.kt
@@ -49,7 +49,8 @@
@Children children: @Composable() (state: TransitionState) -> Unit
) {
if (transitionsEnabled) {
- val model = +memo(definition) { TransitionModel(definition) }
+ // TODO: This null is workaround for b/132148894
+ val model = +memo(definition, null) { TransitionModel(definition) }
model.anim.toState(toState)
children(state = model)
} else {
diff --git a/ui/material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionsControlsDemo.kt b/ui/material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionsControlsDemo.kt
index b6d6542..886dee2 100644
--- a/ui/material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionsControlsDemo.kt
+++ b/ui/material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionsControlsDemo.kt
@@ -33,6 +33,7 @@
import androidx.ui.material.RadioGroup
import androidx.ui.material.Switch
import androidx.ui.material.parentCheckboxState
+import androidx.ui.material.surface.Surface
import androidx.ui.material.themeTextStyle
import androidx.ui.painting.Color
import androidx.compose.Composable
@@ -58,27 +59,29 @@
val headerStyle = +themeTextStyle { h6 }
val padding = EdgeInsets(10.dp)
- Padding(padding = padding) {
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
- Text(text = "Checkbox", style = headerStyle)
- Padding(padding = padding) {
- CheckboxDemo()
- }
- Text(text = "Switch", style = headerStyle)
- Padding(padding = padding) {
- SwitchDemo()
- }
- Text(text = "RadioButton", style = headerStyle)
- Padding(padding = padding) {
- RadioButtonDemo()
- }
- Text(text = "Radio group :: Default usage", style = headerStyle)
- Padding(padding = padding) {
- DefaultRadioGroup()
- }
- Text(text = "Radio group :: Custom usage", style = headerStyle)
- Padding(padding = padding) {
- CustomRadioGroup()
+ Surface {
+ Padding(padding = padding) {
+ Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Text(text = "Checkbox", style = headerStyle)
+ Padding(padding = padding) {
+ CheckboxDemo()
+ }
+ Text(text = "Switch", style = headerStyle)
+ Padding(padding = padding) {
+ SwitchDemo()
+ }
+ Text(text = "RadioButton", style = headerStyle)
+ Padding(padding = padding) {
+ RadioButtonDemo()
+ }
+ Text(text = "Radio group :: Default usage", style = headerStyle)
+ Padding(padding = padding) {
+ DefaultRadioGroup()
+ }
+ Text(text = "Radio group :: Custom usage", style = headerStyle)
+ Padding(padding = padding) {
+ CustomRadioGroup()
+ }
}
}
}
diff --git a/ui/material/src/androidTest/java/androidx/ui/material/CheckboxUiTest.kt b/ui/material/src/androidTest/java/androidx/ui/material/CheckboxUiTest.kt
index 44d72b7..09b449e 100644
--- a/ui/material/src/androidTest/java/androidx/ui/material/CheckboxUiTest.kt
+++ b/ui/material/src/androidTest/java/androidx/ui/material/CheckboxUiTest.kt
@@ -40,6 +40,7 @@
import com.google.common.truth.Truth
import androidx.compose.Model
import androidx.compose.composer
+import androidx.ui.core.round
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -107,10 +108,10 @@
setMaterialContent {
TestTag(tag = defaultTag) {
Checkbox(
- value = state.value,
- onClick = {
- state.toggle()
- })
+ value = state.value,
+ onClick = {
+ state.toggle()
+ })
}
}
@@ -127,10 +128,10 @@
setMaterialContent {
TestTag(tag = defaultTag) {
Checkbox(
- value = state.value,
- onClick = {
- state.toggle()
- })
+ value = state.value,
+ onClick = {
+ state.toggle()
+ })
}
}
@@ -186,8 +187,9 @@
}
}
withDensity(density) {
- Truth.assertThat(checkboxSize?.width).isEqualTo(materialCheckboxSize.toPx())
- Truth.assertThat(checkboxSize?.height).isEqualTo(materialCheckboxSize.toPx())
+ Truth.assertThat(checkboxSize?.width?.round()).isEqualTo(materialCheckboxSize.toIntPx())
+ Truth.assertThat(checkboxSize?.height?.round())
+ .isEqualTo(materialCheckboxSize.toIntPx())
}
}
}
\ No newline at end of file
diff --git a/ui/material/src/androidTest/java/androidx/ui/material/MaterialTest.kt b/ui/material/src/androidTest/java/androidx/ui/material/MaterialTest.kt
index 216dbb2..ddae1d0 100644
--- a/ui/material/src/androidTest/java/androidx/ui/material/MaterialTest.kt
+++ b/ui/material/src/androidTest/java/androidx/ui/material/MaterialTest.kt
@@ -18,12 +18,15 @@
import androidx.compose.Composable
import androidx.compose.composer
+import androidx.ui.material.surface.Surface
import androidx.ui.test.android.AndroidUiTestRunner
fun AndroidUiTestRunner.setMaterialContent(composable: @Composable() () -> Unit) {
setContent {
MaterialTheme {
- composable()
+ Surface {
+ composable()
+ }
}
}
}
\ No newline at end of file
diff --git a/ui/material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt b/ui/material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
index bfafb67..09428f5 100644
--- a/ui/material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
+++ b/ui/material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
@@ -17,8 +17,14 @@
package androidx.ui.material
import androidx.test.filters.MediumTest
+import androidx.ui.core.OnChildPositioned
+import androidx.ui.core.PxSize
import androidx.ui.core.TestTag
+import androidx.ui.core.dp
+import androidx.ui.core.withDensity
import androidx.ui.layout.Column
+import androidx.ui.layout.Container
+import androidx.ui.layout.DpConstraints
import androidx.ui.test.android.AndroidUiTestRunner
import androidx.ui.test.assertIsInMutuallyExclusiveGroup
import androidx.ui.test.assertIsSelected
@@ -31,6 +37,8 @@
import androidx.compose.Composable
import androidx.compose.Model
import androidx.compose.composer
+import androidx.ui.core.round
+import com.google.common.truth.Truth
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -46,6 +54,8 @@
private val itemTwo = "Foo"
private val itemThree = "Sap"
+ private val materialRadioSize = 24.dp
+
private val unselectedRadioGroupItemSemantics = createFullSemantics(
inMutuallyExclusiveGroup = true,
isSelected = false
@@ -74,9 +84,9 @@
options.forEach { item ->
TestTag(tag = item) {
RadioGroupTextItem(
- text = item,
- selected = (select.selected == item),
- onSelected = { select.selected = item })
+ text = item,
+ selected = (select.selected == item),
+ onSelected = { select.selected = item })
}
}
}
@@ -106,9 +116,9 @@
options.forEach { item ->
TestTag(tag = item) {
RadioGroupTextItem(
- text = item,
- selected = (select.selected == item),
- onSelected = { select.selected = item })
+ text = item,
+ selected = (select.selected == item),
+ onSelected = { select.selected = item })
}
}
}
@@ -134,9 +144,9 @@
options.forEach { item ->
TestTag(tag = item) {
RadioGroupTextItem(
- text = item,
- selected = (select.selected == item),
- onSelected = { select.selected = item })
+ text = item,
+ selected = (select.selected == item),
+ onSelected = { select.selected = item })
}
}
}
@@ -162,9 +172,9 @@
options.forEach { item ->
TestTag(tag = item) {
RadioGroupTextItem(
- text = item,
- selected = (select.selected == item),
- onSelected = { select.selected = item })
+ text = item,
+ selected = (select.selected == item),
+ onSelected = { select.selected = item })
}
}
}
@@ -189,4 +199,36 @@
findByTag(itemTwo)
.assertSemanticsIsEqualTo(unselectedRadioGroupItemSemantics)
}
+
+ @Test
+ fun radioButton_materialSizes_whenSelected() {
+ materialSizesTestForValue(selected = true)
+ }
+
+ @Test
+ fun radioButton_materialSizes_whenNotSelected() {
+ materialSizesTestForValue(selected = false)
+ }
+
+ private fun materialSizesTestForValue(selected: Boolean) {
+ var radioSize: PxSize? = null
+ setMaterialContent {
+ Container(
+ constraints = DpConstraints(
+ maxWidth = 5000.dp,
+ maxHeight = 5000.dp
+ )
+ ) {
+ OnChildPositioned(onPositioned = { coordinates ->
+ radioSize = coordinates.size
+ }) {
+ RadioButton(selected = selected)
+ }
+ }
+ }
+ withDensity(density) {
+ Truth.assertThat(radioSize?.width?.round()).isEqualTo(materialRadioSize.toIntPx())
+ Truth.assertThat(radioSize?.height?.round()).isEqualTo(materialRadioSize.toIntPx())
+ }
+ }
}
\ No newline at end of file
diff --git a/ui/material/src/androidTest/java/androidx/ui/material/SwitchUiTest.kt b/ui/material/src/androidTest/java/androidx/ui/material/SwitchUiTest.kt
index 94869bf..85a49fb 100644
--- a/ui/material/src/androidTest/java/androidx/ui/material/SwitchUiTest.kt
+++ b/ui/material/src/androidTest/java/androidx/ui/material/SwitchUiTest.kt
@@ -17,8 +17,15 @@
package androidx.ui.material
import androidx.test.filters.MediumTest
+
+import androidx.ui.core.OnChildPositioned
+import androidx.ui.core.PxSize
import androidx.ui.core.TestTag
+import androidx.ui.core.dp
+import androidx.ui.core.withDensity
import androidx.ui.layout.Column
+import androidx.ui.layout.Container
+import androidx.ui.layout.DpConstraints
import androidx.ui.test.DisableTransitions
import androidx.ui.test.android.AndroidUiTestRunner
import androidx.ui.test.assertIsChecked
@@ -30,7 +37,8 @@
import androidx.ui.test.findByTag
import androidx.compose.state
import androidx.compose.unaryPlus
-import androidx.compose.composer
+import androidx.ui.core.round
+import com.google.common.truth.Truth
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,7 +61,7 @@
private val defaultSwitchTag = "switch"
@Test
- fun SwitchTest_defaultSemantics() {
+ fun switch_defaultSemantics() {
setMaterialContent {
Column {
TestTag(tag = "checked") {
@@ -70,7 +78,7 @@
}
@Test
- fun SwitchTest_toggle() {
+ fun switch_toggle() {
setMaterialContent {
val (checked, onChecked) = +state { false }
TestTag(tag = defaultSwitchTag) {
@@ -84,12 +92,12 @@
}
@Test
- fun SwitchTest_toggleTwice() {
+ fun switch_toggleTwice() {
setMaterialContent {
val (checked, onChecked) = +state { false }
TestTag(tag = defaultSwitchTag) {
- Switch(checked = checked, onClick = { onChecked(!checked)})
+ Switch(checked = checked, onClick = { onChecked(!checked) })
}
}
findByTag(defaultSwitchTag)
@@ -101,7 +109,7 @@
}
@Test
- fun SwitchTest_uncheckableWithNoLambda() {
+ fun switch_uncheckableWithNoLambda() {
setMaterialContent {
val (checked, _) = +state { false }
TestTag(tag = defaultSwitchTag) {
@@ -113,4 +121,38 @@
.doClick()
.assertIsNotChecked()
}
+
+ @Test
+ fun switch_materialSizes_whenChecked() {
+ materialSizesTestForValue(true)
+ }
+
+ @Test
+ fun switch_materialSizes_whenUnchecked() {
+ materialSizesTestForValue(false)
+ }
+
+ private fun materialSizesTestForValue(checked: Boolean) {
+ var switchSize: PxSize? = null
+ setMaterialContent {
+ Container(
+ constraints = DpConstraints(
+ maxWidth = 5000.dp,
+ maxHeight = 5000.dp
+ )
+ ) {
+ OnChildPositioned(onPositioned = { coordinates ->
+ switchSize = coordinates.size
+ }) {
+ Switch(checked = checked)
+ }
+ }
+ }
+ withDensity(density) {
+ Truth.assertThat(switchSize?.width?.round())
+ .isEqualTo(34.dp.toIntPx() + 2.dp.toIntPx() * 2)
+ Truth.assertThat(switchSize?.height?.round())
+ .isEqualTo(20.dp.toIntPx() + 2.dp.toIntPx() * 2)
+ }
+ }
}
\ No newline at end of file
diff --git a/ui/material/src/main/java/androidx/ui/baseui/Clickable.kt b/ui/material/src/main/java/androidx/ui/baseui/Clickable.kt
index 362f1e1..acfec85 100644
--- a/ui/material/src/main/java/androidx/ui/baseui/Clickable.kt
+++ b/ui/material/src/main/java/androidx/ui/baseui/Clickable.kt
@@ -41,18 +41,20 @@
@Children children: @Composable() () -> Unit
) {
Semantics(
- button=true,
- enabled=(onClick != null),
- actions=if (onClick != null) {
+ button = true,
+ enabled = (onClick != null),
+ actions = if (onClick != null) {
// TODO(ryanmentley): The unnecessary generic type specification works around an IR bug
listOf<SemanticsAction<*>>(SemanticsAction(SemanticsActionType.Tap, onClick))
} else {
emptyList<SemanticsAction<*>>()
- }) {
+ }
+ ) {
PressReleasedGestureDetector(
onRelease = onClick,
- consumeDownOnStart = consumeDownOnStart) {
+ consumeDownOnStart = consumeDownOnStart
+ ) {
children()
}
}
-}
+}
\ No newline at end of file
diff --git a/ui/material/src/main/java/androidx/ui/baseui/selection/MutuallyExclusiveSetItem.kt b/ui/material/src/main/java/androidx/ui/baseui/selection/MutuallyExclusiveSetItem.kt
index 704a64a..c9ca5d8 100644
--- a/ui/material/src/main/java/androidx/ui/baseui/selection/MutuallyExclusiveSetItem.kt
+++ b/ui/material/src/main/java/androidx/ui/baseui/selection/MutuallyExclusiveSetItem.kt
@@ -16,11 +16,11 @@
package androidx.ui.baseui.selection
-import androidx.ui.core.gesture.PressGestureDetector
-import androidx.ui.core.Semantics
import androidx.compose.Children
import androidx.compose.Composable
import androidx.compose.composer
+import androidx.ui.core.Semantics
+import androidx.ui.core.gesture.PressReleasedGestureDetector
/**
* Component for representing one option out of many
@@ -37,10 +37,14 @@
onSelected: () -> Unit,
@Children children: @Composable() () -> Unit
) {
- PressGestureDetector(onPress = { onSelected() }) {
+ PressReleasedGestureDetector(
+ onRelease = onSelected,
+ consumeDownOnStart = false
+ ) {
Semantics(
inMutuallyExclusiveGroup = true,
- selected = selected) {
+ selected = selected
+ ) {
children()
}
}
diff --git a/ui/material/src/main/java/androidx/ui/baseui/selection/Toggleable.kt b/ui/material/src/main/java/androidx/ui/baseui/selection/Toggleable.kt
index bf1cdc8..a4957c8 100644
--- a/ui/material/src/main/java/androidx/ui/baseui/selection/Toggleable.kt
+++ b/ui/material/src/main/java/androidx/ui/baseui/selection/Toggleable.kt
@@ -17,7 +17,7 @@
package androidx.ui.baseui.selection
import androidx.ui.core.Semantics
-import androidx.ui.core.gesture.PressGestureDetector
+import androidx.ui.core.gesture.PressReleasedGestureDetector
import androidx.ui.core.semantics.SemanticsAction
import androidx.ui.core.semantics.SemanticsActionType
import androidx.compose.Children
@@ -35,8 +35,9 @@
} else {
emptyList()
}
- // TODO should we use PressReleasedGestureDetector?
- PressGestureDetector(onRelease = onToggle) {
+ PressReleasedGestureDetector(
+ onRelease = onToggle,
+ consumeDownOnStart = false) {
// TODO: enabled should not be hardcoded
// TODO(pavlis): Semantics currently doesn't support 4 states (only checked / unchecked / not checkable).
Semantics(
diff --git a/ui/material/src/main/java/androidx/ui/material/Checkbox.kt b/ui/material/src/main/java/androidx/ui/material/Checkbox.kt
index f8308a3..a8b63e4 100644
--- a/ui/material/src/main/java/androidx/ui/material/Checkbox.kt
+++ b/ui/material/src/main/java/androidx/ui/material/Checkbox.kt
@@ -24,14 +24,13 @@
import androidx.ui.baseui.selection.Toggleable
import androidx.ui.baseui.selection.ToggleableState
import androidx.ui.core.Draw
-import androidx.ui.core.Layout
-import androidx.ui.core.coerceIn
import androidx.ui.core.dp
import androidx.ui.engine.geometry.Offset
import androidx.ui.engine.geometry.RRect
import androidx.ui.engine.geometry.Radius
import androidx.ui.engine.geometry.shrink
import androidx.ui.engine.geometry.withRadius
+import androidx.ui.layout.Container
import androidx.ui.layout.Padding
import androidx.ui.painting.Color
import androidx.ui.painting.Paint
@@ -41,6 +40,8 @@
import androidx.compose.composer
import androidx.compose.memo
import androidx.compose.unaryPlus
+import androidx.ui.layout.Wrap
+import androidx.ui.material.ripple.Ripple
// TODO(malkov): think about how to abstract it better
/**
@@ -73,16 +74,15 @@
onClick: (() -> Unit)? = null,
color: Color? = null
) {
- Toggleable(value = value, onToggle = onClick) {
- Padding(padding = CheckboxDefaultPadding) {
- Layout(
- children = { DrawCheckbox(value = value, color = color) },
- layoutBlock = { _, constraints ->
- val checkboxSizePx = CheckboxSize.toIntPx()
- val height = checkboxSizePx.coerceIn(constraints.minHeight, constraints.maxHeight)
- val width = checkboxSizePx.coerceIn(constraints.minWidth, constraints.maxWidth)
- layout(width, height) {}
- })
+ Wrap {
+ Ripple {
+ Toggleable(value = value, onToggle = onClick) {
+ Padding(padding = CheckboxDefaultPadding) {
+ Container(width = CheckboxSize, height = CheckboxSize) {
+ DrawCheckbox(value = value, color = color)
+ }
+ }
+ }
}
}
}
@@ -90,29 +90,32 @@
@Composable
private fun DrawCheckbox(value: ToggleableState, color: Color?) {
val activeColor = +color.orFromTheme { secondary }
- val definition = +memo(activeColor) {
- generateTransitionDefinition(activeColor)
+ val unselectedColor = (+themeColor { onSurface }).withOpacity(UncheckedBoxOppacity)
+ val definition = +memo(activeColor, unselectedColor) {
+ generateTransitionDefinition(activeColor, unselectedColor)
}
// TODO: remove @Composable annotation here when b/131681875 is fixed
Transition(definition = definition, toState = value) @Composable { state ->
DrawBox(
color = state[BoxColorProp],
- innerRadiusFraction = state[InnerRadiusFractionProp])
+ innerRadiusFraction = state[InnerRadiusFractionProp]
+ )
DrawCheck(
checkFraction = state[CheckFractionProp],
- crossCenterGravitation = state[CenterGravitationForCheck])
+ crossCenterGravitation = state[CenterGravitationForCheck]
+ )
}
}
@Composable
private fun DrawBox(color: Color, innerRadiusFraction: Float) {
- Draw { canvas, _ ->
+ Draw { canvas, parentSize ->
val paint = Paint()
paint.strokeWidth = StrokeWidth.toPx().value
paint.isAntiAlias = true
paint.color = color
- val checkboxSize = CheckboxSize.toPx().value
+ val checkboxSize = parentSize.width.value
val outer = RRect(
0f,
@@ -142,7 +145,7 @@
checkFraction: Float,
crossCenterGravitation: Float
) {
- Draw { canvas, _ ->
+ Draw { canvas, parentSize ->
val paint = Paint()
paint.isAntiAlias = true
paint.style = PaintingStyle.stroke
@@ -150,7 +153,7 @@
paint.strokeWidth = StrokeWidth.toPx().value
paint.color = CheckStrokeDefaultColor
- val width = CheckboxSize.toPx().value
+ val width = parentSize.width.value
val checkCrossX = 0.4f
val checkCrossY = 0.7f
@@ -193,54 +196,55 @@
private val BoxAnimationDuration = 100
private val CheckStrokeAnimationDuration = 100
-private fun generateTransitionDefinition(color: Color) = transitionDefinition {
- state(ToggleableState.Checked) {
- this[CheckFractionProp] = 1f
- this[InnerRadiusFractionProp] = 1f
- this[CenterGravitationForCheck] = 0f
- this[BoxColorProp] = color
- }
- state(ToggleableState.Unchecked) {
- this[CheckFractionProp] = 0f
- this[InnerRadiusFractionProp] = 0f
- this[CenterGravitationForCheck] = 1f
- this[BoxColorProp] = UncheckedBoxColor
- }
- state(ToggleableState.Indeterminate) {
- this[CheckFractionProp] = 1f
- this[InnerRadiusFractionProp] = 1f
- this[CenterGravitationForCheck] = 1f
- this[BoxColorProp] = color
- }
- transition(fromState = ToggleableState.Unchecked, toState = ToggleableState.Checked) {
- boxTransitionFromUnchecked()
- CenterGravitationForCheck using tween {
- duration = 0
+private fun generateTransitionDefinition(color: Color, unselectedColor: Color) =
+ transitionDefinition {
+ state(ToggleableState.Checked) {
+ this[CheckFractionProp] = 1f
+ this[InnerRadiusFractionProp] = 1f
+ this[CenterGravitationForCheck] = 0f
+ this[BoxColorProp] = color
+ }
+ state(ToggleableState.Unchecked) {
+ this[CheckFractionProp] = 0f
+ this[InnerRadiusFractionProp] = 0f
+ this[CenterGravitationForCheck] = 1f
+ this[BoxColorProp] = unselectedColor
+ }
+ state(ToggleableState.Indeterminate) {
+ this[CheckFractionProp] = 1f
+ this[InnerRadiusFractionProp] = 1f
+ this[CenterGravitationForCheck] = 1f
+ this[BoxColorProp] = color
+ }
+ transition(fromState = ToggleableState.Unchecked, toState = ToggleableState.Checked) {
+ boxTransitionFromUnchecked()
+ CenterGravitationForCheck using tween {
+ duration = 0
+ }
+ }
+ transition(fromState = ToggleableState.Checked, toState = ToggleableState.Unchecked) {
+ boxTransitionToUnchecked()
+ CenterGravitationForCheck using tween {
+ duration = CheckStrokeAnimationDuration
+ }
+ }
+ transition(fromState = ToggleableState.Checked, toState = ToggleableState.Indeterminate) {
+ CenterGravitationForCheck using tween {
+ duration = CheckStrokeAnimationDuration
+ }
+ }
+ transition(fromState = ToggleableState.Indeterminate, toState = ToggleableState.Checked) {
+ CenterGravitationForCheck using tween {
+ duration = CheckStrokeAnimationDuration
+ }
+ }
+ transition(fromState = ToggleableState.Indeterminate, toState = ToggleableState.Unchecked) {
+ boxTransitionToUnchecked()
+ }
+ transition(fromState = ToggleableState.Unchecked, toState = ToggleableState.Indeterminate) {
+ boxTransitionFromUnchecked()
}
}
- transition(fromState = ToggleableState.Checked, toState = ToggleableState.Unchecked) {
- boxTransitionToUnchecked()
- CenterGravitationForCheck using tween {
- duration = CheckStrokeAnimationDuration
- }
- }
- transition(fromState = ToggleableState.Checked, toState = ToggleableState.Indeterminate) {
- CenterGravitationForCheck using tween {
- duration = CheckStrokeAnimationDuration
- }
- }
- transition(fromState = ToggleableState.Indeterminate, toState = ToggleableState.Checked) {
- CenterGravitationForCheck using tween {
- duration = CheckStrokeAnimationDuration
- }
- }
- transition(fromState = ToggleableState.Indeterminate, toState = ToggleableState.Unchecked) {
- boxTransitionToUnchecked()
- }
- transition(fromState = ToggleableState.Unchecked, toState = ToggleableState.Indeterminate) {
- boxTransitionFromUnchecked()
- }
-}
private fun TransitionSpec.boxTransitionFromUnchecked() {
BoxColorProp using tween {
@@ -273,5 +277,5 @@
private val StrokeWidth = 2.dp
private val RadiusSize = 2.dp
-private val UncheckedBoxColor = Color(0xFF7D7D7D.toInt())
+private val UncheckedBoxOppacity = 0.6f
private val CheckStrokeDefaultColor = Color(0xFFFFFFFF.toInt())
\ No newline at end of file
diff --git a/ui/material/src/main/java/androidx/ui/material/RadioButton.kt b/ui/material/src/main/java/androidx/ui/material/RadioButton.kt
index b1a87aa..e51952d 100644
--- a/ui/material/src/main/java/androidx/ui/material/RadioButton.kt
+++ b/ui/material/src/main/java/androidx/ui/material/RadioButton.kt
@@ -24,19 +24,16 @@
import androidx.ui.core.DensityReceiver
import androidx.ui.core.Dp
import androidx.ui.core.Draw
-import androidx.ui.core.Layout
-import androidx.ui.core.Layout
import androidx.ui.core.PxSize
import androidx.ui.core.Text
import androidx.ui.core.dp
-import androidx.ui.core.max
-import androidx.ui.core.min
import androidx.ui.engine.geometry.Offset
import androidx.ui.engine.geometry.RRect
import androidx.ui.engine.geometry.Radius
import androidx.ui.engine.geometry.shift
import androidx.ui.engine.geometry.shrink
import androidx.ui.layout.Column
+import androidx.ui.layout.Container
import androidx.ui.layout.EdgeInsets
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.layout.MainAxisSize
@@ -52,6 +49,7 @@
import androidx.compose.composer
import androidx.compose.memo
import androidx.compose.unaryPlus
+import androidx.ui.material.ripple.BoundedRipple
/**
* Components for creating mutually exclusive set of [RadioButton]s.
@@ -142,10 +140,14 @@
onSelected: () -> Unit,
@Children children: @Composable() () -> Unit
) {
- MutuallyExclusiveSetItem(
- selected = selected,
- onSelected = { if (!selected) onSelected() }) {
- children()
+ Container {
+ BoundedRipple {
+ MutuallyExclusiveSetItem(
+ selected = selected,
+ onSelected = { if (!selected) onSelected() }) {
+ children()
+ }
+ }
}
}
@@ -202,27 +204,24 @@
selected: Boolean,
color: Color? = null
) {
- Layout(children = {
- val activeColor = +color.orFromTheme { primary }
- val definition = +memo(activeColor) {
- generateTransitionDefinition(activeColor)
+ Padding(padding = RadioButtonPadding) {
+ Container(width = RadioButtonSize, height = RadioButtonSize) {
+ val selectedColor = +color.orFromTheme { secondary }
+ val unselectedColor = (+themeColor { onSurface }).withOpacity(UnselectedOpacity)
+ val definition = +memo(selectedColor, unselectedColor) {
+ generateTransitionDefinition(selectedColor, unselectedColor)
+ }
+ // TODO: remove @Composable annotation here when b/131681875 is fixed
+ Transition(definition = definition, toState = selected) @Composable { state ->
+ DrawRadioButton(
+ color = state[ColorProp],
+ outerRadius = state[OuterRadiusProp],
+ innerRadius = state[InnerRadiusProp],
+ gap = state[GapProp]
+ )
+ }
}
- // TODO: remove @Composable annotation here when b/131681875 is fixed
- Transition(definition = definition, toState = selected) @Composable { state ->
- DrawRadioButton(
- color = state[ColorProp],
- outerRadius = state[OuterRadiusProp],
- innerRadius = state[InnerRadiusProp],
- gap = state[GapProp])
- }
- }, layoutBlock = { _, constraints ->
- val size = RadioRadius.toIntPx() * 2
- val w = max(constraints.minWidth, min(constraints.maxWidth, size))
- val h = max(constraints.minHeight, min(constraints.maxHeight, size))
- layout(w, h) {
- // no children to place
- }
- })
+ }
}
@Composable
@@ -282,18 +281,21 @@
private val GapDuration = 150
private val TotalDuration = RadiusClosureDuration + PulseDuration + GapDuration
-private fun generateTransitionDefinition(activeColor: Color) = transitionDefinition {
+private fun generateTransitionDefinition(
+ selectedColor: Color,
+ unselectedColor: Color
+) = transitionDefinition {
state(false) {
this[OuterRadiusProp] = RadioRadius
this[InnerRadiusProp] = InitialInner
this[GapProp] = 0.dp
- this[ColorProp] = UnselectedRadioColor
+ this[ColorProp] = unselectedColor
}
state(true) {
this[OuterRadiusProp] = RadioRadius
this[InnerRadiusProp] = 0.dp
this[GapProp] = DefaultGap
- this[ColorProp] = activeColor
+ this[ColorProp] = selectedColor
}
transition(fromState = false, toState = true) {
ColorProp using tween {
@@ -339,13 +341,13 @@
}
}
-// TODO(malkov): see how it goes and maybe move it to styles or cross-widget defaults
-private val UnselectedRadioColor = Color(0xFF7D7D7D.toInt())
-
-// TODO(malkov): random numbers for now to produce radio as in material comp.
-private val RadioRadius = 10.dp
+private val RadioButtonPadding = 2.dp
+private val RadioButtonSize = 20.dp
+private val RadioRadius = RadioButtonSize / 2
private val RadioStrokeWidth = 2.dp
+// TODO(malkov): random numbers for now to produce radio as in material comp.
private val DefaultGap = 3.dp
+private val UnselectedOpacity = 0.6f
// for animations
private val OuterOffsetDuringAnimation = 2.dp
diff --git a/ui/material/src/main/java/androidx/ui/material/Switch.kt b/ui/material/src/main/java/androidx/ui/material/Switch.kt
index 5ac0fc9..a5d48c7 100644
--- a/ui/material/src/main/java/androidx/ui/material/Switch.kt
+++ b/ui/material/src/main/java/androidx/ui/material/Switch.kt
@@ -17,21 +17,18 @@
package androidx.ui.material
import androidx.animation.ColorPropKey
+import androidx.animation.FastOutSlowInEasing
import androidx.animation.FloatPropKey
import androidx.animation.TransitionSpec
import androidx.animation.transitionDefinition
import androidx.ui.animation.Transition
import androidx.ui.baseui.selection.Toggleable
import androidx.ui.baseui.selection.ToggleableState
-import androidx.ui.core.Constraints
import androidx.ui.core.DensityReceiver
import androidx.ui.core.Draw
-import androidx.ui.core.Layout
-import androidx.ui.core.PxSize
-import androidx.ui.core.coerceIn
import androidx.ui.core.dp
-import androidx.ui.core.ipx
import androidx.ui.engine.geometry.Offset
+import androidx.ui.layout.Container
import androidx.ui.painting.Canvas
import androidx.ui.painting.Color
import androidx.ui.painting.Paint
@@ -40,6 +37,10 @@
import androidx.compose.composer
import androidx.compose.memo
import androidx.compose.unaryPlus
+import androidx.ui.core.PxSize
+import androidx.ui.layout.Padding
+import androidx.ui.layout.Wrap
+import androidx.ui.material.ripple.Ripple
/**
* A Switch is a two state toggleable component that provides on/off like options
@@ -48,7 +49,7 @@
* @param onClick callback to be invoked when Switch is clicked.
* if [null], Switch will show static [checked] state and remain disabled
* @param color optional active color for Switch,
- * by default [androidx.ui.material.MaterialColors.primary] will be used
+ * by default [androidx.ui.material.MaterialColors.secondaryVariant] will be used
*/
@Composable
fun Switch(
@@ -57,34 +58,38 @@
color: Color? = null
) {
val value = if (checked) ToggleableState.Checked else ToggleableState.Unchecked
- Toggleable(value = value, onToggle = onClick) {
- val children = @Composable { DrawSwitch(checked = checked, color = color) }
- Layout(children = children, layoutBlock = { measurables, constraints ->
- val height =
- MinHeight.toIntPx().coerceIn(constraints.minHeight, constraints.maxHeight)
- val width =
- MinWidth.toIntPx().coerceIn(constraints.minWidth, constraints.maxWidth)
- val ps = measurables.map { m ->
- m.measure(Constraints.tightConstraints(width, height))
+ Wrap {
+ Ripple {
+ Toggleable(value = value, onToggle = onClick) {
+ Padding(padding = DefaultSwitchPadding) {
+ Container(width = SwitchWidth, height = SwitchHeight) {
+ DrawSwitch(checked = checked, color = color)
+ }
+ }
}
- layout(width, height) { ps.forEach { it.place(0.ipx, 0.ipx) } }
- })
+ }
}
}
@Composable
private fun DrawSwitch(checked: Boolean, color: Color? = null) {
- val activeColor = +color.orFromTheme { primary }
- val transDef = +memo(activeColor) {
- generateTransitionDefinition(activeColor)
+ val checkedThumbColor = +color.orFromTheme { secondaryVariant }
+ val uncheckedThumbColor = +themeColor { surface }
+ val transDef = +memo(checkedThumbColor, uncheckedThumbColor) {
+ generateTransitionDefinition(checkedThumbColor, uncheckedThumbColor)
}
- val trackColor = if (checked) activeColor else UncheckedTrackColor
+ val trackColor = if (checked) {
+ checkedThumbColor.withOpacity(CheckedTrackOpacity)
+ } else {
+ (+themeColor { onSurface }).withOpacity(UncheckedTrackOpacity)
+ }
DrawTrack(color = trackColor)
// TODO: remove @Composable annotation here when b/131681875 is fixed
Transition(definition = transDef, toState = checked) @Composable { state ->
DrawThumb(
color = state[ThumbColorProp],
- relativePosition = state[RelativeThumbTranslationProp])
+ relativePosition = state[RelativeThumbTranslationProp]
+ )
}
}
@@ -108,21 +113,19 @@
color: Color
) {
val paint = Paint()
-
paint.isAntiAlias = true
paint.color = color
- .withAlpha(TrackAlpha)
paint.strokeCap = StrokeCap.round
- paint.strokeWidth = TrackHeight.toPx().value
+ paint.strokeWidth = TrackStrokeWidth.toPx().value
- // TODO(malkov): currently Switch gravity is always CENTER but we need to be flexible
- val centerHeight = parentSize.height.value / 2
- val centerWidth = parentSize.width.value / 2
+ val strokeRadius = TrackStrokeWidth / 2
+ val centerHeight = parentSize.height / 2
- val startW = centerWidth - TrackWidth.toPx().value / 2
- val endW = centerWidth + TrackWidth.toPx().value / 2
-
- canvas.drawLine(Offset(startW, centerHeight), Offset(endW, centerHeight), paint)
+ canvas.drawLine(
+ Offset(strokeRadius.toPx().value, centerHeight.value),
+ Offset((TrackWidth - strokeRadius).toPx().value, centerHeight.value),
+ paint
+ )
}
private fun DensityReceiver.drawThumb(
@@ -132,57 +135,60 @@
color: Color
) {
val paint = Paint()
-
paint.isAntiAlias = true
paint.color = color
- // currently I assume that layout gravity of Switch is always CENTER
- val centerHeight = parentSize.height.value / 2
- val centerWidth = parentSize.width.value / 2
+ val centerHeight = parentSize.height / 2
+ val thumbRadius = ThumbDiameter / 2
+ val thumbPathWidth = TrackWidth - ThumbDiameter
- val thumbTrackWidth = TrackWidth + PointRadius
- val thumbStartPoint = centerWidth - thumbTrackWidth.toPx().value / 2
-
- val start = thumbStartPoint + thumbTrackWidth.toPx().value * relativePosition
- canvas.drawCircle(Offset(start, centerHeight), PointRadius.toPx().value, paint)
+ val start = thumbRadius + thumbPathWidth * relativePosition
+ canvas.drawCircle(
+ Offset(start.toPx().value, centerHeight.value),
+ // TODO(malkov): wierd +1 but necessary in order to properly cover track. Investigate
+ thumbRadius.toPx().value + 1,
+ paint
+ )
}
private val RelativeThumbTranslationProp = FloatPropKey()
private val ThumbColorProp = ColorPropKey()
private const val SwitchAnimationDuration = 100
-private fun generateTransitionDefinition(activeColor: Color) = transitionDefinition {
- fun <T> TransitionSpec.switchTween() = tween<T> {
- duration = SwitchAnimationDuration
+private fun generateTransitionDefinition(checkedColor: Color, uncheckedColor: Color) =
+ transitionDefinition {
+ fun <T> TransitionSpec.switchTween() = tween<T> {
+ duration = SwitchAnimationDuration
+ easing = FastOutSlowInEasing
+ }
+
+ state(false) {
+ this[RelativeThumbTranslationProp] = 0f
+ this[ThumbColorProp] = uncheckedColor
+ }
+ state(true) {
+ this[RelativeThumbTranslationProp] = 1f
+ this[ThumbColorProp] = checkedColor
+ }
+ transition(fromState = false, toState = true) {
+ RelativeThumbTranslationProp using switchTween()
+ ThumbColorProp using switchTween()
+ }
+ transition(fromState = true, toState = false) {
+ RelativeThumbTranslationProp using switchTween()
+ ThumbColorProp using switchTween()
+ }
}
- state(false) {
- this[RelativeThumbTranslationProp] = 0f
- this[ThumbColorProp] = UncheckedThumbColor
- }
- state(true) {
- this[RelativeThumbTranslationProp] = 1f
- this[ThumbColorProp] = activeColor
- }
- transition(fromState = false, toState = true) {
- RelativeThumbTranslationProp using switchTween()
- ThumbColorProp using switchTween()
- }
- transition(fromState = true, toState = false) {
- RelativeThumbTranslationProp using switchTween()
- ThumbColorProp using switchTween()
- }
-}
+private val CheckedTrackOpacity = 0.54f
+private val UncheckedTrackOpacity = 0.38f
-// TODO(malkov): see how it goes and maybe move it to styles or cross-widget defaults
-private val UncheckedThumbColor = Color(0xFFEBEBEB.toInt())
-private val UncheckedTrackColor = Color(0xFF7D7D7D.toInt())
-private const val TrackAlpha = 75
+private val TrackWidth = 34.dp
+private val TrackStrokeWidth = 14.dp
-// TODO(malkov): random numbers which produce the same switch as android.widget.Switch
-private val MinWidth = 47.5.dp
-private val MinHeight = 27.25.dp
+private val ThumbDiameter = 20.dp
-private val TrackHeight = 14.dp
-private val TrackWidth = 11.dp
-private val PointRadius = 10.dp
\ No newline at end of file
+// TODO(malkov): clarify this padding for Switch
+private val DefaultSwitchPadding = 2.dp
+private val SwitchWidth = TrackWidth
+private val SwitchHeight = ThumbDiameter
\ No newline at end of file
diff --git a/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/WorkManagerGcmDispatcherTest.kt b/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/WorkManagerGcmDispatcherTest.kt
index b04c246..1948994 100644
--- a/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/WorkManagerGcmDispatcherTest.kt
+++ b/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/WorkManagerGcmDispatcherTest.kt
@@ -22,7 +22,7 @@
import androidx.arch.core.executor.TaskExecutor
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
+import androidx.test.filters.MediumTest
import androidx.work.Configuration
import androidx.work.impl.WorkManagerImpl
import androidx.work.impl.utils.SynchronousExecutor
@@ -36,7 +36,7 @@
import java.util.concurrent.Executor
@RunWith(AndroidJUnit4::class)
-@SmallTest
+@MediumTest
class WorkManagerGcmDispatcherTest {
lateinit var mContext: Context
lateinit var mExecutor: Executor