Merge "Do not allow custom Parcelables for media2" into androidx-master-dev
diff --git a/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt b/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt
index 2b7b97f..0cdc8cc 100644
--- a/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt
+++ b/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt
@@ -20,7 +20,6 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelLazy
import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
import androidx.lifecycle.ViewModelProvider.Factory
/**
@@ -42,10 +41,7 @@
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
- val application = application ?: throw IllegalArgumentException(
- "ViewModel can be accessed only when Activity is attached"
- )
- AndroidViewModelFactory.getInstance(application)
+ defaultViewModelProviderFactory
}
return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
diff --git a/activity/activity/api/1.1.0-alpha02.txt b/activity/activity/api/1.1.0-alpha02.txt
index cd98da6..f087fd3 100644
--- a/activity/activity/api/1.1.0-alpha02.txt
+++ b/activity/activity/api/1.1.0-alpha02.txt
@@ -1,9 +1,10 @@
// Signature format: 3.0
package androidx.activity {
- public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+ public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
ctor public ComponentActivity();
ctor @ContentView public ComponentActivity(@LayoutRes int);
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
method @Deprecated public Object? getLastCustomNonConfigurationInstance();
method public androidx.lifecycle.Lifecycle getLifecycle();
method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
diff --git a/activity/activity/api/current.txt b/activity/activity/api/current.txt
index cd98da6..f087fd3 100644
--- a/activity/activity/api/current.txt
+++ b/activity/activity/api/current.txt
@@ -1,9 +1,10 @@
// Signature format: 3.0
package androidx.activity {
- public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+ public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
ctor public ComponentActivity();
ctor @ContentView public ComponentActivity(@LayoutRes int);
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
method @Deprecated public Object? getLastCustomNonConfigurationInstance();
method public androidx.lifecycle.Lifecycle getLifecycle();
method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
diff --git a/activity/activity/api/restricted_1.1.0-alpha02.txt b/activity/activity/api/restricted_1.1.0-alpha02.txt
index cd98da6..f087fd3 100644
--- a/activity/activity/api/restricted_1.1.0-alpha02.txt
+++ b/activity/activity/api/restricted_1.1.0-alpha02.txt
@@ -1,9 +1,10 @@
// Signature format: 3.0
package androidx.activity {
- public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+ public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
ctor public ComponentActivity();
ctor @ContentView public ComponentActivity(@LayoutRes int);
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
method @Deprecated public Object? getLastCustomNonConfigurationInstance();
method public androidx.lifecycle.Lifecycle getLifecycle();
method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
diff --git a/activity/activity/api/restricted_current.txt b/activity/activity/api/restricted_current.txt
index cd98da6..f087fd3 100644
--- a/activity/activity/api/restricted_current.txt
+++ b/activity/activity/api/restricted_current.txt
@@ -1,9 +1,10 @@
// Signature format: 3.0
package androidx.activity {
- public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+ public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
ctor public ComponentActivity();
ctor @ContentView public ComponentActivity(@LayoutRes int);
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
method @Deprecated public Object? getLastCustomNonConfigurationInstance();
method public androidx.lifecycle.Lifecycle getLifecycle();
method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index 81bb273..36959fc 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -18,10 +18,11 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
api(project(":lifecycle:lifecycle-runtime"))
api(project(":lifecycle:lifecycle-viewmodel"))
api("androidx.savedstate:savedstate:1.0.0-rc01")
+ api(project(":lifecycle:lifecycle-viewmodel-savedstate"))
androidTestImplementation(KOTLIN_STDLIB)
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt
index b7a8b3f..e52b140 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt
@@ -16,7 +16,10 @@
package androidx.activity
+import android.app.Application
import android.os.Bundle
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStore
@@ -72,12 +75,18 @@
fun testActivityOnCleared() {
lateinit var activityModel: TestViewModel
lateinit var defaultActivityModel: TestViewModel
+ lateinit var androidModel: TestAndroidViewModel
+ lateinit var savedStateModel: TestSavedStateViewModel
ActivityScenario.launch(ViewModelActivity::class.java).use { scenario ->
activityModel = scenario.withActivity { this.activityModel }
defaultActivityModel = scenario.withActivity { this.defaultActivityModel }
+ androidModel = scenario.withActivity { this.androidModel }
+ savedStateModel = scenario.withActivity { this.savedStateModel }
}
assertThat(activityModel.cleared).isTrue()
assertThat(defaultActivityModel.cleared).isTrue()
+ assertThat(androidModel.cleared).isTrue()
+ assertThat(savedStateModel.cleared).isTrue()
}
}
@@ -91,18 +100,19 @@
lateinit var postOnCreateViewModelStore: ViewModelStore
lateinit var activityModel: TestViewModel
lateinit var defaultActivityModel: TestViewModel
+ lateinit var androidModel: TestAndroidViewModel
+ lateinit var savedStateModel: TestSavedStateViewModel
override fun onCreate(savedInstanceState: Bundle?) {
preOnCreateViewModelStore = viewModelStore
super.onCreate(savedInstanceState)
postOnCreateViewModelStore = viewModelStore
- val viewModelProvider = ViewModelProvider(
- this,
- ViewModelProvider.NewInstanceFactory()
- )
+ val viewModelProvider = ViewModelProvider(this)
activityModel = viewModelProvider.get(KEY_ACTIVITY_MODEL, TestViewModel::class.java)
defaultActivityModel = viewModelProvider.get(TestViewModel::class.java)
+ androidModel = viewModelProvider.get(TestAndroidViewModel::class.java)
+ savedStateModel = viewModelProvider.get(TestSavedStateViewModel::class.java)
}
}
@@ -112,4 +122,21 @@
override fun onCleared() {
cleared = true
}
-}
\ No newline at end of file
+}
+
+class TestAndroidViewModel(application: Application) : AndroidViewModel(application) {
+ var cleared = false
+
+ override fun onCleared() {
+ cleared = true
+ }
+}
+
+@Suppress("unused")
+class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel() {
+ var cleared = false
+
+ override fun onCleared() {
+ cleared = true
+ }
+}
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
index a596e73..cee2d27 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
+++ b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
@@ -29,11 +29,14 @@
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.lifecycle.HasDefaultViewModelProviderFactory;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import androidx.lifecycle.ReportFragment;
+import androidx.lifecycle.SavedStateViewModelFactory;
+import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.savedstate.SavedStateRegistry;
@@ -50,6 +53,7 @@
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
+ HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
@@ -64,6 +68,7 @@
// Lazily recreated from NonConfigurationInstances by getViewModelStore()
private ViewModelStore mViewModelStore;
+ private ViewModelProvider.Factory mDefaultFactory;
private final OnBackPressedDispatcher mOnBackPressedDispatcher =
new OnBackPressedDispatcher(new Runnable() {
@@ -273,6 +278,29 @@
}
/**
+ * {@inheritDoc}
+ *
+ * <p>The extras of {@link #getIntent()} when this is first called will be used as
+ * the defaults to any {@link androidx.lifecycle.SavedStateHandle} passed to a view model
+ * created using this factory.</p>
+ */
+ @NonNull
+ @Override
+ public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+ if (getApplication() == null) {
+ throw new IllegalStateException("Your activity is not yet attached to the "
+ + "Application instance. You can't request ViewModel before onCreate call.");
+ }
+ if (mDefaultFactory == null) {
+ mDefaultFactory = new SavedStateViewModelFactory(
+ getApplication(),
+ this,
+ getIntent() != null ? getIntent().getExtras() : null);
+ }
+ return mDefaultFactory;
+ }
+
+ /**
* Called when the activity has detected the user's press of the back
* key. The {@link #getOnBackPressedDispatcher() OnBackPressedDispatcher} will be given a
* chance to handle the back button before the default behavior of
diff --git a/ads/ads-identifier-provider/build.gradle b/ads/ads-identifier-provider/build.gradle
index 78b4ade..535bbde 100644
--- a/ads/ads-identifier-provider/build.gradle
+++ b/ads/ads-identifier-provider/build.gradle
@@ -27,7 +27,7 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
implementation(AUTO_VALUE_ANNOTATIONS)
annotationProcessor(AUTO_VALUE)
diff --git a/ads/ads-identifier/build.gradle b/ads/ads-identifier/build.gradle
index fed8afc..3ab81a6 100644
--- a/ads/ads-identifier/build.gradle
+++ b/ads/ads-identifier/build.gradle
@@ -27,11 +27,11 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
implementation(AUTO_VALUE_ANNOTATIONS)
annotationProcessor(AUTO_VALUE)
api(GUAVA_LISTENABLE_FUTURE)
- implementation(project(":concurrent:concurrent-futures"))
+ implementation("androidx.concurrent:concurrent-futures:1.0.0-beta01")
implementation(project(":ads-identifier-common"))
diff --git a/animation/build.gradle b/animation/build.gradle
index 65781d0..1d1800e 100644
--- a/animation/build.gradle
+++ b/animation/build.gradle
@@ -26,7 +26,7 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT, libs.exclude_for_espresso)
diff --git a/animation/integration-tests/testapp/build.gradle b/animation/integration-tests/testapp/build.gradle
index 82a6fe05..a9992a4 100644
--- a/animation/integration-tests/testapp/build.gradle
+++ b/animation/integration-tests/testapp/build.gradle
@@ -23,7 +23,7 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
implementation(project(":animation"))
implementation(project(":animation:testing"))
diff --git a/animation/testing/build.gradle b/animation/testing/build.gradle
index f33191e..ad98a4c 100644
--- a/animation/testing/build.gradle
+++ b/animation/testing/build.gradle
@@ -26,7 +26,7 @@
dependencies {
implementation("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
implementation(project(":animation"))
implementation(ANDROIDX_TEST_EXT_JUNIT)
implementation(ANDROIDX_TEST_CORE)
diff --git a/appcompat/benchmark/build.gradle b/appcompat/benchmark/build.gradle
index 8ff9b65..962cb98 100644
--- a/appcompat/benchmark/build.gradle
+++ b/appcompat/benchmark/build.gradle
@@ -21,6 +21,7 @@
id("AndroidXPlugin")
id("com.android.library")
id("kotlin-android")
+ id("androidx.benchmark")
}
dependencies {
diff --git a/appcompat/build.gradle b/appcompat/build.gradle
index 5ce2ab6..e18e0bd9 100644
--- a/appcompat/build.gradle
+++ b/appcompat/build.gradle
@@ -12,7 +12,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.0.0")
api("androidx.cursoradapter:cursoradapter:1.0.0")
api("androidx.fragment:fragment:1.1.0-rc01")
diff --git a/asynclayoutinflater/build.gradle b/asynclayoutinflater/build.gradle
index 2c12378..3dcd2f0 100644
--- a/asynclayoutinflater/build.gradle
+++ b/asynclayoutinflater/build.gradle
@@ -9,7 +9,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
}
androidx {
diff --git a/benchmark/benchmark/build.gradle b/benchmark/benchmark/build.gradle
index 23f8633..b540dcd 100644
--- a/benchmark/benchmark/build.gradle
+++ b/benchmark/benchmark/build.gradle
@@ -19,6 +19,7 @@
id("AndroidXPlugin")
id("com.android.library")
id("kotlin-android")
+ id("androidx.benchmark")
}
dependencies {
diff --git a/benchmark/gradle-plugin/build.gradle b/benchmark/gradle-plugin/build.gradle
index 6ce036b..949c11a 100644
--- a/benchmark/gradle-plugin/build.gradle
+++ b/benchmark/gradle-plugin/build.gradle
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-
+import androidx.build.BuildServerConfigurationKt
import androidx.build.CompilationTarget
import androidx.build.LibraryGroups
import androidx.build.LibraryVersions
@@ -70,6 +70,17 @@
tasks["compileTestJava"].dependsOn generateSdkResource
+task buildOnServer(type: Copy) {
+ from {
+ def f = project.file("src/main/resources/scripts/lockClocks.sh")
+ if (!f.exists()) {
+ throw new GradleException(f.toString() + " does not exist")
+ }
+ return f
+ }
+ destinationDir BuildServerConfigurationKt.getDistributionDirectory(rootProject)
+}
+
gradlePlugin {
plugins {
benchmark {
diff --git a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt
index 7365bc8..1a57e4f9 100644
--- a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt
+++ b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt
@@ -88,11 +88,17 @@
}
}
- project.tasks.register("lockClocks", LockClocksTask::class.java).configure {
- it.adbPath.set(extension.adbExecutable.absolutePath)
+ if (project.rootProject.tasks.findByName("lockClocks") == null) {
+ project.rootProject.tasks.register("lockClocks", LockClocksTask::class.java).configure {
+ it.adbPath.set(extension.adbExecutable.absolutePath)
+ }
}
- project.tasks.register("unlockClocks", UnlockClocksTask::class.java).configure {
- it.adbPath.set(extension.adbExecutable.absolutePath)
+
+ if (project.rootProject.tasks.findByName("unlockClocks") == null) {
+ project.rootProject.tasks.register("unlockClocks", UnlockClocksTask::class.java)
+ .configure {
+ it.adbPath.set(extension.adbExecutable.absolutePath)
+ }
}
val extensionVariants = when (extension) {
diff --git a/benchmark/integration-tests/startup-benchmark/build.gradle b/benchmark/integration-tests/startup-benchmark/build.gradle
index 4219241..80085a3 100644
--- a/benchmark/integration-tests/startup-benchmark/build.gradle
+++ b/benchmark/integration-tests/startup-benchmark/build.gradle
@@ -20,6 +20,7 @@
id("AndroidXPlugin")
id("com.android.library")
id("kotlin-android")
+ id("androidx.benchmark")
}
dependencies {
diff --git a/biometric/build.gradle b/biometric/build.gradle
index e423137..e1dbcfe 100644
--- a/biometric/build.gradle
+++ b/biometric/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
api("androidx.appcompat:appcompat:1.1.0-rc01")
- api("androidx.core:core:1.1.0-rc02")
+ api("androidx.core:core:1.1.0")
api("androidx.fragment:fragment:1.1.0-rc01")
}
diff --git a/browser/build.gradle b/browser/build.gradle
index 06018de..5dd3789 100644
--- a/browser/build.gradle
+++ b/browser/build.gradle
@@ -17,7 +17,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
api("androidx.annotation:annotation:1.1.0")
implementation("androidx.collection:collection:1.1.0")
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 59996ce..c51fd30 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -58,11 +58,6 @@
}
}
-sourceSets {
- main.java.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/kotlin"
- main.resources.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/resources"
-}
-
dependencies {
implementation build_libs.gradle
implementation build_libs.error_prone_gradle
@@ -73,3 +68,18 @@
testImplementation "junit:junit:4.12"
}
+apply plugin: "java-gradle-plugin"
+
+sourceSets {
+ main.java.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/kotlin"
+ main.resources.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/resources"
+}
+
+gradlePlugin {
+ plugins {
+ benchmark {
+ id = 'androidx.benchmark'
+ implementationClass = 'androidx.benchmark.gradle.BenchmarkPlugin'
+ }
+ }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index 8ab32de..35fedcb 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -16,8 +16,6 @@
package androidx.build
-import androidx.benchmark.gradle.LockClocksTask
-import androidx.benchmark.gradle.UnlockClocksTask
import androidx.build.SupportConfig.BENCHMARK_INSTRUMENTATION_RUNNER
import androidx.build.SupportConfig.BUILD_TOOLS_VERSION
import androidx.build.SupportConfig.COMPILE_SDK_VERSION
@@ -277,6 +275,7 @@
"verifyDependencyVersions" == task.name ||
"reportLibraryMetrics" == task.name ||
CREATE_STUB_API_JAR_TASK == task.name ||
+ BUILD_ON_SERVER_TASK == task.name ||
("lintDebug" == task.name &&
!project.rootProject.hasProperty(USE_MAX_DEP_VERSIONS))) {
buildOnServerTask.dependsOn(task)
@@ -315,14 +314,6 @@
CheckSameVersionLibraryGroupsTask::class.java)
buildOnServerTask.dependsOn(checkSameVersionLibraryGroupsTask)
- val adbPath = "${getSdkPath(SupportConfig.getSupportRoot(project)).path}/platform-tools/adb"
- tasks.register("lockClocks", LockClocksTask::class.java).configure {
- it.adbPath.set(adbPath)
- }
- tasks.register("unlockClocks", UnlockClocksTask::class.java).configure {
- it.adbPath.set(adbPath)
- }
-
AffectedModuleDetector.configure(gradle, this)
// If useMaxDepVersions is set, iterate through all the project and substitute any androidx
diff --git a/buildSrc/src/main/kotlin/androidx/build/Jetify.kt b/buildSrc/src/main/kotlin/androidx/build/Jetify.kt
index 25b8e8b..ec84188 100644
--- a/buildSrc/src/main/kotlin/androidx/build/Jetify.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/Jetify.kt
@@ -30,6 +30,7 @@
"m2repository/androidx/arch/**",
"m2repository/androidx/arch/core/**",
"m2repository/androidx/asynclayoutinflater/**",
+ "m2repository/androidx/benchmark/**",
"m2repository/androidx/biometric/**",
"m2repository/androidx/browser/**",
"m2repository/androidx/camera/**",
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index b2af62a..9dacda3 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -58,7 +58,7 @@
val ENTERPRISE = Version("1.0.0-alpha03")
val EXIFINTERFACE = Version("1.1.0-beta02")
val FRAGMENT = Version("1.2.0-alpha02")
- val FUTURES = Version("1.0.0-beta02")
+ val FUTURES = Version("1.0.0-rc01")
val GRIDLAYOUT = Version("1.1.0-alpha01")
val HEIFWRITER = Version("1.1.0-alpha01")
val INSPECTION = Version("1.0.0-alpha01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index 014e196..6c5bd2c 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -127,7 +127,7 @@
prebuilts(LibraryGroups.WEBKIT, "1.1.0-alpha01")
ignore(LibraryGroups.WORK.group, "work-gcm")
ignore(LibraryGroups.WORK.group, "work-foreground")
- prebuilts(LibraryGroups.WORK, "2.2.0-beta02")
+ prebuilts(LibraryGroups.WORK, "2.2.0-rc01")
default(Ignore)
}
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 2a15464..b13de44 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -27,7 +27,7 @@
dependencies {
api(project(":camera:camera-core"))
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
implementation("androidx.annotation:annotation:1.0.0")
implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
implementation(GUAVA_LISTENABLE_FUTURE)
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index d792fc0..b111e0d 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -29,7 +29,7 @@
api("androidx.lifecycle:lifecycle-common:2.0.0")
implementation("androidx.exifinterface:exifinterface:1.0.0")
implementation("androidx.annotation:annotation:1.0.0")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
implementation(ARCH_LIFECYCLE_LIVEDATA)
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index 396ab3c..2be49b7 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -30,7 +30,7 @@
implementation("androidx.annotation:annotation:1.0.0")
implementation(GUAVA_LISTENABLE_FUTURE)
implementation(project(":camera:camera-core"))
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
implementation(JUNIT)
}
diff --git a/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/MainActivity.kt b/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/MainActivity.kt
index 63105dd..6dc6ca7 100644
--- a/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/MainActivity.kt
+++ b/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/MainActivity.kt
@@ -117,7 +117,7 @@
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
- camViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
+ camViewModel = ViewModelProvider(this)
.get(CamViewModel::class.java)
cameraParams = camViewModel.getCameraParams()
deviceInfo = DeviceInfo()
diff --git a/car/moderator/build.gradle b/car/moderator/build.gradle
index 4d19dbd..bd7370d 100644
--- a/car/moderator/build.gradle
+++ b/car/moderator/build.gradle
@@ -26,7 +26,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/compose/compose-runtime/compose-runtime-benchmark/build.gradle b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
index 07a7246..dcb32f6 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/build.gradle
+++ b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
@@ -24,6 +24,7 @@
id("com.android.library")
id("AndroidXUiPlugin")
id("kotlin-android")
+ id("androidx.benchmark")
}
android {
diff --git a/concurrent/futures/api/1.0.0-rc01.txt b/concurrent/futures/api/1.0.0-rc01.txt
new file mode 100644
index 0000000..beb76bd
--- /dev/null
+++ b/concurrent/futures/api/1.0.0-rc01.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.concurrent.futures {
+
+ public final class CallbackToFutureAdapter {
+ method public static <T> com.google.common.util.concurrent.ListenableFuture<T!> getFuture(androidx.concurrent.futures.CallbackToFutureAdapter.Resolver<T!>);
+ }
+
+ public static final class CallbackToFutureAdapter.Completer<T> {
+ method public void addCancellationListener(Runnable, java.util.concurrent.Executor);
+ method protected void finalize();
+ method public boolean set(T!);
+ method public boolean setCancelled();
+ method public boolean setException(Throwable);
+ }
+
+ public static interface CallbackToFutureAdapter.Resolver<T> {
+ method public Object? attachCompleter(androidx.concurrent.futures.CallbackToFutureAdapter.Completer<T!>) throws java.lang.Exception;
+ }
+
+}
+
diff --git a/concurrent/futures/api/restricted_1.0.0-rc01.txt b/concurrent/futures/api/restricted_1.0.0-rc01.txt
new file mode 100644
index 0000000..6dabf0b
--- /dev/null
+++ b/concurrent/futures/api/restricted_1.0.0-rc01.txt
@@ -0,0 +1,45 @@
+// Signature format: 3.0
+package androidx.concurrent.futures {
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class AbstractResolvableFuture<V> implements com.google.common.util.concurrent.ListenableFuture<V> {
+ ctor protected AbstractResolvableFuture();
+ method public final void addListener(Runnable!, java.util.concurrent.Executor!);
+ method protected void afterDone();
+ method public final boolean cancel(boolean);
+ method public final V! get(long, java.util.concurrent.TimeUnit!) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+ method public final V! get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+ method protected void interruptTask();
+ method public final boolean isCancelled();
+ method public final boolean isDone();
+ method protected String? pendingToString();
+ method protected boolean set(V?);
+ method protected boolean setException(Throwable!);
+ method protected boolean setFuture(com.google.common.util.concurrent.ListenableFuture<? extends V>!);
+ method protected final boolean wasInterrupted();
+ }
+
+ public final class CallbackToFutureAdapter {
+ method public static <T> com.google.common.util.concurrent.ListenableFuture<T!> getFuture(androidx.concurrent.futures.CallbackToFutureAdapter.Resolver<T!>);
+ }
+
+ public static final class CallbackToFutureAdapter.Completer<T> {
+ method public void addCancellationListener(Runnable, java.util.concurrent.Executor);
+ method protected void finalize();
+ method public boolean set(T!);
+ method public boolean setCancelled();
+ method public boolean setException(Throwable);
+ }
+
+ public static interface CallbackToFutureAdapter.Resolver<T> {
+ method public Object? attachCompleter(androidx.concurrent.futures.CallbackToFutureAdapter.Completer<T!>) throws java.lang.Exception;
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ResolvableFuture<V> extends androidx.concurrent.futures.AbstractResolvableFuture<V> {
+ method public static <V> androidx.concurrent.futures.ResolvableFuture<V!>! create();
+ method public boolean set(V?);
+ method public boolean setException(Throwable!);
+ method public boolean setFuture(com.google.common.util.concurrent.ListenableFuture<? extends V>!);
+ }
+
+}
+
diff --git a/content/build.gradle b/content/build.gradle
index cafb452..4a27186 100644
--- a/content/build.gradle
+++ b/content/build.gradle
@@ -26,7 +26,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(JUNIT)
diff --git a/coordinatorlayout/build.gradle b/coordinatorlayout/build.gradle
index ae2a733..6050e84 100644
--- a/coordinatorlayout/build.gradle
+++ b/coordinatorlayout/build.gradle
@@ -11,7 +11,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
// TODO: change to 1.1.0-alpha04 after release
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.0.0")
api("androidx.customview:customview:1.0.0")
diff --git a/core/core/api/restricted_1.2.0-alpha03.txt b/core/core/api/restricted_1.2.0-alpha03.txt
index 0693bb2..138fb9f 100644
--- a/core/core/api/restricted_1.2.0-alpha03.txt
+++ b/core/core/api/restricted_1.2.0-alpha03.txt
@@ -140,13 +140,13 @@
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component {
ctor public ComponentActivity();
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
+ method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
+ method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean superDispatchKeyEvent(android.view.KeyEvent!);
}
- @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
- ctor public ComponentActivity.ExtraData();
+ @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
+ ctor @Deprecated public ComponentActivity.ExtraData();
}
@RequiresApi(api=28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CoreComponentFactory extends android.app.AppComponentFactory {
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 0693bb2..138fb9f 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -140,13 +140,13 @@
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component {
ctor public ComponentActivity();
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
+ method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
+ method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean superDispatchKeyEvent(android.view.KeyEvent!);
}
- @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
- ctor public ComponentActivity.ExtraData();
+ @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
+ ctor @Deprecated public ComponentActivity.ExtraData();
}
@RequiresApi(api=28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CoreComponentFactory extends android.app.AppComponentFactory {
diff --git a/core/core/src/androidTest/java/androidx/core/app/ComponentActivityTest.java b/core/core/src/androidTest/java/androidx/core/app/ComponentActivityTest.java
index cda201b..98f553f 100644
--- a/core/core/src/androidTest/java/androidx/core/app/ComponentActivityTest.java
+++ b/core/core/src/androidTest/java/androidx/core/app/ComponentActivityTest.java
@@ -27,6 +27,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@SuppressWarnings("deprecation")
@RunWith(AndroidJUnit4.class)
@LargeTest
public class ComponentActivityTest extends BaseInstrumentationTestCase<TestComponentActivity> {
@@ -54,10 +55,10 @@
assertEquals(mTestExtraData, mComponentActivity.getExtraData(TestExtraData.class));
}
- public class NeverAddedExtraData extends ComponentActivity.ExtraData {
+ private class NeverAddedExtraData extends ComponentActivity.ExtraData {
}
- public class TestExtraData extends ComponentActivity.ExtraData {
+ private class TestExtraData extends ComponentActivity.ExtraData {
}
}
diff --git a/core/core/src/androidTest/java/androidx/core/os/TraceCompatTest.java b/core/core/src/androidTest/java/androidx/core/os/TraceCompatTest.java
index d36b79a..5f229c4 100644
--- a/core/core/src/androidTest/java/androidx/core/os/TraceCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/os/TraceCompatTest.java
@@ -21,15 +21,18 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import android.app.UiAutomation;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.LargeTest;
import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -50,12 +53,23 @@
mByteArrayOutputStream = new ByteArrayOutputStream();
}
+ @After
+ public void stopAtrace() throws IOException {
+ // Since API 23, 'async_stop' will work. On lower API levels it was broken (see aosp/157142)
+ if (Build.VERSION.SDK_INT >= 23) {
+ executeCommand("atrace --async_stop");
+ } else {
+ // Ensure tracing is not currently running by performing a short synchronous trace.
+ executeCommand("atrace -t 0");
+ }
+ }
+
@Test
public void beginAndEndSection() throws IOException {
startTrace();
TraceCompat.beginSection("beginAndEndSection");
TraceCompat.endSection();
- endTrace();
+ dumpTrace();
assertTraceContains("tracing_mark_write:\\ B\\|.*\\|beginAndEndSection");
assertTraceContains("tracing_mark_write:\\ E");
@@ -66,7 +80,7 @@
startTrace();
TraceCompat.beginAsyncSection("beginAndEndSectionAsync", /*cookie=*/5099);
TraceCompat.endAsyncSection("beginAndEndSectionAsync", /*cookie=*/5099);
- endTrace();
+ dumpTrace();
assertTraceContains("tracing_mark_write:\\ S\\|.*\\|beginAndEndSectionAsync\\|5099");
assertTraceContains("tracing_mark_write:\\ F\\|.*\\|beginAndEndSectionAsync\\|5099");
@@ -78,7 +92,7 @@
TraceCompat.setCounter("counterName", 42);
TraceCompat.setCounter("counterName", 47);
TraceCompat.setCounter("counterName", 9787);
- endTrace();
+ dumpTrace();
assertTraceContains("tracing_mark_write:\\ C\\|.*\\|counterName\\|42");
assertTraceContains("tracing_mark_write:\\ C\\|.*\\|counterName\\|47");
@@ -89,7 +103,7 @@
public void isEnabledDuringTrace() throws IOException {
startTrace();
boolean enabled = TraceCompat.isEnabled();
- endTrace();
+ dumpTrace();
assertThat(enabled).isTrue();
}
@@ -101,39 +115,45 @@
}
private void startTrace() throws IOException {
- UiAutomation automation = InstrumentationRegistry.getInstrumentation()
- .getUiAutomation();
String processName =
ApplicationProvider.getApplicationContext().getApplicationInfo().processName;
// Write the "async_start" status to the byte array to ensure atrace has fully started
// before issuing any trace commands. This will also capture any errors that occur during
// start so they can be added to the assertion error's message.
- writeDataToByteStream(automation.executeShellCommand(
- String.format("atrace --async_start -b %d -a %s", TRACE_BUFFER_SIZE,
- processName)),
+ executeCommand(
+ String.format("atrace --async_start -b %d -a %s", TRACE_BUFFER_SIZE, processName));
+ }
+
+ private void dumpTrace() throws IOException {
+ // On older versions of atrace, the -b option is required when dumping the trace so the
+ // trace buffer doesn't get cleared before being dumped.
+ executeCommand(
+ String.format("atrace --async_dump -b %d", TRACE_BUFFER_SIZE),
mByteArrayOutputStream);
}
- private void endTrace() throws IOException {
+ private static void executeCommand(@NonNull String command) throws IOException {
+ executeCommand(command, null);
+ }
+
+ private static void executeCommand(@NonNull String command,
+ @Nullable ByteArrayOutputStream outputStream) throws IOException {
UiAutomation automation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- writeDataToByteStream(automation.executeShellCommand("atrace --async_stop"),
- mByteArrayOutputStream);
- }
- private void writeDataToByteStream(ParcelFileDescriptor pfDescriptor,
- ByteArrayOutputStream outputStream) throws IOException {
- try (ParcelFileDescriptor.AutoCloseInputStream inputStream =
+ try (ParcelFileDescriptor pfDescriptor = automation.executeShellCommand(command);
+ ParcelFileDescriptor.AutoCloseInputStream inputStream =
new ParcelFileDescriptor.AutoCloseInputStream(
pfDescriptor)) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) >= 0) {
- outputStream.write(buffer, 0, length);
+ if (outputStream != null) {
+ outputStream.write(buffer, 0, length);
+ }
}
}
-
}
private void assertTraceContains(@NonNull String contentRegex) {
diff --git a/core/core/src/main/java/androidx/core/app/ComponentActivity.java b/core/core/src/main/java/androidx/core/app/ComponentActivity.java
index 9672a78..90d61c4 100644
--- a/core/core/src/main/java/androidx/core/app/ComponentActivity.java
+++ b/core/core/src/main/java/androidx/core/app/ComponentActivity.java
@@ -43,6 +43,7 @@
*
* <p>Note that these objects are not retained across configuration changes</p>
*/
+ @SuppressWarnings("deprecation")
private SimpleArrayMap<Class<? extends ExtraData>, ExtraData> mExtraDataMap =
new SimpleArrayMap<>();
@@ -54,8 +55,11 @@
*
* @see #getExtraData
* @hide
+ * @deprecated Use {@link View#setTag(int, Object)} with the window's decor view.
*/
+ @SuppressWarnings("deprecation")
@RestrictTo(LIBRARY_GROUP_PREFIX)
+ @Deprecated
public void putExtraData(ExtraData extraData) {
mExtraDataMap.put(extraData.getClass(), extraData);
}
@@ -65,9 +69,11 @@
*
* @see #putExtraData
* @hide
+ * @deprecated Use {@link View#getTag(int)} with the window's decor view.
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "deprecation"})
+ @Deprecated
public <T extends ExtraData> T getExtraData(Class<T> extraDataClass) {
return (T) mExtraDataMap.get(extraDataClass);
}
@@ -101,8 +107,12 @@
/**
* @hide
+ * @deprecated Store the object you want to save directly by using
+ * {@link View#setTag(int, Object)} with the window's decor view.
*/
+ @SuppressWarnings("DeprecatedIsStillUsed")
@RestrictTo(LIBRARY_GROUP_PREFIX)
+ @Deprecated
public static class ExtraData {
}
}
diff --git a/dynamic-animation/build.gradle b/dynamic-animation/build.gradle
index fe7bd58..6ad3e31 100644
--- a/dynamic-animation/build.gradle
+++ b/dynamic-animation/build.gradle
@@ -9,7 +9,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
index 0b811cb..4e13514 100644
--- a/emoji/core/build.gradle
+++ b/emoji/core/build.gradle
@@ -22,7 +22,7 @@
// treats this as local jar and package it inside the aar.
api files(configurations.repackage)
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation(project(':collection:collection'))
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt
index f67b19a..b9bf7b5 100644
--- a/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt
+++ b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt
@@ -20,7 +20,6 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelLazy
import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
import androidx.lifecycle.ViewModelProvider.Factory
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
@@ -61,7 +60,9 @@
/**
* Returns a property delegate to access parent activity's [ViewModel],
* if [factoryProducer] is specified then [ViewModelProvider.Factory]
- * returned by it will be used to create [ViewModel] first time.
+ * returned by it will be used to create [ViewModel] first time. Otherwise, the activity's
+ * [androidx.activity.ComponentActivity.getDefaultViewModelProviderFactory](default factory)
+ * will be used.
*
* ```
* class MyFragment : Fragment() {
@@ -75,7 +76,8 @@
@MainThread
inline fun <reified VM : ViewModel> Fragment.activityViewModels(
noinline factoryProducer: (() -> Factory)? = null
-) = createViewModelLazy(VM::class, { requireActivity().viewModelStore }, factoryProducer)
+) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
+ factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })
/**
* Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
@@ -88,10 +90,7 @@
factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
- val application = activity?.application ?: throw IllegalStateException(
- "ViewModel can be accessed only when Fragment is attached"
- )
- AndroidViewModelFactory.getInstance(application)
+ defaultViewModelProviderFactory
}
return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
-}
+}
\ No newline at end of file
diff --git a/fragment/fragment/api/1.2.0-alpha02.txt b/fragment/fragment/api/1.2.0-alpha02.txt
index 346d9b7..672cb24 100644
--- a/fragment/fragment/api/1.2.0-alpha02.txt
+++ b/fragment/fragment/api/1.2.0-alpha02.txt
@@ -25,7 +25,7 @@
field public static final int STYLE_NO_TITLE = 1; // 0x1
}
- public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+ public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
ctor public Fragment();
ctor @ContentView public Fragment(@LayoutRes int);
method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
@@ -36,6 +36,7 @@
method public final android.os.Bundle? getArguments();
method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
method public android.content.Context? getContext();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
method public Object? getEnterTransition();
method public Object? getExitTransition();
method public final androidx.fragment.app.FragmentManager? getFragmentManager();
diff --git a/fragment/fragment/api/current.txt b/fragment/fragment/api/current.txt
index 346d9b7..672cb24 100644
--- a/fragment/fragment/api/current.txt
+++ b/fragment/fragment/api/current.txt
@@ -25,7 +25,7 @@
field public static final int STYLE_NO_TITLE = 1; // 0x1
}
- public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+ public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
ctor public Fragment();
ctor @ContentView public Fragment(@LayoutRes int);
method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
@@ -36,6 +36,7 @@
method public final android.os.Bundle? getArguments();
method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
method public android.content.Context? getContext();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
method public Object? getEnterTransition();
method public Object? getExitTransition();
method public final androidx.fragment.app.FragmentManager? getFragmentManager();
diff --git a/fragment/fragment/api/restricted_1.2.0-alpha02.txt b/fragment/fragment/api/restricted_1.2.0-alpha02.txt
index 7b475f2..b1f0d5f 100644
--- a/fragment/fragment/api/restricted_1.2.0-alpha02.txt
+++ b/fragment/fragment/api/restricted_1.2.0-alpha02.txt
@@ -26,7 +26,7 @@
field public static final int STYLE_NO_TITLE = 1; // 0x1
}
- public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+ public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
ctor public Fragment();
ctor @ContentView public Fragment(@LayoutRes int);
method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
@@ -37,6 +37,7 @@
method public final android.os.Bundle? getArguments();
method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
method public android.content.Context? getContext();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
method public Object? getEnterTransition();
method public Object? getExitTransition();
method public final androidx.fragment.app.FragmentManager? getFragmentManager();
diff --git a/fragment/fragment/api/restricted_current.txt b/fragment/fragment/api/restricted_current.txt
index 7b475f2..b1f0d5f 100644
--- a/fragment/fragment/api/restricted_current.txt
+++ b/fragment/fragment/api/restricted_current.txt
@@ -26,7 +26,7 @@
field public static final int STYLE_NO_TITLE = 1; // 0x1
}
- public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+ public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
ctor public Fragment();
ctor @ContentView public Fragment(@LayoutRes int);
method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
@@ -37,6 +37,7 @@
method public final android.os.Bundle? getArguments();
method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
method public android.content.Context? getContext();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
method public Object? getEnterTransition();
method public Object? getExitTransition();
method public final androidx.fragment.app.FragmentManager? getFragmentManager();
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index c199145..777cb59 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -18,13 +18,14 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
api("androidx.collection:collection:1.1.0")
api("androidx.viewpager:viewpager:1.0.0")
api("androidx.loader:loader:1.0.0")
api(project(":activity:activity"))
api(project(":lifecycle:lifecycle-livedata-core"))
api(project(":lifecycle:lifecycle-viewmodel"))
+ api(project(":lifecycle:lifecycle-viewmodel-savedstate"))
androidTestImplementation(KOTLIN_STDLIB)
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt
index eabe5c4..021ea3c 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt
@@ -35,6 +35,7 @@
import androidx.testutils.waitForExecution
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Assert.fail
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -46,10 +47,12 @@
class FragmentContainerViewTest {
@get:Rule
var activityRule = ActivityTestRule(FragmentTestActivity::class.java)
+ lateinit var context: Context
@Before
fun setupContainer() {
activityRule.setContentView(R.layout.fragment_container_view)
+ context = activityRule.activity.applicationContext
}
@Test
@@ -99,8 +102,6 @@
@SdkSuppress(minSdkVersion = 29) // WindowInsets.Builder requires API 29
@Test
fun windowInsetsDispatchToChildren() {
- val context = activityRule.activity.applicationContext
-
val parentView = FragmentContainerView(context)
val childView = FragmentContainerView(context)
@@ -118,6 +119,8 @@
insets
}
+ childView.setTag(R.id.fragment_container_view_tag, Fragment())
+
parentView.addView(childView)
parentView.dispatchApplyWindowInsets(sentInsets)
@@ -125,18 +128,63 @@
}
@Test
- fun removeViewAt() {
- val context = activityRule.activity.applicationContext
- val view = FragmentContainerView(context)
+ fun addView() {
+ val fm = activityRule.activity.supportFragmentManager
- val childView1 = FragmentContainerView(context)
+ val view = View(context)
+ val fragment = Fragment()
+ fragment.mView = view
+
+ fm.setViewTag(fragment)
+
+ val fragmentContainerView = FragmentContainerView(context)
+
+ assertWithMessage("FragmentContainerView should have no child views")
+ .that(fragmentContainerView.childCount).isEqualTo(0)
+
+ fragmentContainerView.addView(view)
+
+ assertWithMessage("FragmentContainerView should have one child view")
+ .that(fragmentContainerView.childCount).isEqualTo(1)
+ }
+
+ @Test
+ fun addViewNotAssociatedWithFragment() {
+ val view = View(context)
+
+ try {
+ FragmentContainerView(context).addView(view, 0, null)
+ fail("View without a Fragment added to FragmentContainerView should throw an exception")
+ } catch (e: IllegalStateException) {
+ assertThat(e)
+ .hasMessageThat().contains(
+ "Views added to a FragmentContainerView must be associated with a Fragment. " +
+ "View " + view + " is not associated with a Fragment."
+ )
+ }
+ }
+
+ @Test
+ fun addViewInLayoutNotAssociatedWithFragment() {
+ val view = View(context)
+
+ try {
+ FragmentContainerView(context).addViewInLayout(view, 0, null, false)
+ fail("View without a Fragment added to FragmentContainerView should throw an exception")
+ } catch (e: IllegalStateException) {
+ assertThat(e)
+ .hasMessageThat().contains(
+ "Views added to a FragmentContainerView must be associated with a Fragment. " +
+ "View " + view + " is not associated with a Fragment."
+ )
+ }
+ }
+
+ @Test
+ fun removeViewAt() {
val childView2 = FragmentContainerView(context)
- view.addView(childView1)
- view.addView(childView2)
-
- assertThat(view.childCount).isEqualTo(2)
- assertThat(view.getChildAt(1)).isEqualTo(childView2)
+ val view = setupRemoveTestsView(FragmentContainerView(context), childView2)
view.removeViewAt(0)
@@ -146,17 +194,10 @@
@Test
fun removeViewInLayout() {
- val context = activityRule.activity.applicationContext
- val view = FragmentContainerView(context)
-
val childView1 = FragmentContainerView(context)
val childView2 = FragmentContainerView(context)
- view.addView(childView1)
- view.addView(childView2)
-
- assertThat(view.childCount).isEqualTo(2)
- assertThat(view.getChildAt(1)).isEqualTo(childView2)
+ val view = setupRemoveTestsView(childView1, childView2)
view.removeViewInLayout(childView1)
@@ -166,17 +207,10 @@
@Test
fun removeView() {
- val context = activityRule.activity.applicationContext
- val view = FragmentContainerView(context)
-
val childView1 = FragmentContainerView(context)
val childView2 = FragmentContainerView(context)
- view.addView(childView1)
- view.addView(childView2)
-
- assertThat(view.childCount).isEqualTo(2)
- assertThat(view.getChildAt(1)).isEqualTo(childView2)
+ val view = setupRemoveTestsView(childView1, childView2)
view.removeView(childView1)
@@ -185,17 +219,10 @@
@Test
fun removeViews() {
- val context = activityRule.activity.applicationContext
- val view = FragmentContainerView(context)
-
- val childView1 = FragmentContainerView(context)
- val childView2 = FragmentContainerView(context)
-
- view.addView(childView1)
- view.addView(childView2)
-
- assertThat(view.childCount).isEqualTo(2)
- assertThat(view.getChildAt(1)).isEqualTo(childView2)
+ val view = setupRemoveTestsView(
+ FragmentContainerView(context),
+ FragmentContainerView(context)
+ )
view.removeViews(1, 1)
@@ -204,17 +231,10 @@
@Test
fun removeViewsInLayout() {
- val context = activityRule.activity.applicationContext
- val view = FragmentContainerView(context)
-
- val childView1 = FragmentContainerView(context)
- val childView2 = FragmentContainerView(context)
-
- view.addView(childView1)
- view.addView(childView2)
-
- assertThat(view.childCount).isEqualTo(2)
- assertThat(view.getChildAt(1)).isEqualTo(childView2)
+ val view = setupRemoveTestsView(
+ FragmentContainerView(context),
+ FragmentContainerView(context)
+ )
view.removeViewsInLayout(1, 1)
@@ -223,17 +243,10 @@
@Test
fun removeAllViewsInLayout() {
- val context = activityRule.activity.applicationContext
- val view = FragmentContainerView(context)
-
- val childView1 = FragmentContainerView(context)
- val childView2 = FragmentContainerView(context)
-
- view.addView(childView1)
- view.addView(childView2)
-
- assertThat(view.childCount).isEqualTo(2)
- assertThat(view.getChildAt(1)).isEqualTo(childView2)
+ val view = setupRemoveTestsView(
+ FragmentContainerView(context),
+ FragmentContainerView(context)
+ )
view.removeAllViewsInLayout()
@@ -243,22 +256,37 @@
// removeDetachedView should not actually remove the view
@Test
fun removeDetachedView() {
- val context = activityRule.activity.applicationContext
- val view = FragmentContainerView(context)
-
val childView1 = FragmentContainerView(context)
val childView2 = FragmentContainerView(context)
+ val view = setupRemoveTestsView(childView1, childView2)
+
+ view.removeDetachedView(childView1, false)
+
+ assertThat(view.childCount).isEqualTo(2)
+ assertThat(view.getChildAt(1)).isEqualTo(childView2)
+ }
+
+ private fun setupRemoveTestsView(
+ childView1: FragmentContainerView,
+ childView2: FragmentContainerView
+ ): FragmentContainerView {
+ val view = FragmentContainerView(context)
+ val fragment1 = Fragment()
+ val fragment2 = Fragment()
+
+ fragment1.mView = childView1
+ fragment2.mView = childView2
+
+ childView1.setTag(R.id.fragment_container_view_tag, fragment1)
+ childView2.setTag(R.id.fragment_container_view_tag, fragment2)
+
view.addView(childView1)
view.addView(childView2)
assertThat(view.childCount).isEqualTo(2)
assertThat(view.getChildAt(1)).isEqualTo(childView2)
-
- view.removeDetachedView(childView1, false)
-
- assertThat(view.childCount).isEqualTo(2)
- assertThat(view.getChildAt(1)).isEqualTo(childView2)
+ return view
}
// Disappearing child views should be drawn first before other child views.
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt
index 28e73e2..3edf294 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt
@@ -108,20 +108,32 @@
val fragmentModel = withActivity {
getFragment(ViewModelActivity.FRAGMENT_TAG_1).fragmentModel
}
+ val fragmentAndroidModel = withActivity {
+ getFragment(ViewModelActivity.FRAGMENT_TAG_1).androidModel
+ }
+ val fragmentSavedStateAndroidModel = withActivity {
+ getFragment(ViewModelActivity.FRAGMENT_TAG_1).savedStateModel
+ }
val backStackFragmentModel = withActivity {
getFragment(ViewModelActivity.FRAGMENT_TAG_BACK_STACK).fragmentModel
}
assertThat(fragmentModel.cleared).isFalse()
+ assertThat(fragmentAndroidModel.cleared).isFalse()
+ assertThat(fragmentSavedStateAndroidModel.cleared).isFalse()
assertThat(backStackFragmentModel.cleared).isFalse()
recreate()
// recreate shouldn't clear the ViewModels
assertThat(fragmentModel.cleared).isFalse()
+ assertThat(fragmentAndroidModel.cleared).isFalse()
+ assertThat(fragmentSavedStateAndroidModel.cleared).isFalse()
assertThat(backStackFragmentModel.cleared).isFalse()
moveToState(Lifecycle.State.DESTROYED)
// But destroying the Activity should
assertThat(fragmentModel.cleared).isTrue()
+ assertThat(fragmentAndroidModel.cleared).isTrue()
+ assertThat(fragmentSavedStateAndroidModel.cleared).isTrue()
assertThat(backStackFragmentModel.cleared).isTrue()
}
}
@@ -134,10 +146,7 @@
supportFragmentManager.beginTransaction().add(it, "temp").commitNow()
}
}
- val viewModelProvider = ViewModelProvider(
- fragment,
- ViewModelProvider.NewInstanceFactory()
- )
+ val viewModelProvider = ViewModelProvider(fragment)
val vm = viewModelProvider.get(TestViewModel::class.java)
assertThat(vm.cleared).isFalse()
onActivity { activity ->
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTestInTransaction.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTestInTransaction.kt
index 03804cc..f281002 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTestInTransaction.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTestInTransaction.kt
@@ -20,7 +20,6 @@
import androidx.fragment.app.test.EmptyFragmentTestActivity
import androidx.fragment.app.test.TestViewModel
import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelStoreOwner
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -43,7 +42,7 @@
val activity = activityRule.activity
val fragment = TestFragment()
activity.supportFragmentManager.beginTransaction().add(fragment, "tag").commitNow()
- val viewModelProvider = ViewModelProvider(activity, ViewModelProvider.NewInstanceFactory())
+ val viewModelProvider = ViewModelProvider(activity)
val viewModel = viewModelProvider.get(TestViewModel::class.java)
assertThat(viewModel).isSameInstanceAs(fragment.viewModel)
}
@@ -65,7 +64,7 @@
super.onCreate(savedInstanceState)
val fragment = TestFragment()
childFragmentManager.beginTransaction().add(fragment, "tag").commitNow()
- val viewModelProvider = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
+ val viewModelProvider = ViewModelProvider(this)
val viewModel = viewModelProvider.get(TestViewModel::class.java)
assertThat(viewModel).isSameInstanceAs(fragment.viewModel)
executed = true
@@ -79,10 +78,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val parentFragment = parentFragment
- val provider = ViewModelProvider(
- (parentFragment ?: requireActivity()) as ViewModelStoreOwner,
- ViewModelProvider.NewInstanceFactory()
- )
+ val provider = ViewModelProvider(parentFragment ?: requireActivity())
viewModel = provider.get(TestViewModel::class.java)
assertThat(viewModel).isNotNull()
}
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/test/ViewModelActivity.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/test/ViewModelActivity.kt
index a058649..b63f54d 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/test/ViewModelActivity.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/test/ViewModelActivity.kt
@@ -16,10 +16,14 @@
package androidx.fragment.app.test
+import android.app.Application
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.test.R
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStore
@@ -48,10 +52,7 @@
.commit()
}
- val viewModelProvider = ViewModelProvider(
- this,
- ViewModelProvider.NewInstanceFactory()
- )
+ val viewModelProvider = ViewModelProvider(this)
activityModel = viewModelProvider.get(KEY_ACTIVITY_MODEL, TestViewModel::class.java)
defaultActivityModel = viewModelProvider.get(TestViewModel::class.java)
}
@@ -60,26 +61,41 @@
lateinit var fragmentModel: TestViewModel
lateinit var activityModel: TestViewModel
lateinit var defaultActivityModel: TestViewModel
+ lateinit var androidModel: TestAndroidViewModel
+ lateinit var savedStateModel: TestSavedStateViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val viewModelProvider = ViewModelProvider(
- this,
- ViewModelProvider.NewInstanceFactory()
- )
+ val viewModelProvider = ViewModelProvider(this)
fragmentModel = viewModelProvider.get(
KEY_FRAGMENT_MODEL,
TestViewModel::class.java
)
- val activityViewModelProvider = ViewModelProvider(
- requireActivity(),
- ViewModelProvider.NewInstanceFactory()
- )
+ val activityViewModelProvider = ViewModelProvider(requireActivity())
activityModel = activityViewModelProvider.get(
ViewModelActivity.KEY_ACTIVITY_MODEL,
TestViewModel::class.java
)
defaultActivityModel = activityViewModelProvider.get(TestViewModel::class.java)
+ androidModel = viewModelProvider.get(TestAndroidViewModel::class.java)
+ savedStateModel = viewModelProvider.get(TestSavedStateViewModel::class.java)
+ }
+ }
+
+ class TestAndroidViewModel(application: Application) : AndroidViewModel(application) {
+ var cleared = false
+
+ override fun onCleared() {
+ cleared = true
+ }
+ }
+
+ @Suppress("unused")
+ class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel() {
+ var cleared = false
+
+ override fun onCleared() {
+ cleared = true
}
}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
index f6efea6..4dc499c 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -59,12 +59,15 @@
import androidx.core.app.SharedElementCallback;
import androidx.core.util.DebugUtils;
import androidx.core.view.LayoutInflaterCompat;
+import androidx.lifecycle.HasDefaultViewModelProviderFactory;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.SavedStateViewModelFactory;
+import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.loader.app.LoaderManager;
@@ -94,7 +97,7 @@
*
*/
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
- ViewModelStoreOwner, SavedStateRegistryOwner {
+ ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner {
static final Object USE_DEFAULT_TRANSITION = new Object();
@@ -267,6 +270,8 @@
@Nullable FragmentViewLifecycleOwner mViewLifecycleOwner;
MutableLiveData<LifecycleOwner> mViewLifecycleOwnerLiveData = new MutableLiveData<>();
+ private ViewModelProvider.Factory mDefaultFactory;
+
SavedStateRegistryController mSavedStateRegistryController;
@LayoutRes
@@ -365,6 +370,28 @@
return mFragmentManager.getViewModelStore(this);
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The {@link #getArguments() Fragment's arguments} when this is first called will be used
+ * as the defaults to any {@link androidx.lifecycle.SavedStateHandle} passed to a view model
+ * created using this factory.</p>
+ */
+ @NonNull
+ @Override
+ public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+ if (mFragmentManager == null) {
+ throw new IllegalStateException("Can't access ViewModels from detached fragment");
+ }
+ if (mDefaultFactory == null) {
+ mDefaultFactory = new SavedStateViewModelFactory(
+ requireActivity().getApplication(),
+ this,
+ getArguments());
+ }
+ return mDefaultFactory;
+ }
+
@NonNull
@Override
public final SavedStateRegistry getSavedStateRegistry() {
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java
index f59c067..898a17c 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java
@@ -19,8 +19,11 @@
import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Canvas;
+import android.os.Bundle;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.FrameLayout;
@@ -50,6 +53,10 @@
* <p>FragmentContainerView should not be used as a replacement for other ViewGroups (FrameLayout,
* LinearLayout, etc) outside of Fragment use cases.
*
+ * <p>FragmentContainerView will only allow views to returned by a Fragment's
+ * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}. Attempting to add any other
+ * view will result in an {@link IllegalStateException}.
+ *
* <p>Layout animations and transitions are disabled for FragmentContainerView. Animations should be
* done through {@link FragmentTransaction#setCustomAnimations(int, int, int, int)}. If
* animateLayoutChanges is set to <code>true</code> or
@@ -159,6 +166,41 @@
super.endViewTransition(view);
}
+ /**
+ * <p>FragmentContainerView will only allow views to returned by a Fragment's
+ * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}. Attempting to add any
+ * other view will result in an {@link IllegalStateException}.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public void addView(@NonNull View child, int index, @Nullable ViewGroup.LayoutParams params) {
+ if (FragmentManager.getViewFragment(child) == null) {
+ throw new IllegalStateException("Views added to a FragmentContainerView must be"
+ + " associated with a Fragment. View " + child + " is not associated with a"
+ + " Fragment.");
+ }
+ super.addView(child, index, params);
+ }
+
+ /**
+ * <p>FragmentContainerView will only allow views to returned by a Fragment's
+ * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}. Attempting to add any
+ * other view will result in an {@link IllegalStateException}.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean addViewInLayout(@NonNull View child, int index,
+ @Nullable ViewGroup.LayoutParams params, boolean preventRequestLayout) {
+ if (FragmentManager.getViewFragment(child) == null) {
+ throw new IllegalStateException("Views added to a FragmentContainerView must be"
+ + " associated with a Fragment. View " + child + " is not associated with a"
+ + " Fragment.");
+ }
+ return super.addViewInLayout(child, index, params, preventRequestLayout);
+ }
+
@Override
public void removeViewAt(int index) {
View view = getChildAt(index);
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index c1354c8..d376369 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -780,11 +780,11 @@
* @return the locally scoped {@link Fragment} to the given view, if found
*/
@Nullable
- static Fragment findViewFragment(@NonNull View view) {
+ private static Fragment findViewFragment(@NonNull View view) {
while (view != null) {
- Object tag = view.getTag(R.id.fragment_container_view_tag);
- if (tag instanceof Fragment) {
- return (Fragment) tag;
+ Fragment fragment = getViewFragment(view);
+ if (fragment != null) {
+ return fragment;
}
ViewParent parent = view.getParent();
view = parent instanceof View ? (View) parent : null;
@@ -793,6 +793,20 @@
}
/**
+ * Check if this view has an associated Fragment
+ * @param view the view to search from
+ * @return the locally scoped {@link Fragment} to the given view, if found
+ */
+ @Nullable
+ static Fragment getViewFragment(@NonNull View view) {
+ Object tag = view.getTag(R.id.fragment_container_view_tag);
+ if (tag instanceof Fragment) {
+ return (Fragment) tag;
+ }
+ return null;
+ }
+
+ /**
* Used to store the Fragment inside of its view's tag. This is done after the fragment's view
* is created, but before the view is added to the container.
*
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index a3e453a..f5e1ba4 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/gridlayout/build.gradle b/gridlayout/build.gradle
index efb7375..58ab795 100644
--- a/gridlayout/build.gradle
+++ b/gridlayout/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api(ANDROIDX_ANNOTATION)
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/jetifier/jetifier/migration.config b/jetifier/jetifier/migration.config
index 03e7d2a..16ac67b 100644
--- a/jetifier/jetifier/migration.config
+++ b/jetifier/jetifier/migration.config
@@ -614,6 +614,10 @@
"to": "ignore"
},
{
+ "from": "androidx/benchmark/(.*)",
+ "to": "ignore"
+ },
+ {
"from": "androidx/camera/(.*)",
"to": "ignore"
},
@@ -972,6 +976,10 @@
"to": "androidx/sharetarget"
},
{
+ "from": "androidx/benchmark",
+ "to": "androidx/benchmark"
+ },
+ {
"from": "androidx/camera",
"to": "androidx/camera"
},
@@ -3023,6 +3031,30 @@
},
{
"from": {
+ "groupId": "androidx.benchmark",
+ "artifactId": "benchmark-common",
+ "version": "{newBenchmarkVersion}"
+ },
+ "to": {
+ "groupId": "androidx.benchmark",
+ "artifactId": "benchmark-common",
+ "version": "{newBenchmarkVersion}"
+ }
+ },
+ {
+ "from": {
+ "groupId": "androidx.benchmark",
+ "artifactId": "benchmark-junit4",
+ "version": "{newBenchmarkVersion}"
+ },
+ "to": {
+ "groupId": "androidx.benchmark",
+ "artifactId": "benchmark-junit4",
+ "version": "{newBenchmarkVersion}"
+ }
+ },
+ {
+ "from": {
"groupId": "androidx.camera",
"artifactId": "camera-core",
"version": "{newCameraVersion}"
@@ -3133,6 +3165,7 @@
"newBiometricVersion": "1.0.0-alpha03",
"newDataBindingVersion": "undefined",
"newWorkManagerVersion": "2.0.0",
+ "newBenchmarkVersion": "1.0.0-alpha04",
"newCameraVersion": "1.0.0-alpha01"
}
},
diff --git a/leanback/build.gradle b/leanback/build.gradle
index 7588656..df32fcf 100644
--- a/leanback/build.gradle
+++ b/leanback/build.gradle
@@ -11,7 +11,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
api("androidx.interpolator:interpolator:1.0.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.0.0")
api("androidx.media:media:1.0.0")
api("androidx.fragment:fragment:1.0.0")
diff --git a/legacy/core-utils/build.gradle b/legacy/core-utils/build.gradle
index 7a331a9..c4fed24 100644
--- a/legacy/core-utils/build.gradle
+++ b/legacy/core-utils/build.gradle
@@ -9,7 +9,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
api(project(":documentfile"))
api(project(":loader:loader"))
api(project(":localbroadcastmanager"))
diff --git a/lifecycle/lifecycle-extensions/api/2.2.0-alpha03.txt b/lifecycle/lifecycle-extensions/api/2.2.0-alpha03.txt
index c85f1ab..273ffbf 100644
--- a/lifecycle/lifecycle-extensions/api/2.2.0-alpha03.txt
+++ b/lifecycle/lifecycle-extensions/api/2.2.0-alpha03.txt
@@ -1,12 +1,12 @@
// Signature format: 3.0
package androidx.lifecycle {
- public class ViewModelProviders {
+ @Deprecated public class ViewModelProviders {
ctor @Deprecated public ViewModelProviders();
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
}
@Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
diff --git a/lifecycle/lifecycle-extensions/api/current.txt b/lifecycle/lifecycle-extensions/api/current.txt
index c85f1ab..273ffbf 100644
--- a/lifecycle/lifecycle-extensions/api/current.txt
+++ b/lifecycle/lifecycle-extensions/api/current.txt
@@ -1,12 +1,12 @@
// Signature format: 3.0
package androidx.lifecycle {
- public class ViewModelProviders {
+ @Deprecated public class ViewModelProviders {
ctor @Deprecated public ViewModelProviders();
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
}
@Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
diff --git a/lifecycle/lifecycle-extensions/api/restricted_2.2.0-alpha03.txt b/lifecycle/lifecycle-extensions/api/restricted_2.2.0-alpha03.txt
index c85f1ab..273ffbf 100644
--- a/lifecycle/lifecycle-extensions/api/restricted_2.2.0-alpha03.txt
+++ b/lifecycle/lifecycle-extensions/api/restricted_2.2.0-alpha03.txt
@@ -1,12 +1,12 @@
// Signature format: 3.0
package androidx.lifecycle {
- public class ViewModelProviders {
+ @Deprecated public class ViewModelProviders {
ctor @Deprecated public ViewModelProviders();
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
}
@Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
diff --git a/lifecycle/lifecycle-extensions/api/restricted_current.txt b/lifecycle/lifecycle-extensions/api/restricted_current.txt
index c85f1ab..273ffbf 100644
--- a/lifecycle/lifecycle-extensions/api/restricted_current.txt
+++ b/lifecycle/lifecycle-extensions/api/restricted_current.txt
@@ -1,12 +1,12 @@
// Signature format: 3.0
package androidx.lifecycle {
- public class ViewModelProviders {
+ @Deprecated public class ViewModelProviders {
ctor @Deprecated public ViewModelProviders();
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
- method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
}
@Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
diff --git a/lifecycle/lifecycle-extensions/src/androidTest/java/androidx/lifecycle/ViewModelProvidersFragmentTest.kt b/lifecycle/lifecycle-extensions/src/androidTest/java/androidx/lifecycle/ViewModelProvidersFragmentTest.kt
index 696c0c9..9cfd0c9 100644
--- a/lifecycle/lifecycle-extensions/src/androidTest/java/androidx/lifecycle/ViewModelProvidersFragmentTest.kt
+++ b/lifecycle/lifecycle-extensions/src/androidTest/java/androidx/lifecycle/ViewModelProvidersFragmentTest.kt
@@ -25,6 +25,7 @@
import org.junit.Test
import org.junit.runner.RunWith
+@Suppress("DEPRECATION")
@MediumTest
@RunWith(AndroidJUnit4::class)
class ViewModelProvidersFragmentTest {
diff --git a/lifecycle/lifecycle-extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java b/lifecycle/lifecycle-extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
index 0b83b92..2bf899e 100644
--- a/lifecycle/lifecycle-extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
+++ b/lifecycle/lifecycle-extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
@@ -16,7 +16,6 @@
package androidx.lifecycle;
-import android.app.Activity;
import android.app.Application;
import androidx.annotation.MainThread;
@@ -28,7 +27,10 @@
/**
* Utilities methods for {@link ViewModelStore} class.
+ *
+ * @deprecated Use the constructors for {@link ViewModelProvider} directly.
*/
+@Deprecated
public class ViewModelProviders {
/**
@@ -38,51 +40,44 @@
public ViewModelProviders() {
}
- private static Application checkApplication(Activity activity) {
- Application application = activity.getApplication();
- if (application == null) {
- throw new IllegalStateException("Your activity/fragment is not yet attached to "
- + "Application. You can't request ViewModel before onCreate call.");
- }
- return application;
- }
-
- private static Activity checkActivity(Fragment fragment) {
- Activity activity = fragment.getActivity();
- if (activity == null) {
- throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
- }
- return activity;
- }
-
/**
* Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
* {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
* <p>
- * It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
+ * It uses the {@link Fragment#getDefaultViewModelProviderFactory() default factory}
+ * to instantiate new ViewModels.
*
* @param fragment a fragment, in whose scope ViewModels should be retained
* @return a ViewModelProvider instance
+ * @deprecated Use the 'by viewModels()' Kotlin property delegate or
+ * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)},
+ * passing in the fragment.
*/
+ @Deprecated
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
- return of(fragment, null);
+ return new ViewModelProvider(fragment);
}
/**
* Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
* is alive. More detailed explanation is in {@link ViewModel}.
* <p>
- * It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
+ * It uses the {@link FragmentActivity#getDefaultViewModelProviderFactory() default factory}
+ * to instantiate new ViewModels.
*
* @param activity an activity, in whose scope ViewModels should be retained
* @return a ViewModelProvider instance
+ * @deprecated Use the 'by viewModels()' Kotlin property delegate or
+ * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)},
+ * passing in the activity.
*/
+ @Deprecated
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
- return of(activity, null);
+ return new ViewModelProvider(activity);
}
/**
@@ -94,13 +89,16 @@
* @param fragment a fragment, in whose scope ViewModels should be retained
* @param factory a {@code Factory} to instantiate new ViewModels
* @return a ViewModelProvider instance
+ * @deprecated Use the 'by viewModels()' Kotlin property delegate or
+ * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner, Factory)},
+ * passing in the fragment and factory.
*/
+ @Deprecated
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
- Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
- factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+ factory = fragment.getDefaultViewModelProviderFactory();
}
return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
@@ -114,14 +112,17 @@
* @param activity an activity, in whose scope ViewModels should be retained
* @param factory a {@code Factory} to instantiate new ViewModels
* @return a ViewModelProvider instance
+ * @deprecated Use the 'by viewModels()' Kotlin property delegate or
+ * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner, Factory)},
+ * passing in the activity and factory.
*/
+ @Deprecated
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
- Application application = checkApplication(activity);
if (factory == null) {
- factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+ factory = activity.getDefaultViewModelProviderFactory();
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
diff --git a/lifecycle/lifecycle-extensions/src/test/java/androidx/lifecycle/ViewModelProvidersTest.java b/lifecycle/lifecycle-extensions/src/test/java/androidx/lifecycle/ViewModelProvidersTest.java
index e21f5c1..1dc35ae 100644
--- a/lifecycle/lifecycle-extensions/src/test/java/androidx/lifecycle/ViewModelProvidersTest.java
+++ b/lifecycle/lifecycle-extensions/src/test/java/androidx/lifecycle/ViewModelProvidersTest.java
@@ -23,6 +23,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@SuppressWarnings("deprecation")
@RunWith(JUnit4.class)
public class ViewModelProvidersTest {
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/1.0.0-alpha03.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/1.0.0-alpha03.txt
index 53339c9..8bd77f2 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/1.0.0-alpha03.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/1.0.0-alpha03.txt
@@ -20,10 +20,7 @@
}
public final class SavedStateViewModelFactory extends androidx.lifecycle.AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
- ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment, android.os.Bundle?);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity, android.os.Bundle?);
+ ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner);
ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner, android.os.Bundle?);
method protected <T extends androidx.lifecycle.ViewModel> T create(String, Class<T!>, androidx.lifecycle.SavedStateHandle);
}
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt
index 53339c9..8bd77f2 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt
@@ -20,10 +20,7 @@
}
public final class SavedStateViewModelFactory extends androidx.lifecycle.AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
- ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment, android.os.Bundle?);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity, android.os.Bundle?);
+ ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner);
ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner, android.os.Bundle?);
method protected <T extends androidx.lifecycle.ViewModel> T create(String, Class<T!>, androidx.lifecycle.SavedStateHandle);
}
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_1.0.0-alpha03.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_1.0.0-alpha03.txt
index b023830b..efd5ff3 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_1.0.0-alpha03.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_1.0.0-alpha03.txt
@@ -21,10 +21,7 @@
}
public final class SavedStateViewModelFactory extends androidx.lifecycle.AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
- ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment, android.os.Bundle?);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity, android.os.Bundle?);
+ ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner);
ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner, android.os.Bundle?);
method protected <T extends androidx.lifecycle.ViewModel> T create(String, Class<T!>, androidx.lifecycle.SavedStateHandle);
}
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt
index b023830b..efd5ff3 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt
@@ -21,10 +21,7 @@
}
public final class SavedStateViewModelFactory extends androidx.lifecycle.AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
- ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment, android.os.Bundle?);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity);
- ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity, android.os.Bundle?);
+ ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner);
ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner, android.os.Bundle?);
method protected <T extends androidx.lifecycle.ViewModel> T create(String, Class<T!>, androidx.lifecycle.SavedStateHandle);
}
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
index 2d58931..ad09591 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
@@ -38,10 +38,9 @@
api(project(":lifecycle:lifecycle-livedata-core"))
api(project(":lifecycle:lifecycle-viewmodel"))
- api project(":fragment:fragment"), {
- exclude group: 'androidx.lifecycle', module: 'lifecycle-livedata-core'
+ androidTestImplementation project(":fragment:fragment"), {
+ exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-savedstate'
}
-
androidTestImplementation(TRUTH)
androidTestImplementation(KOTLIN_STDLIB)
androidTestImplementation(ESPRESSO_CORE)
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateFactoryTest.kt b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateFactoryTest.kt
index 0277b01..2da4e17 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateFactoryTest.kt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateFactoryTest.kt
@@ -41,8 +41,9 @@
@Test
fun testCreateAndroidVM() {
- val savedStateVMFactory =
- SavedStateViewModelFactory(activityRule.activity)
+ val savedStateVMFactory = SavedStateViewModelFactory(
+ activityRule.activity.application,
+ activityRule.activity)
val vm = ViewModelProvider(ViewModelStore(), savedStateVMFactory)
assertThat(vm.get(MyAndroidViewModel::class.java).handle).isNotNull()
assertThat(vm.get(MyViewModel::class.java).handle).isNotNull()
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/ViewModelsWithStateTests.java b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/ViewModelsWithStateTests.java
index 5b2b458..8f0a31d 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/ViewModelsWithStateTests.java
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/ViewModelsWithStateTests.java
@@ -143,14 +143,17 @@
private ViewModelProvider vmProvider(FakingSavedStateActivity activity) {
if (FRAGMENT_MODE.equals(mode)) {
Fragment fragment = activity.getFragment();
- return new ViewModelProvider(fragment, new SavedStateViewModelFactory(fragment));
+ return new ViewModelProvider(fragment, new SavedStateViewModelFactory(
+ fragment.requireActivity().getApplication(), fragment));
}
- return new ViewModelProvider(activity, new SavedStateViewModelFactory(activity));
+ return new ViewModelProvider(activity, new SavedStateViewModelFactory(
+ activity.getApplication(), activity));
}
// copy copy copy paste
@SuppressWarnings("unchecked")
- private static <T extends Activity> T recreateActivity(final T activity, ActivityTestRule rule)
+ private static <T extends Activity> T recreateActivity(final T activity,
+ ActivityTestRule<?> rule)
throws Throwable {
Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
activity.getClass().getCanonicalName(), null, false);
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java
index a4c48dc..59a3039 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java
@@ -16,14 +16,12 @@
package androidx.lifecycle;
-import android.app.Activity;
+import android.annotation.SuppressLint;
import android.app.Application;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
import androidx.savedstate.SavedStateRegistryOwner;
import java.lang.reflect.Constructor;
@@ -33,8 +31,7 @@
/**
* {@link androidx.lifecycle.ViewModelProvider.Factory} that can create ViewModels accessing and
* contributing to a saved state via {@link SavedStateHandle} received in a constructor.
- * If {@code defaultArgs} bundle was passed in {@link #SavedStateViewModelFactory(Fragment, Bundle)}
- * or {@link #SavedStateViewModelFactory(FragmentActivity, Bundle)}, it will provide default
+ * If {@code defaultArgs} bundle was passed into the constructor, it will provide default
* values in {@code SavedStateHandle}.
* <p>
* If ViewModel is instance of {@link androidx.lifecycle.AndroidViewModel}, it looks for a
@@ -49,55 +46,15 @@
* Creates {@link SavedStateViewModelFactory}.
* <p>
* {@link androidx.lifecycle.ViewModel} created with this factory can access to saved state
- * scoped to the given {@code fragment}.
- *
- * @param fragment scope of this fragment will be used for state saving
- */
- public SavedStateViewModelFactory(@NonNull Fragment fragment) {
- this(fragment, null);
- }
-
- /**
- * Creates {@link SavedStateViewModelFactory}.
- * <p>
- * {@link androidx.lifecycle.ViewModel} created with this factory can access to saved state
- * scoped to the given {@code fragment}.
- *
- * @param fragment scope of this fragment will be used for state saving
- * @param defaultArgs values from this {@code Bundle} will be used as defaults by
- * {@link SavedStateHandle} if there is no previously saved state or previously saved state
- * miss a value by such key.
- */
- public SavedStateViewModelFactory(@NonNull Fragment fragment, @Nullable Bundle defaultArgs) {
- this(checkApplication(checkActivity(fragment)), fragment, defaultArgs);
- }
-
- /**
- * Creates {@link SavedStateViewModelFactory}.
- * <p>
- * {@link androidx.lifecycle.ViewModel} created with this factory can access to saved state
* scoped to the given {@code activity}.
*
- * @param activity scope of this activity will be used for state saving
+ * @param application an application
+ * @param owner {@link SavedStateRegistryOwner} that will provide restored state for created
+ * {@link androidx.lifecycle.ViewModel ViewModels}
*/
- public SavedStateViewModelFactory(@NonNull FragmentActivity activity) {
- this(activity, null);
- }
-
- /**
- * Creates {@link SavedStateViewModelFactory}.
- * <p>
- * {@link androidx.lifecycle.ViewModel} created with this factory can access to saved state
- * scoped to the given {@code activity}.
- *
- * @param activity scope of this activity will be used for state saving
- * @param defaultArgs values from this {@code Bundle} will be used as defaults by
- * {@link SavedStateHandle} if there is no previously saved state or previously saved state
- * misses a value by such key.
- */
- public SavedStateViewModelFactory(@NonNull FragmentActivity activity,
- @Nullable Bundle defaultArgs) {
- this(checkApplication(activity), activity, defaultArgs);
+ public SavedStateViewModelFactory(@NonNull Application application,
+ @NonNull SavedStateRegistryOwner owner) {
+ this(application, owner, null);
}
/**
@@ -113,6 +70,7 @@
* {@link SavedStateHandle} if there is no previously saved state or previously saved state
* misses a value by such key.
*/
+ @SuppressLint("LambdaLast")
public SavedStateViewModelFactory(@NonNull Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
@@ -166,23 +124,4 @@
}
return null;
}
-
- private static Application checkApplication(Activity activity) {
- Application application = activity.getApplication();
- if (application == null) {
- throw new IllegalStateException("Your activity/fragment is not yet attached to "
- + "Application. You can't request ViewModelsWithStateFactory "
- + "before onCreate call.");
- }
- return application;
- }
-
- private static Activity checkActivity(Fragment fragment) {
- Activity activity = fragment.getActivity();
- if (activity == null) {
- throw new IllegalStateException("Can't create ViewModelsWithStateFactory"
- + " for detached fragment");
- }
- return activity;
- }
}
diff --git a/lifecycle/lifecycle-viewmodel/api/2.2.0-alpha03.txt b/lifecycle/lifecycle-viewmodel/api/2.2.0-alpha03.txt
index 1c69a2a..07a8cb5 100644
--- a/lifecycle/lifecycle-viewmodel/api/2.2.0-alpha03.txt
+++ b/lifecycle/lifecycle-viewmodel/api/2.2.0-alpha03.txt
@@ -6,12 +6,17 @@
method public <T extends android.app.Application> T getApplication();
}
+ public interface HasDefaultViewModelProviderFactory {
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ }
+
public abstract class ViewModel {
ctor public ViewModel();
method protected void onCleared();
}
public class ViewModelProvider {
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner);
ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner, androidx.lifecycle.ViewModelProvider.Factory);
ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore, androidx.lifecycle.ViewModelProvider.Factory);
method @MainThread public <T extends androidx.lifecycle.ViewModel> T get(Class<T!>);
diff --git a/lifecycle/lifecycle-viewmodel/api/current.txt b/lifecycle/lifecycle-viewmodel/api/current.txt
index 1c69a2a..07a8cb5 100644
--- a/lifecycle/lifecycle-viewmodel/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/current.txt
@@ -6,12 +6,17 @@
method public <T extends android.app.Application> T getApplication();
}
+ public interface HasDefaultViewModelProviderFactory {
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ }
+
public abstract class ViewModel {
ctor public ViewModel();
method protected void onCleared();
}
public class ViewModelProvider {
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner);
ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner, androidx.lifecycle.ViewModelProvider.Factory);
ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore, androidx.lifecycle.ViewModelProvider.Factory);
method @MainThread public <T extends androidx.lifecycle.ViewModel> T get(Class<T!>);
diff --git a/lifecycle/lifecycle-viewmodel/api/restricted_2.2.0-alpha03.txt b/lifecycle/lifecycle-viewmodel/api/restricted_2.2.0-alpha03.txt
index 1c69a2a..07a8cb5 100644
--- a/lifecycle/lifecycle-viewmodel/api/restricted_2.2.0-alpha03.txt
+++ b/lifecycle/lifecycle-viewmodel/api/restricted_2.2.0-alpha03.txt
@@ -6,12 +6,17 @@
method public <T extends android.app.Application> T getApplication();
}
+ public interface HasDefaultViewModelProviderFactory {
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ }
+
public abstract class ViewModel {
ctor public ViewModel();
method protected void onCleared();
}
public class ViewModelProvider {
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner);
ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner, androidx.lifecycle.ViewModelProvider.Factory);
ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore, androidx.lifecycle.ViewModelProvider.Factory);
method @MainThread public <T extends androidx.lifecycle.ViewModel> T get(Class<T!>);
diff --git a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
index 1c69a2a..07a8cb5 100644
--- a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
@@ -6,12 +6,17 @@
method public <T extends android.app.Application> T getApplication();
}
+ public interface HasDefaultViewModelProviderFactory {
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ }
+
public abstract class ViewModel {
ctor public ViewModel();
method protected void onCleared();
}
public class ViewModelProvider {
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner);
ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner, androidx.lifecycle.ViewModelProvider.Factory);
ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore, androidx.lifecycle.ViewModelProvider.Factory);
method @MainThread public <T extends androidx.lifecycle.ViewModel> T get(Class<T!>);
diff --git a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/HasDefaultViewModelProviderFactory.java b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/HasDefaultViewModelProviderFactory.java
new file mode 100644
index 0000000..1db6658
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/HasDefaultViewModelProviderFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.lifecycle;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Interface that marks a {@link ViewModelStoreOwner} as having a default
+ * {@link ViewModelProvider.Factory} for use with
+ * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)}.
+ */
+public interface HasDefaultViewModelProviderFactory {
+ /**
+ * Returns the default {@link ViewModelProvider.Factory} that should be
+ * used when no custom {@code Factory} is provided to the
+ * {@link ViewModelProvider} constructors.
+ *
+ * @return a {@code ViewModelProvider.Factory}
+ */
+ @NonNull
+ ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+}
diff --git a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java
index 0709327..259430f 100644
--- a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java
+++ b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java
@@ -54,7 +54,7 @@
* protected void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
* setContentView(R.layout.user_activity_layout);
- * final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
+ * final UserModel viewModel = new ViewModelProvider(this).get(UserModel.class);
* viewModel.userLiveData.observer(this, new Observer<User>() {
* {@literal @}Override
* public void onChanged(@Nullable User data) {
@@ -99,7 +99,7 @@
* <pre>
* public class MyFragment extends Fragment {
* public void onStart() {
- * UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
+ * UserModel userModel = new ViewModelProvider(requireActivity()).get(UserModel.class);
* }
* }
* </pre>
diff --git a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.java b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.java
index 56d9f1b..e66da15 100644
--- a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.java
+++ b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.java
@@ -27,7 +27,7 @@
* An utility class that provides {@code ViewModels} for a scope.
* <p>
* Default {@code ViewModelProvider} for an {@code Activity} or a {@code Fragment} can be obtained
- * from {@link androidx.lifecycle.ViewModelProviders} class.
+ * by passing it to {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)}.
*/
@SuppressWarnings("WeakerAccess")
public class ViewModelProvider {
@@ -82,6 +82,21 @@
private final ViewModelStore mViewModelStore;
/**
+ * Creates {@code ViewModelProvider}. This will create {@code ViewModels}
+ * and retain them in a store of the given {@code ViewModelStoreOwner}.
+ * <p>
+ * This method will use the
+ * {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory}
+ * if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a
+ * {@link NewInstanceFactory} will be used.
+ */
+ public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
+ this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
+ ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
+ : NewInstanceFactory.getInstance());
+ }
+
+ /**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
*
@@ -172,6 +187,21 @@
*/
public static class NewInstanceFactory implements Factory {
+ private static NewInstanceFactory sInstance;
+
+ /**
+ * Retrieve a singleton instance of NewInstanceFactory.
+ *
+ * @return A valid {@link NewInstanceFactory}
+ */
+ @NonNull
+ static NewInstanceFactory getInstance() {
+ if (sInstance == null) {
+ sInstance = new NewInstanceFactory();
+ }
+ return sInstance;
+ }
+
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
diff --git a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java
index dd9470f..0d5783c 100644
--- a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java
+++ b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java
@@ -85,6 +85,17 @@
}
@Test
+ public void testCustomDefaultFactory() {
+ final ViewModelStore store = new ViewModelStore();
+ final CountingFactory factory = new CountingFactory();
+ ViewModelStoreOwnerWithFactory owner = new ViewModelStoreOwnerWithFactory(store, factory);
+ ViewModelProvider provider = new ViewModelProvider(owner);
+ ViewModel1 viewModel = provider.get(ViewModel1.class);
+ assertThat(viewModel, is(provider.get(ViewModel1.class)));
+ assertThat(factory.mCalled, is(1));
+ }
+
+ @Test
public void testKeyedFactory() {
final ViewModelStore store = new ViewModelStore();
ViewModelStoreOwner owner = new ViewModelStoreOwner() {
@@ -107,6 +118,30 @@
provider.get("customkey", ViewModel1.class);
}
+ public static class ViewModelStoreOwnerWithFactory implements
+ ViewModelStoreOwner, HasDefaultViewModelProviderFactory {
+ private final ViewModelStore mStore;
+ private final ViewModelProvider.Factory mFactory;
+
+ ViewModelStoreOwnerWithFactory(@NonNull ViewModelStore store,
+ @NonNull ViewModelProvider.Factory factory) {
+ mStore = store;
+ mFactory = factory;
+ }
+
+ @NonNull
+ @Override
+ public ViewModelStore getViewModelStore() {
+ return mStore;
+ }
+
+ @NonNull
+ @Override
+ public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+ return mFactory;
+ }
+ }
+
public static class ViewModel1 extends ViewModel {
boolean mCleared;
@@ -118,4 +153,15 @@
public static class ViewModel2 extends ViewModel {
}
+
+ public static class CountingFactory extends NewInstanceFactory {
+ int mCalled = 0;
+
+ @Override
+ @NonNull
+ public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+ mCalled++;
+ return super.create(modelClass);
+ }
+ }
}
diff --git a/media/build.gradle b/media/build.gradle
index f0a49ef..863a93c 100644
--- a/media/build.gradle
+++ b/media/build.gradle
@@ -9,7 +9,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.0.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java b/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
index 8f43365..a6bf8dd 100644
--- a/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -53,8 +53,8 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.app.BundleCompat;
-import androidx.core.app.ComponentActivity;
import androidx.media.AudioAttributesCompat;
+import androidx.media.R;
import androidx.media.VolumeProviderCompat;
import androidx.versionedparcelable.ParcelUtils;
import androidx.versionedparcelable.VersionedParcelable;
@@ -144,26 +144,11 @@
public static final String COMMAND_ARGUMENT_INDEX =
"android.support.v4.media.session.command.ARGUMENT_INDEX";
- private static class MediaControllerExtraData extends ComponentActivity.ExtraData {
- private final MediaControllerCompat mMediaController;
-
- MediaControllerExtraData(MediaControllerCompat mediaController) {
- mMediaController = mediaController;
- }
-
- MediaControllerCompat getMediaController() {
- return mMediaController;
- }
- }
-
/**
* Sets a {@link MediaControllerCompat} in the {@code activity} for later retrieval via
* {@link #getMediaController(Activity)}.
*
- * <p>This is compatible with {@link Activity#setMediaController(MediaController)}.
- * If {@code activity} inherits {@link androidx.fragment.app.FragmentActivity}, the
- * {@code mediaController} will be saved in the {@code activity}. In addition to that,
- * on API 21 and later, {@link Activity#setMediaController(MediaController)} will be
+ * <p>On API 21 and later, {@link Activity#setMediaController(MediaController)} will also be
* called.</p>
*
* @param activity The activity to set the {@code mediaController} in, must not be null.
@@ -174,10 +159,8 @@
*/
public static void setMediaController(@NonNull Activity activity,
MediaControllerCompat mediaController) {
- if (activity instanceof ComponentActivity) {
- ((ComponentActivity) activity).putExtraData(
- new MediaControllerExtraData(mediaController));
- }
+ activity.getWindow().getDecorView().setTag(
+ R.id.media_controller_compat_view_tag, mediaController);
if (android.os.Build.VERSION.SDK_INT >= 21) {
MediaController controllerFwk = null;
if (mediaController != null) {
@@ -200,10 +183,10 @@
* @see #setMediaController(Activity, MediaControllerCompat)
*/
public static MediaControllerCompat getMediaController(@NonNull Activity activity) {
- if (activity instanceof ComponentActivity) {
- MediaControllerExtraData extraData =
- ((ComponentActivity) activity).getExtraData(MediaControllerExtraData.class);
- return extraData != null ? extraData.getMediaController() : null;
+ Object tag = activity.getWindow().getDecorView()
+ .getTag(R.id.media_controller_compat_view_tag);
+ if (tag instanceof MediaControllerCompat) {
+ return (MediaControllerCompat) tag;
} else if (android.os.Build.VERSION.SDK_INT >= 21) {
MediaController controllerFwk = activity.getMediaController();
if (controllerFwk == null) {
diff --git a/media/src/main/res/values/ids.xml b/media/src/main/res/values/ids.xml
new file mode 100644
index 0000000..2fa8e5c
--- /dev/null
+++ b/media/src/main/res/values/ids.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <item type="id" name="media_controller_compat_view_tag" />
+</resources>
\ No newline at end of file
diff --git a/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java b/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java
index 2d1b17f..c67153a 100644
--- a/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java
+++ b/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java
@@ -452,8 +452,8 @@
* @return the size of the video. The width and height of size could be 0 if there is no video
* or the size has not been determined yet.
* The {@link PlayerCallback} can be registered via {@link #registerPlayerCallback} to
- * receive a notification {@link PlayerCallback#onVideoSizeChangedInternal} when the size
- * is available.
+ * receive a notification {@link PlayerCallback#onVideoSizeChanged(SessionPlayer, VideoSize)}
+ * when the size is available.
*
* @hide
*/
@@ -1338,6 +1338,16 @@
}
/**
+ * @deprecated Use {@link #onVideoSizeChanged(SessionPlayer, VideoSize)} instead.
+ * @hide
+ */
+ @Deprecated
+ @RestrictTo(LIBRARY_GROUP)
+ public void onVideoSizeChangedInternal(
+ @NonNull SessionPlayer player, @NonNull MediaItem item, @NonNull VideoSize size) {
+ }
+
+ /**
* Called to indicate the video size
* <p>
* The video size (width and height) could be 0 if there was no video,
@@ -1348,16 +1358,14 @@
* is called.
*
* @param player the player associated with this callback
- * @param item the MediaItem of this media item
* @param size the size of the video
* @see #getVideoSizeInternal()
*
* @hide
*/
- // TODO: Add onVideoSizeChanged and deprecate this method (b/132928418)
+ // TODO: Unhide this method (b/132928418)
@RestrictTo(LIBRARY_GROUP)
- public void onVideoSizeChangedInternal(
- @NonNull SessionPlayer player, @NonNull MediaItem item, @NonNull VideoSize size) {
+ public void onVideoSizeChanged(@NonNull SessionPlayer player, @NonNull VideoSize size) {
}
/**
diff --git a/media2/integration-tests/testapp/build.gradle b/media2/integration-tests/testapp/build.gradle
index b269e6a..ffefd37 100644
--- a/media2/integration-tests/testapp/build.gradle
+++ b/media2/integration-tests/testapp/build.gradle
@@ -30,7 +30,7 @@
implementation(project(":media2:media2-player"))
implementation(project(":media2:media2-widget"))
implementation("androidx.appcompat:appcompat:1.0.2")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
}
android {
diff --git a/media2/media2-exoplayer/src/main/libs/exoplayer-media2.jar b/media2/media2-exoplayer/src/main/libs/exoplayer-media2.jar
index dffa337..121b6d1 100755
--- a/media2/media2-exoplayer/src/main/libs/exoplayer-media2.jar
+++ b/media2/media2-exoplayer/src/main/libs/exoplayer-media2.jar
Binary files differ
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 1cf11d4..00e85db 100644
--- a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
+++ b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
@@ -199,6 +199,16 @@
MediaPlayer.PlayerCallback callback = new MediaPlayer.PlayerCallback() {
@Override
public void onVideoSizeChanged(MediaPlayer mp, MediaItem dsd, VideoSize size) {
+ assertVideoSizeEquals(size);
+ }
+
+ @Override
+ public void onVideoSizeChanged(@NonNull SessionPlayer player,
+ @NonNull androidx.media2.common.VideoSize size) {
+ assertVideoSizeEquals(new VideoSize(size.getWidth(), size.getHeight()));
+ }
+
+ private void assertVideoSizeEquals(VideoSize size) {
if (size.getWidth() == 0 && size.getHeight() == 0) {
// A size of 0x0 can be sent initially one time when using NuPlayer.
assertFalse(onVideoSizeChangedCalled.isSignalled());
@@ -226,7 +236,7 @@
mPlayer.prepare();
mPlayer.play();
- onVideoSizeChangedCalled.waitForSignal();
+ onVideoSizeChangedCalled.waitForCountedSignals(2);
onVideoRenderingStartCalled.waitForSignal();
mPlayer.setPlayerVolume(volume);
@@ -299,6 +309,16 @@
MediaPlayer.PlayerCallback callback = new MediaPlayer.PlayerCallback() {
@Override
public void onVideoSizeChanged(MediaPlayer mp, MediaItem dsd, VideoSize size) {
+ assertVideoSizeEquals(size);
+ }
+
+ @Override
+ public void onVideoSizeChanged(@NonNull SessionPlayer player,
+ @NonNull androidx.media2.common.VideoSize size) {
+ assertVideoSizeEquals(new VideoSize(size.getWidth(), size.getHeight()));
+ }
+
+ private void assertVideoSizeEquals(VideoSize size) {
if (size.getWidth() == 0 && size.getHeight() == 0) {
// A size of 0x0 can be sent initially one time when using NuPlayer.
assertFalse(onVideoSizeChangedCalled.isSignalled());
@@ -326,7 +346,7 @@
mPlayer.prepare();
mPlayer.play();
- onVideoSizeChangedCalled.waitForSignal();
+ onVideoSizeChangedCalled.waitForCountedSignals(2);
onVideoRenderingStartCalled.waitForSignal();
}
diff --git a/media2/player/src/main/java/androidx/media2/player/AudioFocusHandler.java b/media2/player/src/main/java/androidx/media2/player/AudioFocusHandler.java
index 8738f1d..54797a6 100644
--- a/media2/player/src/main/java/androidx/media2/player/AudioFocusHandler.java
+++ b/media2/player/src/main/java/androidx/media2/player/AudioFocusHandler.java
@@ -280,7 +280,7 @@
// want to have more finer grained control. (e.g. adding audio focus listener)
return AudioManager.AUDIOFOCUS_NONE;
}
- // Javadoc here means 'The different types of focus reuqests' written in the
+ // Javadoc here means 'The different types of focus requests' written in the
// {@link AudioFocusRequest}.
switch (audioAttributesCompat.getUsage()) {
// USAGE_VOICE_COMMUNICATION_SIGNALLING is for DTMF that may happen multiple times
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 56e8aac..4142751 100644
--- a/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
+++ b/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
@@ -428,7 +428,6 @@
.setAudioFallbackMode(PlaybackParams.AUDIO_FALLBACK_MODE_DEFAULT)
.build();
- private static final int CALL_COMPLETE_PLAYLIST_BASE = -1000;
private static final int END_OF_PLAYLIST = -1;
private static final int NO_MEDIA_ITEM = -2;
@@ -504,21 +503,27 @@
@SuppressWarnings("WeakerAccess") /* synthetic access */
@MediaPlayer2.CallCompleted final int mCallType;
@SuppressWarnings("WeakerAccess") /* synthetic access */
- final ResolvableFuture mFuture;
+ final ResolvableFuture<? extends PlayerResult> mFuture;
@SuppressWarnings("WeakerAccess") /* synthetic access */
final SessionPlayer.TrackInfo mTrackInfo;
@SuppressWarnings("WeakerAccess") /* synthetic access */
- PendingCommand(int callType, ResolvableFuture future) {
+ PendingCommand(int callType, ResolvableFuture<? extends PlayerResult> future) {
this(callType, future, null);
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
- PendingCommand(int callType, ResolvableFuture future, SessionPlayer.TrackInfo trackInfo) {
+ PendingCommand(int callType, ResolvableFuture<? extends PlayerResult> future,
+ SessionPlayer.TrackInfo trackInfo) {
mCallType = callType;
mFuture = future;
mTrackInfo = trackInfo;
}
+
+ @SuppressWarnings("unchecked")
+ <V extends PlayerResult> void setResult(V value) {
+ ((ResolvableFuture<V>) mFuture).set(value);
+ }
}
/* A list for tracking the commands submitted to MediaPlayer2.*/
@@ -622,7 +627,7 @@
/* A list of pending operations within this MediaPlayer that will be executed sequentially. */
@SuppressWarnings("WeakerAccess") /* synthetic access */
@GuardedBy("mPendingFutures")
- final ArrayDeque<PendingFuture<? super PlayerResult>> mPendingFutures = new ArrayDeque<>();
+ final ArrayDeque<PendingFuture<? extends PlayerResult>> mPendingFutures = new ArrayDeque<>();
private final Object mStateLock = new Object();
@GuardedBy("mStateLock")
@@ -686,7 +691,8 @@
@GuardedBy("mPendingCommands")
@SuppressWarnings("WeakerAccess") /* synthetic access */
void addPendingCommandLocked(
- int callType, final ResolvableFuture future, final Object token) {
+ int callType, final ResolvableFuture<? extends PlayerResult> future,
+ final Object token) {
final PendingCommand pendingCommand = new PendingCommand(callType, future);
mPendingCommands.add(pendingCommand);
addFutureListener(pendingCommand, future, token);
@@ -695,17 +701,16 @@
@GuardedBy("mPendingCommands")
@SuppressWarnings("WeakerAccess") /* synthetic access */
void addPendingCommandWithTrackInfoLocked(
- int callType, final ResolvableFuture future, final SessionPlayer.TrackInfo trackInfo,
- final Object token) {
+ int callType, final ResolvableFuture<? extends PlayerResult> future,
+ final SessionPlayer.TrackInfo trackInfo, final Object token) {
final PendingCommand pendingCommand = new PendingCommand(callType, future, trackInfo);
mPendingCommands.add(pendingCommand);
addFutureListener(pendingCommand, future, token);
}
- @GuardedBy("mPendingCommands")
@SuppressWarnings("WeakerAccess") /* synthetic access */
- void addFutureListener(final PendingCommand pendingCommand, final ResolvableFuture future,
- final Object token) {
+ void addFutureListener(final PendingCommand pendingCommand,
+ final ResolvableFuture<? extends PlayerResult> future, final Object token) {
future.addListener(new Runnable() {
@Override
public void run() {
@@ -721,8 +726,7 @@
}, mExecutor);
}
- @SuppressWarnings("unchecked")
- private void addPendingFuture(final PendingFuture pendingFuture) {
+ private void addPendingFuture(final PendingFuture<? extends PlayerResult> pendingFuture) {
synchronized (mPendingFutures) {
mPendingFutures.add(pendingFuture);
executePendingFutures();
@@ -1776,7 +1780,7 @@
}
// Cancel the pending futures.
synchronized (mPendingFutures) {
- for (PendingFuture f : mPendingFutures) {
+ for (PendingFuture<? extends PlayerResult> f : mPendingFutures) {
if (f.mExecuteCalled && !f.isDone() && !f.isCancelled()) {
f.cancel(true);
}
@@ -2976,7 +2980,7 @@
return (value > maxValue) ? maxValue : value;
}
- @SuppressWarnings({"WeakerAccess", "unchecked"}) /* synthetic access */
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
void handleCallComplete(MediaPlayer2 mp, final MediaItem item, int what, int status) {
PendingCommand expected;
synchronized (mPendingCommands) {
@@ -3014,13 +3018,14 @@
case MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE:
case MediaPlayer2.CALL_COMPLETED_SKIP_TO_NEXT:
final List<SessionPlayer.TrackInfo> tracks = mp.getTrackInfo();
+ final androidx.media2.common.VideoSize videoSize = getVideoSizeInternal();
notifySessionPlayerCallback(new SessionPlayerCallbackNotifier() {
@Override
public void callCallback(
SessionPlayer.PlayerCallback callback) {
callback.onCurrentMediaItemChanged(MediaPlayer.this, item);
- callback.onVideoSizeChangedInternal(MediaPlayer.this,
- getCurrentMediaItem(), getVideoSizeInternal());
+ callback.onVideoSizeChanged(MediaPlayer.this, videoSize);
+ callback.onVideoSizeChangedInternal(MediaPlayer.this, item, videoSize);
callback.onTrackInfoChanged(MediaPlayer.this, tracks);
}
});
@@ -3066,20 +3071,20 @@
if (what != MediaPlayer2.CALL_COMPLETED_PREPARE_DRM) {
Integer resultCode = sResultCodeMap.containsKey(status)
? sResultCodeMap.get(status) : RESULT_ERROR_UNKNOWN;
- expected.mFuture.set(new PlayerResult(resultCode, item));
+ expected.setResult(new PlayerResult(resultCode, item));
} else {
Integer resultCode = sPrepareDrmStatusMap.containsKey(status)
? sPrepareDrmStatusMap.get(status) : DrmResult.RESULT_ERROR_PREPARATION_ERROR;
- expected.mFuture.set(new DrmResult(resultCode, item));
+ expected.setResult(new DrmResult(resultCode, item));
}
executePendingFutures();
}
private void executePendingFutures() {
synchronized (mPendingFutures) {
- Iterator<PendingFuture<? super PlayerResult>> it = mPendingFutures.iterator();
+ Iterator<PendingFuture<? extends PlayerResult>> it = mPendingFutures.iterator();
while (it.hasNext()) {
- PendingFuture f = it.next();
+ PendingFuture<? extends PlayerResult> f = it.next();
if (f.isCancelled() || f.execute()) {
mPendingFutures.removeFirst();
} else {
@@ -3088,7 +3093,7 @@
}
// Execute skip futures earlier for making them be skipped.
while (it.hasNext()) {
- PendingFuture f = it.next();
+ PendingFuture<? extends PlayerResult> f = it.next();
if (!f.mIsSeekTo) {
break;
}
@@ -3129,6 +3134,7 @@
notifySessionPlayerCallback(new SessionPlayerCallbackNotifier() {
@Override
public void callCallback(SessionPlayer.PlayerCallback callback) {
+ callback.onVideoSizeChanged(MediaPlayer.this, commonSize);
callback.onVideoSizeChangedInternal(MediaPlayer.this, item, commonSize);
}
});
@@ -3257,9 +3263,12 @@
@NonNull MediaPlayer mp, @NonNull MediaItem item, @NonNull VideoSize size) { }
/**
+ * @deprecated Use
+ * {@link #onVideoSizeChanged(SessionPlayer, androidx.media2.common.VideoSize)} instead.
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
+ @Deprecated
@Override
public void onVideoSizeChangedInternal(
@NonNull SessionPlayer player, @NonNull MediaItem item,
@@ -3508,7 +3517,7 @@
DrmInfo(MediaPlayer2.DrmInfo info) {
mMp2DrmInfo = info;
}
- };
+ }
/**
* Interface definition of a callback to be invoked when the app
diff --git a/media2/player/src/main/java/androidx/media2/player/MediaPlayer2.java b/media2/player/src/main/java/androidx/media2/player/MediaPlayer2.java
index 3cb98ea..379bfac 100644
--- a/media2/player/src/main/java/androidx/media2/player/MediaPlayer2.java
+++ b/media2/player/src/main/java/androidx/media2/player/MediaPlayer2.java
@@ -690,8 +690,8 @@
* Returns the audio session ID.
*
* @return the audio session ID. {@see #setAudioSessionId(int)}
- * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer2 was
- * contructed.
+ * Note that the audio session ID is 0 only if a problem occurred when the MediaPlayer2 was
+ * constructed.
*/
public abstract int getAudioSessionId();
diff --git a/media2/player/src/main/java/androidx/media2/player/MediaTimestamp.java b/media2/player/src/main/java/androidx/media2/player/MediaTimestamp.java
index 63bb050..8d33205 100644
--- a/media2/player/src/main/java/androidx/media2/player/MediaTimestamp.java
+++ b/media2/player/src/main/java/androidx/media2/player/MediaTimestamp.java
@@ -103,8 +103,12 @@
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
final MediaTimestamp that = (MediaTimestamp) obj;
return (this.mMediaTimeUs == that.mMediaTimeUs)
diff --git a/media2/player/src/main/java/androidx/media2/player/exoplayer/DurationProvidingMediaSource.java b/media2/player/src/main/java/androidx/media2/player/exoplayer/DurationProvidingMediaSource.java
index 79aeb0a..5c52b8f 100644
--- a/media2/player/src/main/java/androidx/media2/player/exoplayer/DurationProvidingMediaSource.java
+++ b/media2/player/src/main/java/androidx/media2/player/exoplayer/DurationProvidingMediaSource.java
@@ -20,7 +20,6 @@
import android.annotation.SuppressLint;
-import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.media2.exoplayer.external.C;
import androidx.media2.exoplayer.external.Timeline;
@@ -65,6 +64,13 @@
}
@Override
+ protected void onChildSourceInfoRefreshed(Void id,
+ MediaSource mediaSource, Timeline timeline) {
+ mCurrentTimeline = timeline;
+ refreshSourceInfo(timeline);
+ }
+
+ @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
return mMediaSource.createPeriod(id, allocator, startPositionUs);
}
@@ -74,11 +80,4 @@
mMediaSource.releasePeriod(mediaPeriod);
}
- @Override
- protected void onChildSourceInfoRefreshed(
- Void id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) {
- mCurrentTimeline = timeline;
- refreshSourceInfo(timeline, manifest);
- }
-
}
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java b/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java
index 36e8ff2..57af209 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java
@@ -1649,8 +1649,9 @@
@Test
public void testGetVideoSize() throws InterruptedException {
prepareLooper();
+ final MediaItem item = TestUtils.createMediaItemWithMetadata();
final VideoSize testVideoSize = new VideoSize(100, 42);
- final CountDownLatch latch = new CountDownLatch(1);
+ final CountDownLatch latch = new CountDownLatch(2);
final ControllerCallback callback = new ControllerCallback() {
@Override
public void onVideoSizeChanged(@NonNull MediaController controller,
@@ -1659,8 +1660,16 @@
assertEquals(testVideoSize, videoSize);
latch.countDown();
}
+
+ @Override
+ public void onVideoSizeChanged(@NonNull MediaController controller,
+ @NonNull VideoSize videoSize) {
+ assertEquals(testVideoSize, videoSize);
+ latch.countDown();
+ }
};
MediaController controller = createController(mSession.getToken(), true, null, callback);
+ mPlayer.notifyCurrentMediaItemChanged(item);
mPlayer.notifyVideoSizeChanged(testVideoSize);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(testVideoSize, controller.getVideoSize());
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/TestBrowserCallback.java b/media2/session/src/androidTest/java/androidx/media2/session/TestBrowserCallback.java
index 0d7bed7..cb17489 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/TestBrowserCallback.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/TestBrowserCallback.java
@@ -183,6 +183,12 @@
}
@Override
+ public void onVideoSizeChanged(@NonNull MediaController controller,
+ @NonNull VideoSize videoSize) {
+ mCallbackProxy.onVideoSizeChanged(controller, videoSize);
+ }
+
+ @Override
public void onSubtitleData(@NonNull MediaController controller, @NonNull MediaItem item,
@NonNull SessionPlayer.TrackInfo track, @NonNull SubtitleData data) {
mCallbackProxy.onSubtitleData(controller, item, track, data);
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaController.java b/media2/session/src/main/java/androidx/media2/session/MediaController.java
index 4185834..bbe069d 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaController.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaController.java
@@ -1883,16 +1883,25 @@
public void onPlaybackCompleted(@NonNull MediaController controller) {}
/**
+ * @deprecated Use {@link #onVideoSizeChanged(MediaController, VideoSize)} instead.
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ @Deprecated
+ public void onVideoSizeChanged(@NonNull MediaController controller, @NonNull MediaItem item,
+ @NonNull VideoSize videoSize) {}
+
+ /**
* Called when video size is changed.
*
* @param controller the controller for this event
- * @param item the media item for which the video size changed
* @param videoSize the size of video
*
* @hide
*/
+ // TODO: Unhide this (b/134749006)
@RestrictTo(LIBRARY_GROUP)
- public void onVideoSizeChanged(@NonNull MediaController controller, @NonNull MediaItem item,
+ public void onVideoSizeChanged(@NonNull MediaController controller,
@NonNull VideoSize videoSize) {}
/**
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaControllerImplBase.java b/media2/session/src/main/java/androidx/media2/session/MediaControllerImplBase.java
index 755a17e..3ceb640 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaControllerImplBase.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaControllerImplBase.java
@@ -1144,9 +1144,11 @@
});
}
- void notifyVideoSizeChanged(final MediaItem item, final VideoSize videoSize) {
+ void notifyVideoSizeChanged(final VideoSize videoSize) {
+ final MediaItem currentItem;
synchronized (mLock) {
mVideoSize = videoSize;
+ currentItem = mCurrentMediaItem;
}
mInstance.notifyControllerCallback(new ControllerCallbackRunnable() {
@Override
@@ -1154,7 +1156,11 @@
if (!mInstance.isConnected()) {
return;
}
- callback.onVideoSizeChanged(mInstance, item, videoSize);
+
+ if (currentItem != null) {
+ callback.onVideoSizeChanged(mInstance, currentItem, videoSize);
+ }
+ callback.onVideoSizeChanged(mInstance, videoSize);
}
});
}
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaControllerStub.java b/media2/session/src/main/java/androidx/media2/session/MediaControllerStub.java
index 142dca2..8e16d21 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaControllerStub.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaControllerStub.java
@@ -242,23 +242,18 @@
@Override
public void onVideoSizeChanged(int seq, final ParcelImpl item, final ParcelImpl videoSize) {
- if (item == null || videoSize == null) {
+ if (videoSize == null) {
return;
}
dispatchControllerTask(new ControllerTask() {
@Override
public void run(MediaControllerImplBase controller) {
- MediaItem itemObj = MediaParcelUtils.fromParcelable(item);
- if (itemObj == null) {
- Log.w(TAG, "onVideoSizeChanged(): Ignoring null MediaItem");
- return;
- }
VideoSize size = MediaParcelUtils.fromParcelable(videoSize);
if (size == null) {
Log.w(TAG, "onVideoSizeChanged(): Ignoring null VideoSize");
return;
}
- controller.notifyVideoSizeChanged(itemObj, size);
+ controller.notifyVideoSizeChanged(size);
}
});
}
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaLibraryServiceLegacyStub.java b/media2/session/src/main/java/androidx/media2/session/MediaLibraryServiceLegacyStub.java
index 062046a..7244f81 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaLibraryServiceLegacyStub.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaLibraryServiceLegacyStub.java
@@ -492,8 +492,7 @@
}
@Override
- final void onVideoSizeChanged(int seq, @NonNull MediaItem item,
- @NonNull VideoSize videoSize) {
+ void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
// No-op. BrowserCompat doesn't understand Controller features.
}
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSession.java b/media2/session/src/main/java/androidx/media2/session/MediaSession.java
index e9b13ad..d601bd7 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSession.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSession.java
@@ -1208,8 +1208,8 @@
int currentIdx, int previousIdx, int nextIdx) throws RemoteException;
abstract void onPlaybackCompleted(int seq) throws RemoteException;
abstract void onDisconnected(int seq) throws RemoteException;
- abstract void onVideoSizeChanged(int seq, @NonNull MediaItem item,
- @NonNull VideoSize videoSize) throws RemoteException;
+ abstract void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize)
+ throws RemoteException;
abstract void onTrackInfoChanged(int seq, List<TrackInfo> trackInfos,
TrackInfo selectedVideoTrack, TrackInfo selectedAudioTrack,
TrackInfo selectedSubtitleTrack, TrackInfo selectedMetadataTrack)
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java b/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
index 7f42fc3..eee1bea 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
@@ -1536,17 +1536,22 @@
}
@Override
- public void onVideoSizeChangedInternal(@NonNull final SessionPlayer player,
- @NonNull final MediaItem item, @NonNull final VideoSize videoSize) {
+ public void onVideoSizeChanged(@NonNull SessionPlayer player, @NonNull VideoSize size) {
dispatchRemoteControllerTask(player, new RemoteControllerTask() {
@Override
public void run(ControllerCb callback, int seq) throws RemoteException {
- callback.onVideoSizeChanged(seq, item, videoSize);
+ callback.onVideoSizeChanged(seq, size);
}
});
}
@Override
+ public void onVideoSizeChangedInternal(@NonNull final SessionPlayer player,
+ @NonNull final MediaItem item, @NonNull final VideoSize videoSize) {
+ onVideoSizeChanged(player, videoSize);
+ }
+
+ @Override
public void onTrackInfoChanged(@NonNull SessionPlayer player,
@NonNull final List<TrackInfo> trackInfos) {
final MediaSessionImplBase session = getSession();
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSessionLegacyStub.java b/media2/session/src/main/java/androidx/media2/session/MediaSessionLegacyStub.java
index 367034f..4776445 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSessionLegacyStub.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSessionLegacyStub.java
@@ -732,7 +732,7 @@
}
@Override
- void onVideoSizeChanged(int seq, @NonNull MediaItem item, @NonNull VideoSize videoSize) {
+ void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
// no-op
}
@@ -947,7 +947,7 @@
}
@Override
- void onVideoSizeChanged(int seq, @NonNull MediaItem item, @NonNull VideoSize videoSize) {
+ void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
// no-op
}
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSessionStub.java b/media2/session/src/main/java/androidx/media2/session/MediaSessionStub.java
index 0507f33..6833d59 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSessionStub.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSessionStub.java
@@ -1432,11 +1432,9 @@
}
@Override
- void onVideoSizeChanged(int seq, @NonNull MediaItem item, @NonNull VideoSize videoSize)
- throws RemoteException {
- ParcelImpl itemParcel = MediaParcelUtils.toParcelable(item);
+ void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
ParcelImpl videoSizeParcel = MediaParcelUtils.toParcelable(videoSize);
- mIControllerCallback.onVideoSizeChanged(seq, itemParcel, videoSizeParcel);
+ mIControllerCallback.onVideoSizeChanged(seq, null, videoSizeParcel);
}
@Override
diff --git a/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/MediaControllerCallbackTest.java b/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/MediaControllerCallbackTest.java
index 8674218..9230345 100644
--- a/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/MediaControllerCallbackTest.java
+++ b/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/MediaControllerCallbackTest.java
@@ -865,7 +865,7 @@
prepareLooper();
final VideoSize testSize = new VideoSize(100, 42);
- final CountDownLatch latch = new CountDownLatch(1);
+ final CountDownLatch latch = new CountDownLatch(2);
final MediaController.ControllerCallback callback =
new MediaController.ControllerCallback() {
@Override
@@ -875,10 +875,18 @@
assertEquals(testSize, videoSize);
latch.countDown();
}
+
+ @Override
+ public void onVideoSizeChanged(@NonNull MediaController controller,
+ @NonNull VideoSize videoSize) {
+ assertEquals(testSize, videoSize);
+ latch.countDown();
+ }
};
MediaController controller = createController(mRemoteSession2.getToken(), true, null,
callback);
+ mRemoteSession2.getMockPlayer().notifyCurrentMediaItemChanged(INDEX_FOR_UNKONWN_ITEM);
mRemoteSession2.getMockPlayer().notifyVideoSizeChanged(testSize);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
diff --git a/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/TestBrowserCallback.java b/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/TestBrowserCallback.java
index b7d38fb..70073cd 100644
--- a/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/TestBrowserCallback.java
+++ b/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/TestBrowserCallback.java
@@ -189,6 +189,12 @@
}
@Override
+ public void onVideoSizeChanged(@NonNull MediaController controller,
+ @NonNull VideoSize videoSize) {
+ mCallbackProxy.onVideoSizeChanged(controller, videoSize);
+ }
+
+ @Override
public void onTrackInfoChanged(@NonNull MediaController controller,
@NonNull List<SessionPlayer.TrackInfo> trackInfos) {
mCallbackProxy.onTrackInfoChanged(controller, trackInfos);
diff --git a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
index eeee0e8..cbf4673 100644
--- a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
+++ b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
@@ -622,6 +622,7 @@
@Override
public void run() {
callback.onVideoSizeChangedInternal(MockPlayer.this, dummyItem, videoSize);
+ callback.onVideoSizeChanged(MockPlayer.this, videoSize);
}
});
}
diff --git a/navigation/benchmark/build.gradle b/navigation/benchmark/build.gradle
index 0a308dd..ad5d976 100644
--- a/navigation/benchmark/build.gradle
+++ b/navigation/benchmark/build.gradle
@@ -22,6 +22,7 @@
id("AndroidXPlugin")
id("com.android.library")
id("kotlin-android")
+ id("androidx.benchmark")
}
dependencies {
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
index e20b2c8..3b2698d 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -16,11 +16,14 @@
package androidx.navigation
+import android.app.Application
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStore
import androidx.navigation.test.R
import androidx.navigation.testing.TestNavigator
@@ -890,6 +893,25 @@
}
@Test
+ fun testGetViewModelStoreOwnerAndroidViewModel() {
+ val navController = createNavController()
+ navController.setViewModelStore(ViewModelStore())
+ val navGraph = navController.navigatorProvider.navigation(
+ id = 1,
+ startDestination = R.id.start_test
+ ) {
+ test(R.id.start_test)
+ }
+ navController.setGraph(navGraph, null)
+
+ val owner = navController.getViewModelStoreOwner(navGraph.id)
+ assertThat(owner).isNotNull()
+ val viewModelProvider = ViewModelProvider(owner)
+ val viewModel = viewModelProvider[TestAndroidViewModel::class.java]
+ assertThat(viewModel).isNotNull()
+ }
+
+ @Test
fun testSaveRestoreGetViewModelStoreOwner() {
val hostStore = ViewModelStore()
val navController = createNavController()
@@ -966,6 +988,8 @@
}
}
+class TestAndroidViewModel(application: Application) : AndroidViewModel(application)
+
/**
* [TestNavigator] that helps with testing saving and restoring state.
*/
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java
index 8aaa08c..0b9c4e5d 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java
@@ -16,10 +16,14 @@
package androidx.navigation;
+import android.app.Application;
+import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.lifecycle.HasDefaultViewModelProviderFactory;
+import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
@@ -28,7 +32,8 @@
/**
* Representation of an entry in the back stack of a {@link NavController}.
*/
-final class NavBackStackEntry implements ViewModelStoreOwner {
+final class NavBackStackEntry implements ViewModelStoreOwner, HasDefaultViewModelProviderFactory {
+ private final Context mContext;
private final NavDestination mDestination;
private final Bundle mArgs;
@@ -37,13 +42,17 @@
final UUID mId;
private NavControllerViewModel mNavControllerViewModel;
- NavBackStackEntry(@NonNull NavDestination destination, @Nullable Bundle args,
+ NavBackStackEntry(@NonNull Context context,
+ @NonNull NavDestination destination, @Nullable Bundle args,
@Nullable NavControllerViewModel navControllerViewModel) {
- this(UUID.randomUUID(), destination, args, navControllerViewModel);
+ this(context, destination, args, navControllerViewModel, UUID.randomUUID());
}
- NavBackStackEntry(@NonNull UUID uuid, @NonNull NavDestination destination,
- @Nullable Bundle args, @Nullable NavControllerViewModel navControllerViewModel) {
+ NavBackStackEntry(@NonNull Context context,
+ @NonNull NavDestination destination, @Nullable Bundle args,
+ @Nullable NavControllerViewModel navControllerViewModel,
+ @NonNull UUID uuid) {
+ mContext = context;
mId = uuid;
mDestination = destination;
mArgs = args;
@@ -77,4 +86,11 @@
public ViewModelStore getViewModelStore() {
return mNavControllerViewModel.getViewModelStore(mId);
}
+
+ @NonNull
+ @Override
+ public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+ return ViewModelProvider.AndroidViewModelFactory.getInstance(
+ (Application) mContext.getApplicationContext());
+ }
}
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java
new file mode 100644
index 0000000..a39a6e1
--- /dev/null
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java
@@ -0,0 +1,87 @@
+/*
+ * 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.navigation;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.UUID;
+
+@SuppressLint("BanParcelableUsage")
+final class NavBackStackEntryState implements Parcelable {
+
+ private final UUID mUUID;
+ private final int mDestinationId;
+ private final Bundle mArgs;
+
+ NavBackStackEntryState(NavBackStackEntry entry) {
+ mUUID = entry.mId;
+ mDestinationId = entry.getDestination().getId();
+ mArgs = entry.getArguments();
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ NavBackStackEntryState(Parcel in) {
+ mUUID = UUID.fromString(in.readString());
+ mDestinationId = in.readInt();
+ mArgs = in.readBundle(getClass().getClassLoader());
+ }
+
+ @NonNull
+ UUID getUUID() {
+ return mUUID;
+ }
+
+ int getDestinationId() {
+ return mDestinationId;
+ }
+
+ @Nullable
+ Bundle getArgs() {
+ return mArgs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int i) {
+ parcel.writeString(mUUID.toString());
+ parcel.writeInt(mDestinationId);
+ parcel.writeBundle(mArgs);
+ }
+
+ public static final Parcelable.Creator<NavBackStackEntryState> CREATOR =
+ new Parcelable.Creator<NavBackStackEntryState>() {
+ @Override
+ public NavBackStackEntryState createFromParcel(Parcel in) {
+ return new NavBackStackEntryState(in);
+ }
+
+ @Override
+ public NavBackStackEntryState[] newArray(int size) {
+ return new NavBackStackEntryState[size];
+ }
+ };
+}
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java
index 3c8c015..112e24e 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java
@@ -42,7 +42,6 @@
import java.util.Deque;
import java.util.Iterator;
import java.util.Map;
-import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -64,11 +63,8 @@
"android-support-nav:controller:navigatorState";
private static final String KEY_NAVIGATOR_STATE_NAMES =
"android-support-nav:controller:navigatorState:names";
- private static final String KEY_BACK_STACK_UUIDS =
- "android-support-nav:controller:backStackUUIDs";
- private static final String KEY_BACK_STACK_IDS = "android-support-nav:controller:backStackIds";
- private static final String KEY_BACK_STACK_ARGS =
- "android-support-nav:controller:backStackArgs";
+ private static final String KEY_BACK_STACK =
+ "android-support-nav:controller:backStack";
static final String KEY_DEEP_LINK_IDS = "android-support-nav:controller:deepLinkIds";
static final String KEY_DEEP_LINK_EXTRAS =
"android-support-nav:controller:deepLinkExtras";
@@ -85,9 +81,7 @@
private NavInflater mInflater;
private NavGraph mGraph;
private Bundle mNavigatorStateToRestore;
- private String[] mBackStackUUIDsToRestore;
- private int[] mBackStackIdsToRestore;
- private Parcelable[] mBackStackArgsToRestore;
+ private Parcelable[] mBackStackToRestore;
private boolean mDeepLinkHandled;
private final Deque<NavBackStackEntry> mBackStack = new ArrayDeque<>();
@@ -474,25 +468,23 @@
}
}
}
- if (mBackStackUUIDsToRestore != null) {
- for (int index = 0; index < mBackStackUUIDsToRestore.length; index++) {
- UUID uuid = UUID.fromString(mBackStackUUIDsToRestore[index]);
- int destinationId = mBackStackIdsToRestore[index];
- Bundle args = (Bundle) mBackStackArgsToRestore[index];
- NavDestination node = findDestination(destinationId);
+ if (mBackStackToRestore != null) {
+ for (Parcelable parcelable : mBackStackToRestore) {
+ NavBackStackEntryState state = (NavBackStackEntryState) parcelable;
+ NavDestination node = findDestination(state.getDestinationId());
if (node == null) {
throw new IllegalStateException("unknown destination during restore: "
- + mContext.getResources().getResourceName(destinationId));
+ + mContext.getResources().getResourceName(state.getDestinationId()));
}
+ Bundle args = state.getArgs();
if (args != null) {
args.setClassLoader(mContext.getClassLoader());
}
- mBackStack.add(new NavBackStackEntry(uuid, node, args, mViewModel));
+ mBackStack.add(new NavBackStackEntry(mContext, node, args, mViewModel,
+ state.getUUID()));
}
updateOnBackPressedCallbackEnabled();
- mBackStackUUIDsToRestore = null;
- mBackStackIdsToRestore = null;
- mBackStackArgsToRestore = null;
+ mBackStackToRestore = null;
}
if (mGraph != null && mBackStack.isEmpty()) {
boolean deepLinked = !mDeepLinkHandled && mActivity != null
@@ -873,7 +865,7 @@
}
// The mGraph should always be on the back stack after you navigate()
if (mBackStack.isEmpty()) {
- mBackStack.add(new NavBackStackEntry(mGraph, finalArgs, mViewModel));
+ mBackStack.add(new NavBackStackEntry(mContext, mGraph, finalArgs, mViewModel));
}
// Now ensure all intermediate NavGraphs are put on the back stack
// to ensure that global actions work.
@@ -882,13 +874,14 @@
while (destination != null && findDestination(destination.getId()) == null) {
NavGraph parent = destination.getParent();
if (parent != null) {
- hierarchy.addFirst(new NavBackStackEntry(parent, finalArgs, mViewModel));
+ hierarchy.addFirst(new NavBackStackEntry(mContext, parent, finalArgs,
+ mViewModel));
}
destination = parent;
}
mBackStack.addAll(hierarchy);
// And finally, add the new destination with its default args
- NavBackStackEntry newBackStackEntry = new NavBackStackEntry(newDest,
+ NavBackStackEntry newBackStackEntry = new NavBackStackEntry(mContext, newDest,
newDest.addInDefaultArgs(finalArgs), mViewModel);
mBackStack.add(newBackStackEntry);
}
@@ -971,18 +964,12 @@
if (b == null) {
b = new Bundle();
}
- String[] backStackUUIDs = new String[mBackStack.size()];
- int[] backStackIds = new int[mBackStack.size()];
- Parcelable[] backStackArgs = new Parcelable[mBackStack.size()];
+ Parcelable[] backStack = new Parcelable[mBackStack.size()];
int index = 0;
for (NavBackStackEntry backStackEntry : mBackStack) {
- backStackUUIDs[index] = backStackEntry.mId.toString();
- backStackIds[index] = backStackEntry.getDestination().getId();
- backStackArgs[index++] = backStackEntry.getArguments();
+ backStack[index++] = new NavBackStackEntryState(backStackEntry);
}
- b.putStringArray(KEY_BACK_STACK_UUIDS, backStackUUIDs);
- b.putIntArray(KEY_BACK_STACK_IDS, backStackIds);
- b.putParcelableArray(KEY_BACK_STACK_ARGS, backStackArgs);
+ b.putParcelableArray(KEY_BACK_STACK, backStack);
}
if (mDeepLinkHandled) {
if (b == null) {
@@ -1011,9 +998,7 @@
navState.setClassLoader(mContext.getClassLoader());
mNavigatorStateToRestore = navState.getBundle(KEY_NAVIGATOR_STATE);
- mBackStackUUIDsToRestore = navState.getStringArray(KEY_BACK_STACK_UUIDS);
- mBackStackIdsToRestore = navState.getIntArray(KEY_BACK_STACK_IDS);
- mBackStackArgsToRestore = navState.getParcelableArray(KEY_BACK_STACK_ARGS);
+ mBackStackToRestore = navState.getParcelableArray(KEY_BACK_STACK);
mDeepLinkHandled = navState.getBoolean(KEY_DEEP_LINK_HANDLED);
}
diff --git a/paging/common/api/3.0.0-alpha01.txt b/paging/common/api/3.0.0-alpha01.txt
index 9e3841a..0d44da6 100644
--- a/paging/common/api/3.0.0-alpha01.txt
+++ b/paging/common/api/3.0.0-alpha01.txt
@@ -71,7 +71,6 @@
public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
ctor public ItemKeyedDataSource.LoadCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data);
}
@@ -111,13 +110,11 @@
public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
ctor public PageKeyedDataSource.LoadCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
}
public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
ctor public PageKeyedDataSource.LoadInitialCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
}
@@ -331,7 +328,6 @@
public abstract static class PositionalDataSource.LoadInitialCallback<T> {
ctor public PositionalDataSource.LoadInitialCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
method public abstract void onResult(java.util.List<? extends T> data, int position);
}
@@ -346,7 +342,6 @@
public abstract static class PositionalDataSource.LoadRangeCallback<T> {
ctor public PositionalDataSource.LoadRangeCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends T> data);
}
diff --git a/paging/common/api/api_lint.ignore b/paging/common/api/api_lint.ignore
index a443331..dec4b46 100644
--- a/paging/common/api/api_lint.ignore
+++ b/paging/common/api/api_lint.ignore
@@ -1,22 +1,12 @@
// Baseline format: 1.0
DocumentExceptions: androidx.paging.DataSource#getExecutor():
Method DataSource.getExecutor appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.paging.ItemKeyedDataSource.LoadCallback#onError(Throwable):
- Method LoadCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.paging.PageKeyedDataSource.LoadCallback#onError(Throwable):
- Method LoadCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.paging.PageKeyedDataSource.LoadInitialCallback#onError(Throwable):
- Method LoadInitialCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
DocumentExceptions: androidx.paging.PagedList#loadAround(int):
Method PagedList.loadAround appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
DocumentExceptions: androidx.paging.PagedList.Config.Builder#build():
Method Builder.build appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
DocumentExceptions: androidx.paging.PagedList.Config.Builder#setPageSize(int):
Method Builder.setPageSize appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.paging.PositionalDataSource.LoadInitialCallback#onError(Throwable):
- Method LoadInitialCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.paging.PositionalDataSource.LoadRangeCallback#onError(Throwable):
- Method LoadRangeCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
EqualsAndHashCode: androidx.paging.DataSource.BaseResult#equals(Object):
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index 9e3841a..0d44da6 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -71,7 +71,6 @@
public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
ctor public ItemKeyedDataSource.LoadCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data);
}
@@ -111,13 +110,11 @@
public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
ctor public PageKeyedDataSource.LoadCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
}
public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
ctor public PageKeyedDataSource.LoadInitialCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
}
@@ -331,7 +328,6 @@
public abstract static class PositionalDataSource.LoadInitialCallback<T> {
ctor public PositionalDataSource.LoadInitialCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
method public abstract void onResult(java.util.List<? extends T> data, int position);
}
@@ -346,7 +342,6 @@
public abstract static class PositionalDataSource.LoadRangeCallback<T> {
ctor public PositionalDataSource.LoadRangeCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends T> data);
}
diff --git a/paging/common/api/restricted_3.0.0-alpha01.txt b/paging/common/api/restricted_3.0.0-alpha01.txt
index e0ce8bc..73c6e1a 100644
--- a/paging/common/api/restricted_3.0.0-alpha01.txt
+++ b/paging/common/api/restricted_3.0.0-alpha01.txt
@@ -75,7 +75,6 @@
public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
ctor public ItemKeyedDataSource.LoadCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data);
}
@@ -115,13 +114,11 @@
public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
ctor public PageKeyedDataSource.LoadCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
}
public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
ctor public PageKeyedDataSource.LoadInitialCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
}
@@ -365,7 +362,6 @@
public abstract static class PositionalDataSource.LoadInitialCallback<T> {
ctor public PositionalDataSource.LoadInitialCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
method public abstract void onResult(java.util.List<? extends T> data, int position);
}
@@ -380,7 +376,6 @@
public abstract static class PositionalDataSource.LoadRangeCallback<T> {
ctor public PositionalDataSource.LoadRangeCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends T> data);
}
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index e0ce8bc..73c6e1a 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -75,7 +75,6 @@
public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
ctor public ItemKeyedDataSource.LoadCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data);
}
@@ -115,13 +114,11 @@
public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
ctor public PageKeyedDataSource.LoadCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
}
public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
ctor public PageKeyedDataSource.LoadInitialCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
}
@@ -365,7 +362,6 @@
public abstract static class PositionalDataSource.LoadInitialCallback<T> {
ctor public PositionalDataSource.LoadInitialCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
method public abstract void onResult(java.util.List<? extends T> data, int position);
}
@@ -380,7 +376,6 @@
public abstract static class PositionalDataSource.LoadRangeCallback<T> {
ctor public PositionalDataSource.LoadRangeCallback();
- method public void onError(Throwable error);
method public abstract void onResult(java.util.List<? extends T> data);
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt
index 5acb252..969e289 100644
--- a/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt
@@ -23,7 +23,6 @@
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
/**
* Incremental data loader for paging keyed content, where loaded content uses previously loaded
@@ -177,21 +176,6 @@
* @param data List of items loaded from the [ItemKeyedDataSource].
*/
abstract fun onResult(data: List<Value>)
-
- /**
- * Called to report an error from a DataSource.
- *
- * Call this method to report an error from [loadInitial], [loadBefore], or [loadAfter]
- * methods.
- *
- * @param error The error that occurred during loading.
- */
- open fun onError(error: Throwable) {
- // TODO: remove default implementation in 3.0
- throw IllegalStateException(
- "You must implement onError if implementing your own load callback"
- )
- }
}
@Suppress("RedundantVisibilityModifier") // Metalava doesn't inherit visibility properly.
@@ -220,10 +204,6 @@
override fun onResult(data: List<Value>) {
cont.resume(InitialResult(data))
}
-
- override fun onError(error: Throwable) {
- cont.resumeWithException(error)
- }
})
}
@@ -346,8 +326,4 @@
override fun onResult(data: List<Value>) {
resume(Result(data))
}
-
- override fun onError(error: Throwable) {
- resumeWithException(error)
- }
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt
index 4d133ac..25d5f3e 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt
@@ -22,7 +22,6 @@
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
/**
* Incremental data loader for page-keyed content, where requests return keys for next/previous
@@ -170,20 +169,6 @@
* can be loaded after.
*/
abstract fun onResult(data: List<Value>, previousPageKey: Key?, nextPageKey: Key?)
-
- /**
- * Called to report an error from a DataSource.
- *
- * Call this method to report an error from [loadInitial].
- *
- * @param error The error that occurred during loading.
- */
- open fun onError(error: Throwable) {
- // TODO: remove default implementation in 3.0
- throw IllegalStateException(
- "You must implement onError if implementing your own load callback"
- )
- }
}
/**
@@ -219,21 +204,6 @@
* direction.
*/
abstract fun onResult(data: List<Value>, adjacentPageKey: Key?)
-
- /**
- * Called to report an error from a DataSource.
- *
- * Call this method to report an error from your PageKeyedDataSource's [loadBefore] and
- * [loadAfter] methods.
- *
- * @param error The error that occurred during loading.
- */
- open fun onError(error: Throwable) {
- // TODO: remove default implementation in 3.0
- throw IllegalStateException(
- "You must implement onError if implementing your own load callback"
- )
- }
}
/**
@@ -271,10 +241,6 @@
override fun onResult(data: List<Value>, previousPageKey: Key?, nextPageKey: Key?) {
cont.resume(InitialResult(data, previousPageKey, nextPageKey))
}
-
- override fun onError(error: Throwable) {
- cont.resumeWithException(error)
- }
})
}
@@ -377,8 +343,4 @@
override fun onResult(data: List<Value>, adjacentPageKey: Key?) {
resume(Result(data, adjacentPageKey))
}
-
- override fun onError(error: Throwable) {
- resumeWithException(error)
- }
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
index 4386bc7..6d56579 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
@@ -1127,8 +1127,8 @@
* Retry any retryable errors associated with this [PagedList].
*
* If for example a network [PagedSource] append timed out, calling this method will retry the
- * failed append load. Note that your [PagedSource] will need to pass `true` to `onError()` to
- * signify the error as retryable.
+ * failed append load. Note that your [PagedSource] will need to implement
+ * [PagedSource.isRetryableError] to return `true` for errors that are retryable.
*
* You can observe loading state via [addWeakLoadStateListener], though generally this is done
* through the [PagedListAdapter][androidx.paging.PagedListAdapter] or
diff --git a/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
index 6365c7c..64b9fb4 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
@@ -24,7 +24,6 @@
import androidx.paging.PositionalDataSource.LoadInitialCallback
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
/**
* Position-based data loader for a fixed-size, countable data set, supporting fixed-size loads at
@@ -182,20 +181,6 @@
* before the items in data that can be provided by this [DataSource], pass N.
*/
abstract fun onResult(data: List<T>, position: Int)
-
- /**
- * Called to report an error from a DataSource.
- *
- * Call this method to report an error from [loadInitial].
- *
- * @param error The error that occurred during loading.
- */
- open fun onError(error: Throwable) {
- // TODO: remove default implementation in 3.0
- throw IllegalStateException(
- "You must implement onError if implementing your own load callback"
- )
- }
}
/**
@@ -217,20 +202,6 @@
* unless at end of list.
*/
abstract fun onResult(data: List<T>)
-
- /**
- * Called to report an error from a [DataSource].
- *
- * Call this method to report an error from [loadRange].
- *
- * @param error The error that occurred during loading.
- */
- open fun onError(error: Throwable) {
- // TODO: remove default implementation in 3.0
- throw IllegalStateException(
- "You must implement onError if implementing your own load callback"
- )
- }
}
/**
@@ -424,10 +395,6 @@
}
}
- override fun onError(error: Throwable) {
- cont.resumeWithException(error)
- }
-
private fun resume(params: LoadInitialParams, result: InitialResult<T>) {
if (params.placeholdersEnabled) {
result.validateForInitialTiling(params.pageSize)
@@ -459,10 +426,6 @@
else -> cont.resume(RangeResult(data))
}
}
-
- override fun onError(error: Throwable) {
- cont.resumeWithException(error)
- }
})
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt
index 65047b9..386a370 100644
--- a/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt
@@ -54,7 +54,7 @@
}
override fun loadInitial(params: LoadInitialParams<K>, callback: LoadInitialCallback<B>) {
- source.loadInitial(params, object : ItemKeyedDataSource.LoadInitialCallback<A>() {
+ source.loadInitial(params, object : LoadInitialCallback<A>() {
override fun onResult(data: List<A>, position: Int, totalCount: Int) {
callback.onResult(convertWithStashedKeys(data), position, totalCount)
}
@@ -62,34 +62,22 @@
override fun onResult(data: List<A>) {
callback.onResult(convertWithStashedKeys(data))
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
override fun loadAfter(params: LoadParams<K>, callback: LoadCallback<B>) {
- source.loadAfter(params, object : ItemKeyedDataSource.LoadCallback<A>() {
+ source.loadAfter(params, object : LoadCallback<A>() {
override fun onResult(data: List<A>) {
callback.onResult(convertWithStashedKeys(data))
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
override fun loadBefore(params: LoadParams<K>, callback: LoadCallback<B>) {
- source.loadBefore(params, object : ItemKeyedDataSource.LoadCallback<A>() {
+ source.loadBefore(params, object : LoadCallback<A>() {
override fun onResult(data: List<A>) {
callback.onResult(convertWithStashedKeys(data))
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt
index c49b480..2f17e3a 100644
--- a/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt
@@ -34,7 +34,7 @@
override fun invalidate() = source.invalidate()
override fun loadInitial(params: LoadInitialParams<K>, callback: LoadInitialCallback<K, B>) {
- source.loadInitial(params, object : PageKeyedDataSource.LoadInitialCallback<K, A>() {
+ source.loadInitial(params, object : LoadInitialCallback<K, A>() {
override fun onResult(
data: List<A>,
position: Int,
@@ -50,26 +50,20 @@
val convertedData = convert(listFunction, data)
callback.onResult(convertedData, previousPageKey, nextPageKey)
}
-
- override fun onError(error: Throwable) = callback.onError(error)
})
}
override fun loadBefore(params: LoadParams<K>, callback: LoadCallback<K, B>) {
- source.loadBefore(params, object : PageKeyedDataSource.LoadCallback<K, A>() {
+ source.loadBefore(params, object : LoadCallback<K, A>() {
override fun onResult(data: List<A>, adjacentPageKey: K?) =
callback.onResult(convert(listFunction, data), adjacentPageKey)
-
- override fun onError(error: Throwable) = callback.onError(error)
})
}
override fun loadAfter(params: LoadParams<K>, callback: LoadCallback<K, B>) {
- source.loadAfter(params, object : PageKeyedDataSource.LoadCallback<K, A>() {
+ source.loadAfter(params, object : LoadCallback<K, A>() {
override fun onResult(data: List<A>, adjacentPageKey: K?) =
callback.onResult(convert(listFunction, data), adjacentPageKey)
-
- override fun onError(error: Throwable) = callback.onError(error)
})
}
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt
index 683d670..8308e87 100644
--- a/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt
@@ -40,16 +40,12 @@
override fun onResult(data: List<A>, position: Int) =
callback.onResult(convert(listFunction, data), position)
-
- override fun onError(error: Throwable) = callback.onError(error)
})
}
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<B>) {
source.loadRange(params, object : LoadRangeCallback<A>() {
override fun onResult(data: List<A>) = callback.onResult(convert(listFunction, data))
-
- override fun onError(error: Throwable) = callback.onError(error)
})
}
}
diff --git a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
index b847ea6..4068366 100644
--- a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
@@ -88,7 +88,6 @@
fun loadInitial_nullKey() = runBlocking {
val dataSource = ItemDataSource()
- // dispatchLoadInitial(null, count) == dispatchLoadInitial(count)
val result = loadInitial(dataSource, null, 10, true)
assertEquals(0, result.leadingNulls)
@@ -212,18 +211,10 @@
private val counted: Boolean = true,
private val items: List<Item> = ITEMS_BY_NAME_ID
) : ItemKeyedDataSource<Key, Item>() {
- private var error = false
-
override fun loadInitial(
params: LoadInitialParams<Key>,
callback: LoadInitialCallback<Item>
) {
- if (error) {
- callback.onError(EXCEPTION)
- error = false
- return
- }
-
val key = params.requestedInitialKey ?: Key("", Int.MAX_VALUE)
val start = maxOf(0, findFirstIndexAfter(key) - params.requestedLoadSize / 2)
val endExclusive = minOf(start + params.requestedLoadSize, items.size)
@@ -236,12 +227,6 @@
}
override fun loadAfter(params: LoadParams<Key>, callback: LoadCallback<Item>) {
- if (error) {
- callback.onError(EXCEPTION)
- error = false
- return
- }
-
val start = findFirstIndexAfter(params.key)
val endExclusive = minOf(start + params.requestedLoadSize, items.size)
@@ -249,12 +234,6 @@
}
override fun loadBefore(params: LoadParams<Key>, callback: LoadCallback<Item>) {
- if (error) {
- callback.onError(EXCEPTION)
- error = false
- return
- }
-
val firstIndexBefore = findFirstIndexBefore(params.key)
val endExclusive = maxOf(0, firstIndexBefore + 1)
val start = maxOf(0, firstIndexBefore - params.requestedLoadSize + 1)
@@ -277,10 +256,6 @@
KEY_COMPARATOR.compare(key, getKey(items[it])) > 0
} ?: -1
}
-
- fun enqueueError() {
- error = true
- }
}
private fun performLoadInitial(
@@ -408,10 +383,6 @@
override fun onResult(data: List<A>) {
callback.onResult(convert(data))
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
@@ -420,10 +391,6 @@
override fun onResult(data: List<A>) {
callback.onResult(convert(data))
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
@@ -432,10 +399,6 @@
override fun onResult(data: List<A>) {
callback.onResult(convert(data))
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
@@ -473,12 +436,8 @@
loadInitialCallback
)
verify(loadInitialCallback).onResult(
- ITEMS_BY_NAME_ID.subList(0, 10).map { DecoratedItem(it) })
- // error
- orig.enqueueError()
- wrapper.loadInitial(initParams, loadInitialCallback)
- verify(loadInitialCallback).onError(EXCEPTION)
- verifyNoMoreInteractions(loadInitialCallback)
+ ITEMS_BY_NAME_ID.subList(0, 10).map { DecoratedItem(it) }
+ )
val key = orig.getKey(ITEMS_BY_NAME_ID[20])
@Suppress("UNCHECKED_CAST")
@@ -487,22 +446,12 @@
// load after
wrapper.loadAfter(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
verify(loadCallback).onResult(ITEMS_BY_NAME_ID.subList(21, 31).map { DecoratedItem(it) })
- // load after - error
- orig.enqueueError()
- wrapper.loadAfter(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
- verify(loadCallback).onError(EXCEPTION)
- verifyNoMoreInteractions(loadCallback)
// load before
@Suppress("UNCHECKED_CAST")
loadCallback = mock()
wrapper.loadBefore(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
verify(loadCallback).onResult(ITEMS_BY_NAME_ID.subList(10, 20).map { DecoratedItem(it) })
- // load before - error
- orig.enqueueError()
- wrapper.loadBefore(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
- verify(loadCallback).onError(EXCEPTION)
- verifyNoMoreInteractions(loadCallback)
// verify invalidation
orig.invalidate()
@@ -555,7 +504,5 @@
(Math.random() * 200).toInt().toString() + " fake st."
)
}.sortedWith(ITEM_COMPARATOR)
-
- private val EXCEPTION = Exception()
}
}
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
index 9d795f8..af58970 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
@@ -57,7 +57,6 @@
callback: LoadInitialCallback<String, Item>
) {
if (error) {
- callback.onError(EXCEPTION)
error = false
return
}
@@ -68,7 +67,6 @@
override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<String, Item>) {
if (error) {
- callback.onError(EXCEPTION)
error = false
return
}
@@ -79,7 +77,6 @@
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<String, Item>) {
if (error) {
- callback.onError(EXCEPTION)
error = false
return
}
@@ -399,10 +396,6 @@
override fun onResult(data: List<A>, previousPageKey: K?, nextPageKey: K?) {
callback.onResult(convert(data), previousPageKey, nextPageKey)
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
@@ -411,10 +404,6 @@
override fun onResult(data: List<A>, adjacentPageKey: K?) {
callback.onResult(convert(data), adjacentPageKey)
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
@@ -423,10 +412,6 @@
override fun onResult(data: List<A>, adjacentPageKey: K?) {
callback.onResult(convert(data), adjacentPageKey)
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
@@ -471,7 +456,6 @@
// load after - error
orig.enqueueError()
wrapper.loadAfter(PageKeyedDataSource.LoadParams(expectedInitial.next, 4), loadCallback)
- verify(loadCallback).onError(EXCEPTION)
verifyNoMoreInteractions(loadCallback)
// load before
@@ -487,7 +471,6 @@
// load before - error
orig.enqueueError()
wrapper.loadBefore(PageKeyedDataSource.LoadParams(expectedAfter.prev, 4), loadCallback)
- verify(loadCallback).onError(EXCEPTION)
verifyNoMoreInteractions(loadCallback)
// verify invalidation
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
index 5146591b..4cad7b8 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
@@ -23,7 +23,6 @@
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
-import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -101,17 +100,14 @@
@Test
fun createAsyncThrow() {
- val dataSource = object : PositionalDataSource<String>() {
- override fun loadInitial(
- params: LoadInitialParams,
- callback: LoadInitialCallback<String>
- ) {
- callback.onError(Exception())
+ val pagedSource = object : PagedSource<Int, String>() {
+ override val keyProvider = KeyProvider.Positional<String>()
+
+ override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
+ throw Exception()
}
- override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
- fail("no load range expected")
- }
+ override fun isRetryableError(error: Throwable) = false
}
val config = PagedList.Config.Builder()
@@ -122,7 +118,7 @@
assertFails {
val job = testCoroutineScope.async(backgroundThread.asCoroutineDispatcher()) {
PagedList.create(
- PagedSourceWrapper(dataSource),
+ pagedSource,
testCoroutineScope,
mainThread,
backgroundThread,
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
new file mode 100644
index 0000000..3ef86fb
--- /dev/null
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.paging
+
+import androidx.paging.PagedSource.LoadParams
+import androidx.paging.PagedSource.LoadResult
+import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadType
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertFailsWith
+
+@RunWith(JUnit4::class)
+class PagedSourceTest {
+
+ // ----- STANDARD -----
+
+ private suspend fun loadInitial(
+ pagedSource: ItemDataSource,
+ key: Key?,
+ initialLoadSize: Int,
+ enablePlaceholders: Boolean
+ ): LoadResult<Key, Item> {
+ return pagedSource.load(
+ LoadParams(
+ LoadType.INITIAL,
+ key,
+ initialLoadSize,
+ enablePlaceholders,
+ 10
+ )
+ )
+ }
+
+ @Test
+ fun loadInitial() {
+ runBlocking {
+ val pagedSource = ItemDataSource()
+ val key = pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
+ val result = loadInitial(pagedSource, key, 10, true)
+
+ assertEquals(45, result.itemsBefore)
+ assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
+ assertEquals(45, result.itemsAfter)
+
+ // Verify error is propagated correctly.
+ pagedSource.enqueueError()
+ val errorParams = LoadParams(LoadType.INITIAL, key, 10, false, 10)
+ assertFailsWith<CustomException> {
+ pagedSource.load(errorParams)
+ }
+ }
+ }
+
+ @Test
+ fun loadInitial_keyMatchesSingleItem() = runBlocking {
+ val pagedSource = ItemDataSource(items = ITEMS_BY_NAME_ID.subList(0, 1))
+
+ // this is tricky, since load after and load before with the passed key will fail
+ val result =
+ loadInitial(pagedSource, pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[0]), 20, true)
+
+ assertEquals(0, result.itemsBefore)
+ assertEquals(ITEMS_BY_NAME_ID.subList(0, 1), result.data)
+ assertEquals(0, result.itemsAfter)
+ }
+
+ @Test
+ fun loadInitial_keyMatchesLastItem() = runBlocking {
+ val pagedSource = ItemDataSource()
+
+ // tricky, because load after key is empty, so another load before and load after required
+ val key = pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID.last())
+ val result = loadInitial(pagedSource, key, 20, true)
+
+ assertEquals(90, result.itemsBefore)
+ assertEquals(ITEMS_BY_NAME_ID.subList(90, 100), result.data)
+ assertEquals(0, result.itemsAfter)
+ }
+
+ @Test
+ fun loadInitial_nullKey() = runBlocking {
+ val dataSource = ItemDataSource()
+
+ val result = loadInitial(dataSource, null, 10, true)
+
+ assertEquals(0, result.itemsBefore)
+ assertEquals(ITEMS_BY_NAME_ID.subList(0, 10), result.data)
+ assertEquals(90, result.itemsAfter)
+ }
+
+ @Test
+ fun loadInitial_keyPastEndOfList() = runBlocking {
+ val dataSource = ItemDataSource()
+
+ // if key is past entire data set, should return last items in data set
+ val key = Key("fz", 0)
+ val result = loadInitial(dataSource, key, 10, true)
+
+ // NOTE: ideally we'd load 10 items here, but it adds complexity and unpredictability to
+ // do: load after was empty, so pass full size to load before, since this can incur larger
+ // loads than requested (see keyMatchesLastItem test)
+ assertEquals(95, result.itemsBefore)
+ assertEquals(ITEMS_BY_NAME_ID.subList(95, 100), result.data)
+ assertEquals(0, result.itemsAfter)
+ }
+
+ // ----- UNCOUNTED -----
+
+ @Test
+ fun loadInitial_disablePlaceholders() = runBlocking {
+ val dataSource = ItemDataSource()
+
+ // dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
+ val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
+ val result = loadInitial(dataSource, key, 10, false)
+
+ assertEquals(COUNT_UNDEFINED, result.itemsBefore)
+ assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
+ assertEquals(COUNT_UNDEFINED, result.itemsAfter)
+ }
+
+ @Test
+ fun loadInitial_uncounted() = runBlocking {
+ val dataSource = ItemDataSource(counted = false)
+
+ // dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
+ val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
+ val result = loadInitial(dataSource, key, 10, true)
+
+ assertEquals(COUNT_UNDEFINED, result.itemsBefore)
+ assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
+ assertEquals(COUNT_UNDEFINED, result.itemsAfter)
+ }
+
+ @Test
+ fun loadInitial_nullKey_uncounted() = runBlocking {
+ val dataSource = ItemDataSource(counted = false)
+
+ val result = loadInitial(dataSource, null, 10, true)
+
+ assertEquals(COUNT_UNDEFINED, result.itemsBefore)
+ assertEquals(ITEMS_BY_NAME_ID.subList(0, 10), result.data)
+ assertEquals(COUNT_UNDEFINED, result.itemsAfter)
+ }
+
+ // ----- EMPTY -----
+
+ @Test
+ fun loadInitial_empty() = runBlocking {
+ val dataSource = ItemDataSource(items = ArrayList())
+
+ // dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
+ val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
+ val result = loadInitial(dataSource, key, 10, true)
+
+ assertEquals(0, result.itemsBefore)
+ assertTrue(result.data.isEmpty())
+ assertEquals(0, result.itemsAfter)
+ }
+
+ @Test
+ fun loadInitial_nullKey_empty() = runBlocking {
+ val dataSource = ItemDataSource(items = ArrayList())
+ val result = loadInitial(dataSource, null, 10, true)
+
+ assertEquals(0, result.itemsBefore)
+ assertTrue(result.data.isEmpty())
+ assertEquals(0, result.itemsAfter)
+ }
+
+ // ----- Other behavior -----
+
+ @Test
+ fun loadBefore() {
+ val dataSource = ItemDataSource()
+
+ runBlocking {
+ val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[5])
+ val params = LoadParams(LoadType.START, key, 5, false, 5)
+ val observed = dataSource.load(params).data
+
+ assertEquals(ITEMS_BY_NAME_ID.subList(0, 5), observed)
+
+ // Verify error is propagated correctly.
+ dataSource.enqueueError()
+ assertFailsWith<CustomException> {
+ val errorParams = LoadParams(LoadType.START, key, 5, false, 5)
+ dataSource.load(errorParams)
+ }
+ }
+ }
+
+ @Test
+ fun loadAfter() {
+ val dataSource = ItemDataSource()
+
+ runBlocking {
+ val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[5])
+ val params = LoadParams(LoadType.END, key, 5, false, 5)
+ val observed = dataSource.load(params).data
+
+ assertEquals(ITEMS_BY_NAME_ID.subList(6, 11), observed)
+
+ // Verify error is propagated correctly.
+ dataSource.enqueueError()
+ assertFailsWith<CustomException> {
+ val errorParams = LoadParams(LoadType.END, key, 5, false, 5)
+ dataSource.load(errorParams)
+ }
+ }
+ }
+
+ internal data class Key(val name: String, val id: Int)
+
+ internal data class Item(
+ val name: String,
+ val id: Int,
+ val balance: Double,
+ val address: String
+ )
+
+ internal class ItemDataSource(
+ private val counted: Boolean = true,
+ private val items: List<Item> = ITEMS_BY_NAME_ID
+ ) : PagedSource<Key, Item>() {
+ private var error = false
+
+ override val keyProvider = object : KeyProvider.ItemKey<Key, Item>() {
+ override fun getKey(item: Item) = Key(item.name, item.id)
+ }
+
+ override suspend fun load(params: LoadParams<Key>): LoadResult<Key, Item> {
+ return when (params.loadType) {
+ LoadType.INITIAL -> loadInitial(params)
+ LoadType.START -> loadBefore(params)
+ LoadType.END -> loadAfter(params)
+ }
+ }
+
+ override fun isRetryableError(error: Throwable) = false
+
+ private fun loadInitial(params: LoadParams<Key>): LoadResult<Key, Item> {
+ if (error) {
+ error = false
+ throw EXCEPTION
+ }
+
+ val key = params.key ?: Key("", Int.MAX_VALUE)
+ val start = maxOf(0, findFirstIndexAfter(key) - params.loadSize / 2)
+ val endExclusive = minOf(start + params.loadSize, items.size)
+
+ return if (params.placeholdersEnabled && counted) {
+ val data = items.subList(start, endExclusive)
+ LoadResult(
+ itemsBefore = start,
+ itemsAfter = items.size - data.size - start,
+ data = data,
+ offset = start
+ )
+ } else {
+ LoadResult(data = items.subList(start, endExclusive), offset = 0)
+ }
+ }
+
+ private fun loadAfter(params: LoadParams<Key>): LoadResult<Key, Item> {
+ if (error) {
+ error = false
+ throw EXCEPTION
+ }
+
+ val start = findFirstIndexAfter(params.key!!)
+ val endExclusive = minOf(start + params.loadSize, items.size)
+
+ return LoadResult(data = items.subList(start, endExclusive), offset = 0)
+ }
+
+ private fun loadBefore(params: LoadParams<Key>): LoadResult<Key, Item> {
+ if (error) {
+ error = false
+ throw EXCEPTION
+ }
+
+ val firstIndexBefore = findFirstIndexBefore(params.key!!)
+ val endExclusive = maxOf(0, firstIndexBefore + 1)
+ val start = maxOf(0, firstIndexBefore - params.loadSize + 1)
+
+ return LoadResult(data = items.subList(start, endExclusive), offset = 0)
+ }
+
+ private fun findFirstIndexAfter(key: Key): Int {
+ return items.indices.firstOrNull {
+ KEY_COMPARATOR.compare(key, keyProvider.getKey(items[it])) < 0
+ } ?: items.size
+ }
+
+ private fun findFirstIndexBefore(key: Key): Int {
+ return items.indices.reversed().firstOrNull {
+ KEY_COMPARATOR.compare(key, keyProvider.getKey(items[it])) > 0
+ } ?: -1
+ }
+
+ fun enqueueError() {
+ error = true
+ }
+ }
+
+ class CustomException : Exception()
+
+ companion object {
+ private val ITEM_COMPARATOR = compareBy<Item> { it.name }.thenByDescending { it.id }
+ private val KEY_COMPARATOR = compareBy<Key> { it.name }.thenByDescending { it.id }
+
+ private val ITEMS_BY_NAME_ID = List(100) {
+ val names = Array(10) { index -> "f" + ('a' + index) }
+ Item(
+ names[it % 10],
+ it,
+ Math.random() * 1000,
+ (Math.random() * 200).toInt().toString() + " fake st."
+ )
+ }.sortedWith(ITEM_COMPARATOR)
+
+ private val EXCEPTION = CustomException()
+ }
+}
diff --git a/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
index 5c449fe..475fc4f 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
@@ -287,10 +287,6 @@
override fun onResult(data: List<A>, position: Int) {
callback.onResult(convert(data), position)
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
@@ -299,10 +295,6 @@
override fun onResult(data: List<A>) {
callback.onResult(convert(data))
}
-
- override fun onError(error: Throwable) {
- callback.onError(error)
- }
})
}
@@ -321,7 +313,6 @@
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
if (error) {
- callback.onError(ERROR)
error = false
return
}
@@ -338,7 +329,6 @@
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
if (error) {
- callback.onError(ERROR)
error = false
return
}
@@ -368,7 +358,6 @@
// load initial - error
orig.enqueueError()
wrapper.loadInitial(initParams, loadInitialCallback)
- verify(loadInitialCallback).onError(ERROR)
verifyNoMoreInteractions(loadInitialCallback)
// load range
@@ -380,7 +369,6 @@
// load range - error
orig.enqueueError()
wrapper.loadRange(PositionalDataSource.LoadRangeParams(2, 3), loadRangeCallback)
- verify(loadRangeCallback).onError(ERROR)
verifyNoMoreInteractions(loadRangeCallback)
// check invalidation behavior
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
index cc831a34..44b20ff 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
@@ -18,22 +18,30 @@
import android.graphics.Color
import androidx.annotation.ColorInt
-import androidx.paging.PositionalDataSource
+import androidx.paging.PagedSource
import java.util.ArrayList
import java.util.concurrent.atomic.AtomicBoolean
val dataSourceError = AtomicBoolean(false)
+
/**
* Sample data source with artificial data.
*/
-internal class ItemDataSource : PositionalDataSource<Item>() {
+internal class ItemDataSource : PagedSource<Int, Item>() {
+ override val keyProvider = KeyProvider.Positional<Item>()
+
+ override suspend fun load(params: LoadParams<Int>) = when (params.loadType) {
+ LoadType.INITIAL -> loadInitial(params)
+ else -> loadRange(params)
+ }
+
class RetryableItemError : Exception()
private val mGenerationId = sGenerationId++
- private fun loadRangeInternal(startPosition: Int, loadCount: Int): List<Item>? {
+ private fun loadRangeInternal(startPosition: Int, loadCount: Int): List<Item> {
val items = ArrayList<Item>()
- val end = Math.min(COUNT, startPosition + loadCount)
+ val end = minOf(COUNT, startPosition + loadCount)
val bgColor = COLORS[mGenerationId % COLORS.size]
Thread.sleep(1000)
@@ -45,41 +53,68 @@
items.add(Item(i, "item $i", bgColor))
}
if (dataSourceError.compareAndSet(true, false)) {
- return null
+ throw RetryableItemError()
}
return items
}
+ override fun isRetryableError(error: Throwable): Boolean {
+ return error is RetryableItemError
+ }
+
companion object {
private const val COUNT = 60
@ColorInt
private val COLORS = intArrayOf(Color.RED, Color.BLUE, Color.BLACK)
-
private var sGenerationId: Int = 0
}
- override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<Item>) {
- val position = computeInitialLoadPosition(params, COUNT)
- val loadSize = computeInitialLoadSize(params, position, COUNT)
+ // TODO: Clean up logic only pertinent to tiling.
+ private fun computeStartPosition(params: LoadParams<Int>): Int {
+ val requestedStartPosition = params.key?.let { key ->
+ var initialPosition = key
+
+ if (params.placeholdersEnabled) {
+ // snap load size to page multiple (minimum two)
+ val initialLoadSize = maxOf(params.loadSize / params.pageSize, 2) * params.pageSize
+
+ // move start so the load is centered around the key, not starting at it
+ val idealStart = initialPosition - initialLoadSize / 2
+ initialPosition = maxOf(0, idealStart / params.pageSize * params.pageSize)
+ } else {
+ // not tiled, so don't try to snap or force multiple of a page size
+ initialPosition -= params.loadSize / 2
+ }
+
+ initialPosition
+ } ?: 0
+
+ var pageStart = requestedStartPosition / params.pageSize * params.pageSize
+
+ // maximum start pos is that which will encompass end of list
+ val maximumLoadPage =
+ (COUNT - params.loadSize + params.pageSize - 1) / params.pageSize * params.pageSize
+ pageStart = minOf(maximumLoadPage, pageStart)
+
+ // minimum start position is 0
+ return maxOf(0, pageStart)
+ }
+
+ private fun loadInitial(params: LoadParams<Int>): LoadResult<Int, Item> {
+ val position = computeStartPosition(params)
+ val loadSize = minOf(COUNT - position, params.loadSize)
val data = loadRangeInternal(position, loadSize)
- if (data == null) {
- callback.onError(RetryableItemError())
- } else {
- callback.onResult(data, position, COUNT)
- }
+ return LoadResult(
+ itemsBefore = position,
+ itemsAfter = COUNT - data.size - position,
+ data = data,
+ offset = 0
+ )
}
- override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<Item>) {
- val data = loadRangeInternal(params.startPosition, params.loadSize)
- if (data == null) {
- callback.onError(RetryableItemError())
- } else {
- callback.onResult(data)
- }
- }
-
- override fun isRetryableError(error: Throwable): Boolean {
- return error is RetryableItemError
+ private fun loadRange(params: LoadParams<Int>): LoadResult<Int, Item> {
+ val data = loadRangeInternal(params.key ?: 0, params.loadSize)
+ return LoadResult(data = data, offset = 0)
}
}
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListItemViewModel.java b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListItemViewModel.java
index 5f2c5f3..31263ea 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListItemViewModel.java
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListItemViewModel.java
@@ -16,12 +16,16 @@
package androidx.paging.integration.testapp.custom;
+import android.annotation.SuppressLint;
+
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
-import androidx.paging.DataSource;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;
+import androidx.paging.PagedSource;
+
+import kotlin.jvm.functions.Function0;
/**
* Sample ViewModel backed by an artificial data source
@@ -30,22 +34,22 @@
private ItemDataSource mDataSource;
private final Object mDataSourceLock = new Object();
- private final DataSource.Factory<Integer, Item> mFactory =
- new DataSource.Factory<Integer, Item>() {
- @NonNull
- @Override
- public DataSource<Integer, Item> create() {
- ItemDataSource newDataSource = new ItemDataSource();
- synchronized (mDataSourceLock) {
- mDataSource = newDataSource;
- return mDataSource;
- }
- }
- };
+ private final Function0<PagedSource<Integer, Item>> mFactory =
+ new Function0<PagedSource<Integer, Item>>() {
+ @SuppressLint("SyntheticAccessor")
+ @NonNull
+ @Override
+ public PagedSource<Integer, Item> invoke() {
+ ItemDataSource newDataSource = new ItemDataSource();
+ synchronized (mDataSourceLock) {
+ mDataSource = newDataSource;
+ return mDataSource;
+ }
+ }
+ };
private LiveData<PagedList<Item>> mLivePagedList =
- new LivePagedListBuilder<>(mFactory, 10)
- .build();
+ new LivePagedListBuilder<>(mFactory, 10).build();
void invalidateList() {
synchronized (mDataSourceLock) {
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListActivity.java b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListActivity.java
index 9f3d041..501ec82 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListActivity.java
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListActivity.java
@@ -43,8 +43,7 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_room_recycler_view);
// TODO use by viewModels() once this class switches to Kotlin
- final CustomerViewModel viewModel = new ViewModelProvider(this,
- ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()))
+ final CustomerViewModel viewModel = new ViewModelProvider(this)
.get(CustomerViewModel.class);
mRecyclerView = findViewById(R.id.recyclerview);
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListRxActivity.java b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListRxActivity.java
index 54fe3bf..359c9b9 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListRxActivity.java
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListRxActivity.java
@@ -41,8 +41,7 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_view);
// TODO use by viewModels() once this class switches to Kotlin
- mViewModel = new ViewModelProvider(this,
- ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()))
+ mViewModel = new ViewModelProvider(this)
.get(CustomerViewModel.class);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
index 303a62f..d10028a 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
@@ -80,9 +80,9 @@
ArchTaskExecutor.getInstance().setDelegate(null)
}
- class MockDataSourceFactory : DataSource.Factory<Int, String>() {
- override fun create(): DataSource<Int, String> {
- return MockDataSource()
+ class MockDataSourceFactory {
+ fun create(): PagedSource<Int, String> {
+ return MockPagedSource()
}
var throwable: Throwable? = null
@@ -91,28 +91,35 @@
throwable = RETRYABLE_EXCEPTION
}
- private inner class MockDataSource : PositionalDataSource<String>() {
- override fun loadInitial(
- params: LoadInitialParams,
- callback: LoadInitialCallback<String>
- ) {
+ private inner class MockPagedSource : PagedSource<Int, String>() {
+ override val keyProvider = KeyProvider.Positional<String>()
+
+ override suspend fun load(params: LoadParams<Int>) = when (params.loadType) {
+ LoadType.INITIAL -> loadInitial(params)
+ else -> loadRange()
+ }
+
+ override fun isRetryableError(error: Throwable) = error === RETRYABLE_EXCEPTION
+
+ private fun loadInitial(params: LoadParams<Int>): LoadResult<Int, String> {
assertEquals(2, params.pageSize)
- if (throwable != null) {
-
- callback.onError(throwable!!)
+ throwable?.let { error ->
throwable = null
- } else {
- callback.onResult(listOf("a", "b"), 0, 4)
+ throw error
}
+
+ val data = listOf("a", "b")
+ return LoadResult(
+ itemsBefore = 0,
+ itemsAfter = 4 - data.size,
+ data = data,
+ offset = 0
+ )
}
- override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
- callback.onResult(listOf("c", "d"))
- }
-
- override fun isRetryableError(error: Throwable): Boolean {
- return error === RETRYABLE_EXCEPTION
+ private fun loadRange(): LoadResult<Int, String> {
+ return LoadResult(data = listOf("c", "d"), offset = 0)
}
}
}
@@ -121,8 +128,7 @@
fun executorBehavior() {
// specify a background executor via builder, and verify it gets used for all loads,
// overriding default arch IO executor
- @Suppress("DEPRECATION")
- val livePagedList = LivePagedListBuilder(MockDataSourceFactory(), 2)
+ val livePagedList = LivePagedListBuilder(MockDataSourceFactory()::create, 2)
.setFetchExecutor(backgroundExecutor)
.build()
@@ -161,8 +167,7 @@
val factory = MockDataSourceFactory()
factory.enqueueRetryableError()
- @Suppress("DEPRECATION")
- val livePagedList = LivePagedListBuilder(factory, 2)
+ val livePagedList = LivePagedListBuilder(factory::create, 2)
.setFetchExecutor(backgroundExecutor)
.build()
diff --git a/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt b/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt
index 304af35..39754e2 100644
--- a/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt
+++ b/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt
@@ -68,7 +68,7 @@
* @Override
* public void onCreate(Bundle savedState) {
* super.onCreate(savedState);
- * MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
+ * MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
* RecyclerView recyclerView = findViewById(R.id.user_list);
* final UserAdapter adapter = new UserAdapter();
* viewModel.usersList.observe(this, pagedList -> adapter.submitList(pagedList));
diff --git a/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt b/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt
index e3849ad..a40db41 100644
--- a/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt
+++ b/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt
@@ -59,7 +59,7 @@
* @Override
* public void onCreate(Bundle savedState) {
* super.onCreate(savedState);
- * MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
+ * MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
* RecyclerView recyclerView = findViewById(R.id.user_list);
* UserAdapter<User> adapter = new UserAdapter();
* viewModel.usersList.observe(this, pagedList -> adapter.submitList(pagedList));
diff --git a/palette/palette/build.gradle b/palette/palette/build.gradle
index 1a785db..87eb258b 100644
--- a/palette/palette/build.gradle
+++ b/palette/palette/build.gradle
@@ -9,7 +9,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.1.0")
annotationProcessor(NULLAWAY)
diff --git a/percentlayout/percentlayout/build.gradle b/percentlayout/percentlayout/build.gradle
index 5b2ac05..e5c8470 100644
--- a/percentlayout/percentlayout/build.gradle
+++ b/percentlayout/percentlayout/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api(ANDROIDX_ANNOTATION)
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/preference/build.gradle b/preference/build.gradle
index 43b0f07..8235c7a 100644
--- a/preference/build.gradle
+++ b/preference/build.gradle
@@ -30,7 +30,7 @@
implementation("androidx.annotation:annotation:1.1.0")
api("androidx.appcompat:appcompat:1.1.0-rc01")
// TODO: change to alpha05 after release
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.0.0")
api("androidx.fragment:fragment:1.1.0-rc01")
api("androidx.recyclerview:recyclerview:1.0.0")
diff --git a/recyclerview/recyclerview-benchmark/build.gradle b/recyclerview/recyclerview-benchmark/build.gradle
index 985934e..8b18e14 100644
--- a/recyclerview/recyclerview-benchmark/build.gradle
+++ b/recyclerview/recyclerview-benchmark/build.gradle
@@ -19,6 +19,7 @@
id("AndroidXPlugin")
id("com.android.library")
id("kotlin-android")
+ id("androidx.benchmark")
}
dependencies {
diff --git a/recyclerview/recyclerview-selection/build.gradle b/recyclerview/recyclerview-selection/build.gradle
index ac2f787..a364be0 100644
--- a/recyclerview/recyclerview-selection/build.gradle
+++ b/recyclerview/recyclerview-selection/build.gradle
@@ -27,7 +27,7 @@
dependencies {
api(project(":recyclerview:recyclerview"))
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index 16d7734..4ec3faf 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -11,7 +11,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc02")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.0.0")
api("androidx.customview:customview:1.0.0")
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerExtraLayoutSpaceTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerExtraLayoutSpaceTest.java
index 40e4916..70b1225 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerExtraLayoutSpaceTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerExtraLayoutSpaceTest.java
@@ -304,7 +304,7 @@
mRecordExtraLayoutSpace = false;
mRecordedExtraLayoutSpace[0] = extraLayoutSpace[0];
mRecordedExtraLayoutSpace[1] = extraLayoutSpace[1];
- getViewTreeObserver().addOnDrawListener(mLayoutRecorder);
+ getViewTreeObserver().addOnPreDrawListener(mLayoutRecorder);
}
}
@@ -316,7 +316,7 @@
}
}
- class LayoutBoundsRecorder implements ViewTreeObserver.OnDrawListener {
+ class LayoutBoundsRecorder implements ViewTreeObserver.OnPreDrawListener {
private final OrientationHelper mHelper;
private final int[][] mBounds;
@@ -331,16 +331,17 @@
}
@Override
- public void onDraw() {
+ public boolean onPreDraw() {
if (!mHasRecorded) {
recordBounds();
mRecyclerView.post(new Runnable() {
@Override
public void run() {
- getViewTreeObserver().removeOnDrawListener(LayoutBoundsRecorder.this);
+ getViewTreeObserver().removeOnPreDrawListener(LayoutBoundsRecorder.this);
}
});
}
+ return true;
}
private void recordBounds() {
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java
index 1c345be03..c1fdb87 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java
@@ -67,7 +67,7 @@
* {@literal @}Override
* public void onCreate(Bundle savedState) {
* super.onCreate(savedState);
- * MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
+ * MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
* RecyclerView recyclerView = findViewById(R.id.user_list);
* UserAdapter adapter = new UserAdapter();
* viewModel.usersList.observe(this, list -> adapter.submitList(list));
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/ListAdapter.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/ListAdapter.java
index d00e84c..6b1ad73 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/ListAdapter.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/ListAdapter.java
@@ -50,7 +50,7 @@
* {@literal @}Override
* public void onCreate(Bundle savedState) {
* super.onCreate(savedState);
- * MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
+ * MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
* RecyclerView recyclerView = findViewById(R.id.user_list);
* UserAdapter<User> adapter = new UserAdapter();
* viewModel.usersList.observe(this, list -> adapter.submitList(list));
diff --git a/room/benchmark/build.gradle b/room/benchmark/build.gradle
index 966d074..b13c3b9 100644
--- a/room/benchmark/build.gradle
+++ b/room/benchmark/build.gradle
@@ -20,6 +20,7 @@
id("com.android.library")
id("kotlin-android")
id("kotlin-kapt")
+ id("androidx.benchmark")
}
dependencies {
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
index 0c30d5c..4e926c7 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
@@ -132,8 +132,7 @@
setContentView(getLayoutId());
// TODO use by viewModels() once this class switches to Kotlin
- mViewModel = new ViewModelProvider(this,
- new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
+ mViewModel = new ViewModelProvider(this).get(MyViewModel.class);
mViewModel.refreshDone.observe(this, event -> {
if (event.getContentIfNotHandled() != null) {
mSwipeRefreshWidget.setRefreshing(false);
diff --git a/sharetarget/build.gradle b/sharetarget/build.gradle
index b750a70..cc27624 100644
--- a/sharetarget/build.gradle
+++ b/sharetarget/build.gradle
@@ -25,7 +25,7 @@
}
dependencies {
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.0.0")
api(GUAVA_LISTENABLE_FUTURE)
implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha02")
diff --git a/slices/benchmark/build.gradle b/slices/benchmark/build.gradle
index ecb41ad..f7cb0db 100644
--- a/slices/benchmark/build.gradle
+++ b/slices/benchmark/build.gradle
@@ -21,6 +21,7 @@
plugins {
id("AndroidXPlugin")
id("com.android.library")
+ id("androidx.benchmark")
}
dependencies {
diff --git a/slices/builders/build.gradle b/slices/builders/build.gradle
index 914633c..63bbe8d 100644
--- a/slices/builders/build.gradle
+++ b/slices/builders/build.gradle
@@ -28,7 +28,7 @@
implementation(project(":slice-core"))
api(project(":remotecallback"))
implementation "androidx.annotation:annotation:1.1.0"
- implementation "androidx.core:core:1.1.0-rc01"
+ implementation "androidx.core:core:1.1.0"
implementation project(':collection:collection')
}
diff --git a/slidingpanelayout/build.gradle b/slidingpanelayout/build.gradle
index b9732d5..7738665 100644
--- a/slidingpanelayout/build.gradle
+++ b/slidingpanelayout/build.gradle
@@ -9,7 +9,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
api(project(":customview"))
}
diff --git a/swiperefreshlayout/build.gradle b/swiperefreshlayout/build.gradle
index 1309851..46c3da4 100644
--- a/swiperefreshlayout/build.gradle
+++ b/swiperefreshlayout/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
api("androidx.interpolator:interpolator:1.0.0")
androidTestImplementation(JUNIT)
diff --git a/textclassifier/api/1.0.0-alpha03.txt b/textclassifier/api/1.0.0-alpha03.txt
index 974299f..21567f3 100644
--- a/textclassifier/api/1.0.0-alpha03.txt
+++ b/textclassifier/api/1.0.0-alpha03.txt
@@ -2,7 +2,7 @@
package androidx.textclassifier {
public final class ConversationAction {
- method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle!);
+ method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle);
method public androidx.core.app.RemoteActionCompat? getAction();
method @FloatRange(from=0, to=1) public float getConfidenceScore();
method public android.os.Bundle getExtras();
diff --git a/textclassifier/api/current.txt b/textclassifier/api/current.txt
index 974299f..21567f3 100644
--- a/textclassifier/api/current.txt
+++ b/textclassifier/api/current.txt
@@ -2,7 +2,7 @@
package androidx.textclassifier {
public final class ConversationAction {
- method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle!);
+ method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle);
method public androidx.core.app.RemoteActionCompat? getAction();
method @FloatRange(from=0, to=1) public float getConfidenceScore();
method public android.os.Bundle getExtras();
diff --git a/textclassifier/api/restricted_1.0.0-alpha03.txt b/textclassifier/api/restricted_1.0.0-alpha03.txt
index e335921..2bfef3d 100644
--- a/textclassifier/api/restricted_1.0.0-alpha03.txt
+++ b/textclassifier/api/restricted_1.0.0-alpha03.txt
@@ -2,7 +2,7 @@
package androidx.textclassifier {
public final class ConversationAction {
- method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle!);
+ method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle);
method public androidx.core.app.RemoteActionCompat? getAction();
method @FloatRange(from=0, to=1) public float getConfidenceScore();
method public android.os.Bundle getExtras();
diff --git a/textclassifier/api/restricted_current.txt b/textclassifier/api/restricted_current.txt
index e335921..2bfef3d 100644
--- a/textclassifier/api/restricted_current.txt
+++ b/textclassifier/api/restricted_current.txt
@@ -2,7 +2,7 @@
package androidx.textclassifier {
public final class ConversationAction {
- method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle!);
+ method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle);
method public androidx.core.app.RemoteActionCompat? getAction();
method @FloatRange(from=0, to=1) public float getConfidenceScore();
method public android.os.Bundle getExtras();
diff --git a/textclassifier/build.gradle b/textclassifier/build.gradle
index d2cd121..92508eb 100644
--- a/textclassifier/build.gradle
+++ b/textclassifier/build.gradle
@@ -13,7 +13,7 @@
api("androidx.annotation:annotation:1.1.0")
implementation("androidx.collection:collection:1.0.0")
// TODO: change to 1.1.0-alpha04 after release
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/textclassifier/src/main/java/androidx/textclassifier/ConversationAction.java b/textclassifier/src/main/java/androidx/textclassifier/ConversationAction.java
index 9aa9288..866e49c 100644
--- a/textclassifier/src/main/java/androidx/textclassifier/ConversationAction.java
+++ b/textclassifier/src/main/java/androidx/textclassifier/ConversationAction.java
@@ -32,7 +32,12 @@
import java.lang.annotation.Retention;
-/** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
+/**
+ * Represents an action suggested by a {@link TextClassifier} on a given conversation.
+ *
+ * @see TextClassifier#suggestConversationActions(ConversationActions.Request)
+ * @see ConversationActions
+ */
public final class ConversationAction {
private static final String EXTRA_TYPE = "type";
@@ -214,7 +219,7 @@
* Converts a bundle that was created using {@link #toBundle()} to a {@link ConversationAction}.
*/
@NonNull
- public static ConversationAction createFromBundle(Bundle bundle) {
+ public static ConversationAction createFromBundle(@NonNull Bundle bundle) {
return new ConversationAction(
bundle.getString(EXTRA_TYPE),
(RemoteActionCompat) ParcelUtils.getVersionedParcelable(bundle, EXTRA_ACTION),
diff --git a/textclassifier/src/main/java/androidx/textclassifier/ConversationActions.java b/textclassifier/src/main/java/androidx/textclassifier/ConversationActions.java
index d7129dc..3ba6f20 100644
--- a/textclassifier/src/main/java/androidx/textclassifier/ConversationActions.java
+++ b/textclassifier/src/main/java/androidx/textclassifier/ConversationActions.java
@@ -36,6 +36,8 @@
/**
* Represents a list of actions suggested by a {@link TextClassifier} on a given conversation.
+ * <p>
+ * This is an object to store the result of {@link TextClassifier#suggestConversationActions(Request)}.
*
* @see TextClassifier#suggestConversationActions(Request)
*/
diff --git a/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java b/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java
index 26f0c7f..45c481f 100644
--- a/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java
+++ b/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java
@@ -226,18 +226,18 @@
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
- static void updateRectCoordinates(Rect rect, TextView textView, int start, int end) {
- final int[] startXY = getCoordinates(textView, start);
- final int[] endXY = getCoordinates(textView, end);
+ static void updateRectCoordinates(Rect rect, TextView textView, int startIndex, int endIndex) {
+ final int[] startXY = getCoordinates(textView, startIndex, /* startCoordinate= */ true);
+ final int[] endXY = getCoordinates(textView, endIndex, /* startCoordinate= */false);
rect.set(startXY[0], startXY[1], endXY[0], endXY[1]);
rect.sort();
}
- private static int[] getCoordinates(TextView textView, int index) {
+ private static int[] getCoordinates(TextView textView, int index, boolean startCoordinate) {
final Layout layout = textView.getLayout();
final int line = layout.getLineForOffset(index);
final int x = (int) layout.getPrimaryHorizontal(index);
- final int y = layout.getLineTop(line);
+ final int y = (startCoordinate) ? layout.getLineTop(line) : layout.getLineBottom(line);
final int[] xy = new int[2];
textView.getLocationOnScreen(xy);
return new int[]{
diff --git a/tv-provider/build.gradle b/tv-provider/build.gradle
index b4cbc11..902b0d5 100644
--- a/tv-provider/build.gradle
+++ b/tv-provider/build.gradle
@@ -10,7 +10,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/ui/integration-tests/benchmark/build.gradle b/ui/integration-tests/benchmark/build.gradle
index 43ecf2c..ab94dc5 100644
--- a/ui/integration-tests/benchmark/build.gradle
+++ b/ui/integration-tests/benchmark/build.gradle
@@ -24,6 +24,7 @@
id("com.android.library")
id("AndroidXUiPlugin")
id("org.jetbrains.kotlin.android")
+ id("androidx.benchmark")
}
dependencies {
diff --git a/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt b/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
index fefbde1..bc6446b 100644
--- a/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
+++ b/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
@@ -231,9 +231,8 @@
}
testCase.recomposeSyncAssertHadChanges()
}
+ activity.disposeComposition()
}
-
- activity.disposeComposition()
}
/**
@@ -255,9 +254,8 @@
}
testCase.measure()
}
+ activity.disposeComposition()
}
-
- activity.disposeComposition()
}
/**
@@ -280,9 +278,8 @@
}
testCase.layout()
}
+ activity.disposeComposition()
}
-
- activity.disposeComposition()
}
/**
@@ -310,7 +307,6 @@
testCase.finishDraw()
}
}
+ activity.disposeComposition()
}
-
- activity.disposeComposition()
}
diff --git a/ui/ui-foundation/api/1.0.0-alpha01.txt b/ui/ui-foundation/api/1.0.0-alpha01.txt
index 26ee3a8..5edc08e 100644
--- a/ui/ui-foundation/api/1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/1.0.0-alpha01.txt
@@ -17,6 +17,11 @@
method public static void DeterminateProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
+ public final class DialogKt {
+ ctor public DialogKt();
+ method public static void Dialog(kotlin.jvm.functions.Function0<kotlin.Unit> onCloseRequest, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ }
+
public final class SimpleImageKt {
ctor public SimpleImageKt();
method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
diff --git a/ui/ui-foundation/api/current.txt b/ui/ui-foundation/api/current.txt
index 26ee3a8..5edc08e 100644
--- a/ui/ui-foundation/api/current.txt
+++ b/ui/ui-foundation/api/current.txt
@@ -17,6 +17,11 @@
method public static void DeterminateProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
+ public final class DialogKt {
+ ctor public DialogKt();
+ method public static void Dialog(kotlin.jvm.functions.Function0<kotlin.Unit> onCloseRequest, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ }
+
public final class SimpleImageKt {
ctor public SimpleImageKt();
method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
diff --git a/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt b/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
index 26ee3a8..5edc08e 100644
--- a/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
@@ -17,6 +17,11 @@
method public static void DeterminateProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
+ public final class DialogKt {
+ ctor public DialogKt();
+ method public static void Dialog(kotlin.jvm.functions.Function0<kotlin.Unit> onCloseRequest, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ }
+
public final class SimpleImageKt {
ctor public SimpleImageKt();
method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
diff --git a/ui/ui-foundation/api/restricted_current.txt b/ui/ui-foundation/api/restricted_current.txt
index 26ee3a8..5edc08e 100644
--- a/ui/ui-foundation/api/restricted_current.txt
+++ b/ui/ui-foundation/api/restricted_current.txt
@@ -17,6 +17,11 @@
method public static void DeterminateProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
+ public final class DialogKt {
+ ctor public DialogKt();
+ method public static void Dialog(kotlin.jvm.functions.Function0<kotlin.Unit> onCloseRequest, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ }
+
public final class SimpleImageKt {
ctor public SimpleImageKt();
method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
diff --git a/ui/ui-foundation/build.gradle b/ui/ui-foundation/build.gradle
index e2568bd..105aab3 100644
--- a/ui/ui-foundation/build.gradle
+++ b/ui/ui-foundation/build.gradle
@@ -40,6 +40,7 @@
implementation project(":ui:ui-animation")
implementation project(':ui:ui-framework')
implementation project(':ui:ui-layout')
+ implementation project(':ui:ui-platform')
implementation project(':ui:ui-text')
testImplementation(ANDROIDX_TEST_RULES)
@@ -48,6 +49,7 @@
androidTestImplementation project(':ui:ui-test')
+ androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
androidTestImplementation(ANDROIDX_TEST_RULES)
androidTestImplementation(ANDROIDX_TEST_RUNNER)
androidTestImplementation(JUNIT)
diff --git a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/DialogSample.kt b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/DialogSample.kt
new file mode 100644
index 0000000..2c01062c
--- /dev/null
+++ b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/DialogSample.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.foundation.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.Composable
+import androidx.compose.composer
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.Text
+import androidx.ui.foundation.Dialog
+
+@Sampled
+@Composable
+fun DialogSample() {
+ val openDialog = +state { true }
+
+ if (openDialog.value) {
+ Dialog(onCloseRequest = { openDialog.value = false }) {
+ Text("This is a Dialog. Click outside to dismiss.")
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/DialogUiTest.kt b/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/DialogUiTest.kt
new file mode 100644
index 0000000..0bfd0af
--- /dev/null
+++ b/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/DialogUiTest.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.ui.foundation
+
+import androidx.test.filters.MediumTest
+import androidx.ui.test.createComposeRule
+import androidx.compose.composer
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import androidx.ui.core.Text
+import androidx.ui.test.assertDoesNotExist
+import androidx.ui.test.assertIsVisible
+import androidx.ui.test.doClick
+import androidx.ui.test.findByText
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@MediumTest
+@RunWith(JUnit4::class)
+class DialogUiTest {
+ @get:Rule
+ val composeTestRule = createComposeRule(disableTransitions = true)
+
+ private val defaultText = "dialogText"
+
+ @Test
+ fun dialogTest_isShowingContent() {
+ composeTestRule.setContent {
+ val showDialog = +state { true }
+
+ if (showDialog.value) {
+ Dialog(onCloseRequest = {}) {
+ Text(defaultText)
+ }
+ }
+ }
+
+ findByText(defaultText).assertIsVisible()
+ }
+
+ @Test
+ fun dialogTest_isNotDismissed_whenClicked() {
+ val textBeforeClick = "textBeforeClick"
+ val textAfterClick = "textAfterClick"
+
+ composeTestRule.setContent {
+ val showDialog = +state { true }
+ val text = +state { textBeforeClick }
+
+ if (showDialog.value) {
+ Dialog(onCloseRequest = {
+ showDialog.value = false
+ }) {
+ Clickable(onClick = { text.value = textAfterClick }) {
+ Text(text = text.value)
+ }
+ }
+ }
+ }
+
+ findByText(textBeforeClick).assertIsVisible()
+
+ // Click inside the dialog
+ findByText(textBeforeClick).doClick()
+
+ // Check that the Clickable was pressed and that the Dialog is still visible
+ findByText(textAfterClick).assertIsVisible()
+ }
+
+ @Test
+ fun dialogTest_isDismissed_whenSpecified() {
+ composeTestRule.setContent {
+ val showDialog = +state { true }
+
+ if (showDialog.value) {
+ Dialog(onCloseRequest = { showDialog.value = false }) {
+ Text(defaultText)
+ }
+ }
+ }
+
+ findByText(defaultText).assertIsVisible()
+
+ // Click outside the dialog to dismiss it
+ val outsideX = 0
+ val outsideY = composeTestRule.displayMetrics.heightPixels / 2
+ UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
+
+ assertDoesNotExist { label.equals(defaultText) }
+ }
+
+ @Test
+ fun dialogTest_isNotDismissed_whenNotSpecified() {
+ composeTestRule.setContent {
+ val showDialog = +state { true }
+
+ if (showDialog.value) {
+ Dialog(onCloseRequest = {}) {
+ Text(defaultText)
+ }
+ }
+ }
+
+ findByText(defaultText).assertIsVisible()
+
+ // Click outside the dialog to try to dismiss it
+ val outsideX = 0
+ val outsideY = composeTestRule.displayMetrics.heightPixels / 2
+ UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
+
+ // The Dialog should still be visible
+ findByText(defaultText).assertIsVisible()
+ }
+
+ @Test
+ fun dialogTest_isDismissed_whenSpecified_backButtonPressed() {
+ composeTestRule.setContent {
+ val showDialog = +state { true }
+
+ if (showDialog.value) {
+ Dialog(onCloseRequest = { showDialog.value = false }) {
+ Text(defaultText)
+ }
+ }
+ }
+
+ findByText(defaultText).assertIsVisible()
+
+ // Click the back button to dismiss the Dialog
+ UiDevice.getInstance(getInstrumentation()).pressBack()
+
+ assertDoesNotExist { label.equals(defaultText) }
+ }
+
+ @Test
+ fun dialogTest_isNotDismissed_whenNotSpecified_backButtonPressed() {
+ composeTestRule.setContent {
+ val showDialog = +state { true }
+
+ if (showDialog.value) {
+ Dialog(onCloseRequest = {}) {
+ Text(defaultText)
+ }
+ }
+ }
+
+ findByText(defaultText).assertIsVisible()
+
+ // Click the back button to try to dismiss the dialog
+ UiDevice.getInstance(getInstrumentation()).pressBack()
+
+ // The Dialog should still be visible
+ findByText(defaultText).assertIsVisible()
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
new file mode 100644
index 0000000..8ccc5b6
--- /dev/null
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.foundation
+
+import android.app.Dialog
+import android.content.Context
+import android.view.MotionEvent
+import android.widget.FrameLayout
+import androidx.compose.Children
+import androidx.compose.Composable
+import androidx.compose.ambient
+import androidx.compose.disposeComposition
+import androidx.compose.memo
+import androidx.compose.onActive
+import androidx.compose.onCommit
+import androidx.compose.onDispose
+import androidx.compose.unaryPlus
+import androidx.ui.core.ContextAmbient
+import androidx.ui.core.setContent
+
+/**
+ * Opens a dialog with the given content.
+ *
+ * The dialog is visible as long as it is part of the composition hierarchy.
+ * In order to let the user dismiss the Dialog, the implementation of [onCloseRequest] should
+ * contain a way to remove to remove the dialog from the composition hierarchy.
+ *
+ * Example usage:
+ *
+ * @sample androidx.ui.foundation.samples.DialogSample
+ *
+ * @param onCloseRequest Executes when the user tries to dismiss the Dialog.
+ * @param children The content to be displayed inside the dialog.
+ */
+@Composable
+fun Dialog(onCloseRequest: () -> Unit, @Children children: @Composable() () -> Unit) {
+ val context = +ambient(ContextAmbient)
+
+ val dialog = +memo { DialogWrapper(context, onCloseRequest) }
+
+ +onActive {
+ dialog.show()
+
+ onDispose {
+ dialog.dismiss()
+ dialog.disposeComposition()
+ }
+ }
+
+ +onCommit {
+ dialog.setContent(children)
+ }
+}
+
+private class DialogWrapper(context: Context, val onCloseRequest: () -> Unit) : Dialog(context) {
+ val frameLayout = FrameLayout(context)
+ init {
+ setContentView(frameLayout)
+ }
+
+ fun setContent(@Children children: @Composable() () -> Unit) {
+ frameLayout.setContent(children)
+ }
+
+ fun disposeComposition() {
+ frameLayout.disposeComposition()
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ val result = super.onTouchEvent(event)
+ if (result) {
+ onCloseRequest()
+ }
+
+ return result
+ }
+
+ override fun cancel() {
+ // Prevents the dialog from dismissing itself
+ return
+ }
+
+ override fun onBackPressed() {
+ onCloseRequest()
+ }
+}
diff --git a/ui/ui-framework/api/1.0.0-alpha01.txt b/ui/ui-framework/api/1.0.0-alpha01.txt
index b53ffe0..c7820fa 100644
--- a/ui/ui-framework/api/1.0.0-alpha01.txt
+++ b/ui/ui-framework/api/1.0.0-alpha01.txt
@@ -296,16 +296,16 @@
package androidx.ui.core.selection {
public final class Selection {
- ctor public Selection(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
- method public androidx.ui.engine.geometry.Rect component1();
- method public androidx.ui.engine.geometry.Rect component2();
+ ctor public Selection(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.PxPosition component1();
+ method public androidx.ui.core.PxPosition component2();
method public androidx.ui.core.LayoutCoordinates? component3();
method public androidx.ui.core.LayoutCoordinates? component4();
- method public androidx.ui.core.selection.Selection copy(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.selection.Selection copy(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.PxPosition getEndCoordinates();
method public androidx.ui.core.LayoutCoordinates? getEndLayoutCoordinates();
- method public androidx.ui.engine.geometry.Rect getEndOffset();
+ method public androidx.ui.core.PxPosition getStartCoordinates();
method public androidx.ui.core.LayoutCoordinates? getStartLayoutCoordinates();
- method public androidx.ui.engine.geometry.Rect getStartOffset();
}
public final class SelectionContainerKt {
diff --git a/ui/ui-framework/api/current.txt b/ui/ui-framework/api/current.txt
index b53ffe0..c7820fa 100644
--- a/ui/ui-framework/api/current.txt
+++ b/ui/ui-framework/api/current.txt
@@ -296,16 +296,16 @@
package androidx.ui.core.selection {
public final class Selection {
- ctor public Selection(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
- method public androidx.ui.engine.geometry.Rect component1();
- method public androidx.ui.engine.geometry.Rect component2();
+ ctor public Selection(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.PxPosition component1();
+ method public androidx.ui.core.PxPosition component2();
method public androidx.ui.core.LayoutCoordinates? component3();
method public androidx.ui.core.LayoutCoordinates? component4();
- method public androidx.ui.core.selection.Selection copy(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.selection.Selection copy(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.PxPosition getEndCoordinates();
method public androidx.ui.core.LayoutCoordinates? getEndLayoutCoordinates();
- method public androidx.ui.engine.geometry.Rect getEndOffset();
+ method public androidx.ui.core.PxPosition getStartCoordinates();
method public androidx.ui.core.LayoutCoordinates? getStartLayoutCoordinates();
- method public androidx.ui.engine.geometry.Rect getStartOffset();
}
public final class SelectionContainerKt {
diff --git a/ui/ui-framework/api/restricted_1.0.0-alpha01.txt b/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
index b53ffe0..c7820fa 100644
--- a/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
@@ -296,16 +296,16 @@
package androidx.ui.core.selection {
public final class Selection {
- ctor public Selection(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
- method public androidx.ui.engine.geometry.Rect component1();
- method public androidx.ui.engine.geometry.Rect component2();
+ ctor public Selection(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.PxPosition component1();
+ method public androidx.ui.core.PxPosition component2();
method public androidx.ui.core.LayoutCoordinates? component3();
method public androidx.ui.core.LayoutCoordinates? component4();
- method public androidx.ui.core.selection.Selection copy(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.selection.Selection copy(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.PxPosition getEndCoordinates();
method public androidx.ui.core.LayoutCoordinates? getEndLayoutCoordinates();
- method public androidx.ui.engine.geometry.Rect getEndOffset();
+ method public androidx.ui.core.PxPosition getStartCoordinates();
method public androidx.ui.core.LayoutCoordinates? getStartLayoutCoordinates();
- method public androidx.ui.engine.geometry.Rect getStartOffset();
}
public final class SelectionContainerKt {
diff --git a/ui/ui-framework/api/restricted_current.txt b/ui/ui-framework/api/restricted_current.txt
index b53ffe0..c7820fa 100644
--- a/ui/ui-framework/api/restricted_current.txt
+++ b/ui/ui-framework/api/restricted_current.txt
@@ -296,16 +296,16 @@
package androidx.ui.core.selection {
public final class Selection {
- ctor public Selection(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
- method public androidx.ui.engine.geometry.Rect component1();
- method public androidx.ui.engine.geometry.Rect component2();
+ ctor public Selection(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.PxPosition component1();
+ method public androidx.ui.core.PxPosition component2();
method public androidx.ui.core.LayoutCoordinates? component3();
method public androidx.ui.core.LayoutCoordinates? component4();
- method public androidx.ui.core.selection.Selection copy(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.selection.Selection copy(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+ method public androidx.ui.core.PxPosition getEndCoordinates();
method public androidx.ui.core.LayoutCoordinates? getEndLayoutCoordinates();
- method public androidx.ui.engine.geometry.Rect getEndOffset();
+ method public androidx.ui.core.PxPosition getStartCoordinates();
method public androidx.ui.core.LayoutCoordinates? getStartLayoutCoordinates();
- method public androidx.ui.engine.geometry.Rect getStartOffset();
}
public final class SelectionContainerKt {
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
index 6da979c..7e1680cb 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
@@ -285,13 +285,12 @@
onSelectionChange = { internalSelection.value = it },
textPainter = textPainter
)
-
if (!textSelectionProcessor.isSelected) return null
// TODO(qqd): Determine a set of coordinates around a character that we need.
return Selection(
- startOffset = textSelectionProcessor.startOffset,
- endOffset = textSelectionProcessor.endOffset,
+ startCoordinates = textSelectionProcessor.startCoordinates,
+ endCoordinates = textSelectionProcessor.endCoordinates,
startLayoutCoordinates =
if (textSelectionProcessor.containsWholeSelectionStart) {
layoutCoordinates.value!!
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/Selection.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/Selection.kt
index f4c753a..6e2dd35 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/Selection.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/Selection.kt
@@ -17,24 +17,30 @@
package androidx.ui.core.selection
import androidx.ui.core.LayoutCoordinates
-import androidx.ui.engine.geometry.Rect
+import androidx.ui.core.PxPosition
/**
* Data class of Selection.
*/
data class Selection(
/**
- * A box around the character at the start offset as Rect. This box' height is the line height,
- * and the width is the advance. Note: It is temporary to use Rect.
+ * The coordinates of the graphical position for selection start character offset.
+ *
+ * This graphical position is the point at the left bottom corner for LTR
+ * character, or right bottom corner for RTL character.
+ *
+ * This coordinates is in child widget coordinates system.
*/
- // TODO(qqd): After solving the problem of getting the coordinates of a character, figure out
- // what should the startOffset and endOffset should be.
- val startOffset: Rect,
+ val startCoordinates: PxPosition,
/**
- * A box around the character at the end offset as Rect. This box' height is the line height,
- * and the width is the advance. Note: It is temporary to use Rect.
+ * The coordinates of the graphical position for selection end character offset.
+ *
+ * This graphical position is the point at the left bottom corner for LTR
+ * character, or right bottom corner for RTL character.
+ *
+ * This coordinates is in child widget coordinates system.
*/
- val endOffset: Rect,
+ val endCoordinates: PxPosition,
/**
* The layout coordinates of the child which contains the start of the selection. If the child
* does not contain the start of the selection, this should be null.
@@ -51,13 +57,13 @@
var currentSelection = this.copy()
if (other.startLayoutCoordinates != null) {
currentSelection = currentSelection.copy(
- startOffset = other.startOffset,
+ startCoordinates = other.startCoordinates,
startLayoutCoordinates = other.startLayoutCoordinates
)
}
if (other.endLayoutCoordinates != null) {
currentSelection = currentSelection.copy(
- endOffset = other.endOffset,
+ endCoordinates = other.endCoordinates,
endLayoutCoordinates = other.endLayoutCoordinates
)
}
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
index 54f078c..a3bd5e5 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
@@ -25,11 +25,9 @@
import androidx.ui.core.IntPx
import androidx.ui.core.Layout
import androidx.ui.core.OnPositioned
-import androidx.ui.core.PxPosition
import androidx.ui.core.gesture.DragGestureDetector
import androidx.ui.core.gesture.PressIndicatorGestureDetector
import androidx.ui.core.ipx
-import androidx.ui.core.px
import androidx.ui.core.round
/**
@@ -129,17 +127,11 @@
) {
val startOffset = manager.containerLayoutCoordinates.childToLocal(
selection.startLayoutCoordinates,
- PxPosition(
- selection.startOffset.left.px,
- selection.startOffset.bottom.px
- )
+ selection.startCoordinates
)
val endOffset = manager.containerLayoutCoordinates.childToLocal(
selection.endLayoutCoordinates,
- PxPosition(
- selection.endOffset.right.px,
- selection.endOffset.bottom.px
- )
+ selection.endCoordinates
)
start.place(startOffset.x - HANDLE_WIDTH, startOffset.y)
end.place(endOffset.x, endOffset.y)
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt
index 99e2fa3..d7edee4 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt
@@ -21,7 +21,6 @@
import androidx.ui.core.PxPosition
import androidx.ui.core.gesture.DragObserver
import androidx.ui.core.px
-import androidx.ui.engine.geometry.Rect
internal class SelectionManager : SelectionRegistrar {
/**
@@ -99,11 +98,15 @@
onSelectionChange(result)
}
- // Get the coordinates of a character. Currently, it's the middle point of the left edge of the
- // bounding box of the character. This is a temporary solution.
- // TODO(qqd): Read how Android solve this problem.
- fun getCoordinatesForCharacter(box: Rect): PxPosition {
- return PxPosition(box.left.px, box.top.px + (box.bottom.px - box.top.px) / 2)
+ /**
+ * Adjust coordinates for given text offset.
+ *
+ * Currently [android.text.Layout.getLineBottom] returns y coordinates of the next
+ * line's top offset, which is not included in current line's hit area. To be able to
+ * hit current line, move up this y coordinates by 1 pixel.
+ */
+ fun getAdjustedCoordinates(p: PxPosition): PxPosition {
+ return PxPosition(p.x, p.y - 1.px)
}
fun handleDragObserver(dragStartHandle: Boolean): DragObserver {
@@ -121,11 +124,11 @@
// The position of the character where the drag gesture should begin. This is in
// the widget coordinates.
val beginCoordinates =
- getCoordinatesForCharacter(
+ getAdjustedCoordinates(
if (dragStartHandle) {
- selection!!.startOffset
+ selection!!.startCoordinates
} else {
- selection!!.endOffset
+ selection!!.endCoordinates
}
)
// Convert the position where drag gesture begins from widget coordinates to
@@ -150,7 +153,7 @@
} else {
containerLayoutCoordinates.childToLocal(
selection!!.startLayoutCoordinates!!,
- getCoordinatesForCharacter(selection!!.startOffset)
+ getAdjustedCoordinates(selection!!.startCoordinates)
)
}
@@ -158,7 +161,7 @@
if (dragStartHandle) {
containerLayoutCoordinates.childToLocal(
selection!!.endLayoutCoordinates!!,
- getCoordinatesForCharacter(selection!!.endOffset)
+ getAdjustedCoordinates(selection!!.endCoordinates)
)
} else {
dragBeginPosition + dragTotalDistance
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/TextSelectionProcessor.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/TextSelectionProcessor.kt
index 70819c7..23419ed 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/TextSelectionProcessor.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/TextSelectionProcessor.kt
@@ -18,7 +18,6 @@
import androidx.ui.core.PxPosition
import androidx.ui.core.px
-import androidx.ui.engine.geometry.Rect
import androidx.ui.text.TextSelection
import androidx.ui.text.TextPainter
import kotlin.math.max
@@ -38,21 +37,24 @@
/** The TextPainter object from Text widget. */
val textPainter: TextPainter
) {
- // TODO(qqd): Determine a set of coordinates around a character that we need.
/**
- * The bounding box of the character at the start character offset as Rect. The bounding box
- * includes the top, bottom, left, and right of the character. Note: It is temporary to use
- * Rect.
+ * The coordinates of the graphical position for selection start character offset.
+ *
+ * This graphical position is the point at the left bottom corner for LTR
+ * character, or right bottom corner for RTL character.
+ *
+ * This coordinates is in child widget coordinates system.
*/
- // TODO(qqd): After solving the problem of getting the coordinates of a character, figure out
- // what should the startOffset and endOffset should be.
- internal var startOffset = Rect.zero
+ internal var startCoordinates: PxPosition = PxPosition.Origin
/**
- * The bounding box of the character at the end character offset as Rect. The bounding box
- * includes the top, bottom, left, and right of the character. Note: It is temporary to use
- * Rect.
+ * The coordinates of the graphical position for selection end character offset.
+ *
+ * This graphical position is the point at the left bottom corner for LTR
+ * character, or right bottom corner for RTL character.
+ *
+ * This coordinates is in child widget coordinates system.
*/
- internal var endOffset = Rect.zero
+ internal var endCoordinates: PxPosition = PxPosition.Origin
/**
* A flag to check if the text widget contains the whole selection's start.
*/
@@ -95,22 +97,12 @@
val wordBoundary = textPainter.getWordBoundary(textSelectionStart)
textSelectionStart = wordBoundary.start
textSelectionEnd = wordBoundary.end
- } else {
- // Currently the implementation of selection is inclusive-inclusive which is a temporary
- // workaround, but inclusive-exclusive in Android. Thus before calling drawing selection
- // background, make the selection matches Android behaviour.
- textSelectionEnd = textSelectionEnd + 1
}
onSelectionChange(TextSelection(textSelectionStart, textSelectionEnd))
- // Currently the implementation of selection is inclusive-inclusive which is a temporary
- // workaround, but inclusive-exclusive in Android. Thus make the selection end matches Crane
- // behaviour.
- textSelectionEnd = textSelectionEnd - 1
-
- startOffset = textPainter.getBoundingBox(textSelectionStart)
- endOffset = textPainter.getBoundingBox(textSelectionEnd)
+ startCoordinates = getSelectionHandleCoordinates(textSelectionStart)
+ endCoordinates = getSelectionHandleCoordinates(textSelectionEnd)
this.containsWholeSelectionStart = containsWholeSelectionStart
this.containsWholeSelectionEnd = containsWholeSelectionEnd
@@ -155,4 +147,13 @@
}
return Pair(selectionBorder, containsWholeSelectionBorder)
}
+
+ private fun getSelectionHandleCoordinates(offset: Int): PxPosition {
+ val left = textPainter.getPrimaryHorizontal(offset)
+
+ val line = textPainter.getLineForOffset(offset)
+ val bottom = textPainter.getLineBottom(line)
+
+ return PxPosition(left.px, bottom.px)
+ }
}
diff --git a/ui/ui-layout/build.gradle b/ui/ui-layout/build.gradle
index d467d30..2af0b7e 100644
--- a/ui/ui-layout/build.gradle
+++ b/ui/ui-layout/build.gradle
@@ -27,6 +27,7 @@
id("com.android.library")
id("AndroidXUiPlugin")
id("org.jetbrains.kotlin.android")
+ id("androidx.benchmark")
}
dependencies {
diff --git a/ui/ui-text/api/1.0.0-alpha01.txt b/ui/ui-text/api/1.0.0-alpha01.txt
index b3eed96..59a09a0 100644
--- a/ui/ui-text/api/1.0.0-alpha01.txt
+++ b/ui/ui-text/api/1.0.0-alpha01.txt
@@ -78,7 +78,6 @@
public interface Paragraph {
method public float getBaseline();
- method public androidx.ui.engine.geometry.Rect getBoundingBox(int offset);
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
method public boolean getDidExceedMaxLines();
method public float getHeight();
diff --git a/ui/ui-text/api/current.txt b/ui/ui-text/api/current.txt
index b3eed96..59a09a0 100644
--- a/ui/ui-text/api/current.txt
+++ b/ui/ui-text/api/current.txt
@@ -78,7 +78,6 @@
public interface Paragraph {
method public float getBaseline();
- method public androidx.ui.engine.geometry.Rect getBoundingBox(int offset);
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
method public boolean getDidExceedMaxLines();
method public float getHeight();
diff --git a/ui/ui-text/api/restricted_1.0.0-alpha01.txt b/ui/ui-text/api/restricted_1.0.0-alpha01.txt
index e44dfa9..cb841ce 100644
--- a/ui/ui-text/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-text/api/restricted_1.0.0-alpha01.txt
@@ -92,7 +92,6 @@
public interface Paragraph {
method public float getBaseline();
- method public androidx.ui.engine.geometry.Rect getBoundingBox(int offset);
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
method public boolean getDidExceedMaxLines();
method public float getHeight();
diff --git a/ui/ui-text/api/restricted_current.txt b/ui/ui-text/api/restricted_current.txt
index e44dfa9..cb841ce 100644
--- a/ui/ui-text/api/restricted_current.txt
+++ b/ui/ui-text/api/restricted_current.txt
@@ -92,7 +92,6 @@
public interface Paragraph {
method public float getBaseline();
- method public androidx.ui.engine.geometry.Rect getBoundingBox(int offset);
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
method public boolean getDidExceedMaxLines();
method public float getHeight();
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/Paragraph.kt b/ui/ui-text/src/main/java/androidx/ui/text/Paragraph.kt
index 145b6ac..a6a8156 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/Paragraph.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/Paragraph.kt
@@ -15,6 +15,7 @@
*/
package androidx.ui.text
+import androidx.annotation.RestrictTo
import androidx.ui.core.Density
import androidx.ui.core.PxPosition
import androidx.ui.engine.geometry.Rect
@@ -103,19 +104,47 @@
/** Returns the right x Coordinate of the given line. */
fun getLineRight(lineIndex: Int): Float
+ /**
+ * Returns the bottom y coordinate of the given line.
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun getLineBottom(lineIndex: Int): Float
+
/** Returns the height of the given line. */
fun getLineHeight(lineIndex: Int): Float
/** Returns the width of the given line. */
fun getLineWidth(lineIndex: Int): Float
+ /**
+ * Returns the line number on which the specified text offset appears.
+ * If you ask for a position before 0, you get 0; if you ask for a position
+ * beyond the end of the text, you get the last line.
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun getLineForOffset(offset: Int): Int
+
+ /**
+ * Get the primary horizontal position for the specified text offset.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun getPrimaryHorizontal(offset: Int): Float
+
/** Returns the character offset closest to the given graphical position. */
fun getOffsetForPosition(position: PxPosition): Int
/**
* Returns the bounding box as Rect of the character for given character offset. Rect
* includes the top, bottom, left and right of a character.
+ *
+ * @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
fun getBoundingBox(offset: Int): Rect
/**
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt b/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt
index 67e1eed..7880c2d 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt
@@ -471,6 +471,41 @@
canvas.drawRect(cursorRect, Paint().apply { this.color = Color.Black })
}
+ /**
+ * Returns the bottom y coordinate of the given line.
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun getLineBottom(lineIndex: Int): Float {
+ assert(!needsLayout)
+ return paragraph!!.getLineBottom(lineIndex)
+ }
+
+ /**
+ * Returns the line number on which the specified text offset appears.
+ * If you ask for a position before 0, you get 0; if you ask for a position
+ * beyond the end of the text, you get the last line.
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun getLineForOffset(offset: Int): Int {
+ assert(!needsLayout)
+ return paragraph!!.getLineForOffset(offset)
+ }
+
+ /**
+ * Get the primary horizontal position for the specified text offset.
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun getPrimaryHorizontal(offset: Int): Float {
+ assert(!needsLayout)
+ return paragraph!!.getPrimaryHorizontal(offset)
+ }
+
/** Returns the character offset closest to the given graphical position. */
fun getOffsetForPosition(position: PxPosition): Int {
assert(!needsLayout)
@@ -503,4 +538,4 @@
assert(!needsLayout)
return paragraph!!.getWordBoundary(offset)
}
-}
\ No newline at end of file
+}
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraph.kt b/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraph.kt
index 7ed0a23..054eb01 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraph.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraph.kt
@@ -267,10 +267,17 @@
override fun getLineRight(lineIndex: Int): Float = ensureLayout.getLineRight(lineIndex)
+ override fun getLineBottom(lineIndex: Int): Float = ensureLayout.getLineBottom(lineIndex)
+
override fun getLineHeight(lineIndex: Int): Float = ensureLayout.getLineHeight(lineIndex)
override fun getLineWidth(lineIndex: Int): Float = ensureLayout.getLineWidth(lineIndex)
+ override fun getLineForOffset(offset: Int): Int = ensureLayout.getLineForOffset(offset)
+
+ override fun getPrimaryHorizontal(offset: Int): Float =
+ ensureLayout.getPrimaryHorizontal(offset)
+
/**
* @return true if the given line is ellipsized, else false.
*/
diff --git a/viewpager/build.gradle b/viewpager/build.gradle
index 2559de8..f1f7e65 100644
--- a/viewpager/build.gradle
+++ b/viewpager/build.gradle
@@ -17,7 +17,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
api(project(":customview"))
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/viewpager2/build.gradle b/viewpager2/build.gradle
index 9de6aa7..29ea26d 100644
--- a/viewpager2/build.gradle
+++ b/viewpager2/build.gradle
@@ -27,7 +27,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- implementation("androidx.core:core:1.1.0-rc01")
+ implementation("androidx.core:core:1.1.0")
api("androidx.fragment:fragment:1.1.0-rc01")
api("androidx.recyclerview:recyclerview:1.1.0-beta01")
implementation("androidx.collection:collection:1.1.0")
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
new file mode 100644
index 0000000..4a3337c
--- /dev/null
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.viewpager2.widget
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit.MILLISECONDS
+
+/**
+ * Verifies that [androidx.viewpager2.adapter.FragmentStateAdapter] can handle [Fragment]s
+ * having transient state.
+ */
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class TransientStateFragmentTest : BaseTest() {
+ private val orientation = ORIENTATION_HORIZONTAL
+ private val totalPages = 10
+ private val adapterProvider = fragmentAdapterProviderValueId
+ private val timeoutMs = 3000L
+
+ @Test
+ fun test_swipeBetweenPages() {
+ setUpTest(orientation).apply {
+ val expectedValues = stringSequence(totalPages)
+ val adapter = adapterProvider(expectedValues)
+
+ val fragmentManager = activity.supportFragmentManager
+
+ val transientStateCallback = createTransientStateCallback()
+ fragmentManager.registerFragmentLifecycleCallbacks(transientStateCallback, false)
+ setAdapterSync(adapter)
+
+ assertBasicState(0)
+ listOf(1, 0, 1, 2, 3, 4, 3).plus(4 until totalPages).forEach { target ->
+ val latch = viewPager.addWaitForIdleLatch()
+ swipe(viewPager.currentItem, target)
+ latch.await(timeoutMs, MILLISECONDS)
+ assertBasicState(target)
+ }
+
+ fragmentManager.unregisterFragmentLifecycleCallbacks(transientStateCallback)
+ }
+ }
+
+ private fun createTransientStateCallback(): FragmentManager.FragmentLifecycleCallbacks {
+ return object : FragmentManager.FragmentLifecycleCallbacks() {
+ override fun onFragmentViewCreated(
+ fm: FragmentManager,
+ f: Fragment,
+ v: View,
+ savedInstanceState: Bundle?
+ ) {
+ v.setHasTransientState(true)
+ }
+ }
+ }
+}
diff --git a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java b/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
index 435762c..a07954e 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
@@ -395,12 +395,20 @@
@Override
public final boolean onFailedToRecycleView(@NonNull FragmentViewHolder holder) {
- // This happens when a ViewHolder is in a transient state (e.g. during custom
- // animation). We don't have sufficient information on how to clear up what lead to
- // the transient state, so we are throwing away the ViewHolder to stay on the
- // conservative side.
- onViewRecycled(holder); // the same clean-up steps as when recycling a ViewHolder
- return false; // don't recycle the view
+ /*
+ This happens when a ViewHolder is in a transient state (e.g. during an
+ animation).
+
+ Our ViewHolders are effectively just FrameLayout instances in which we put Fragment
+ Views, so it's safe to force recycle them. This is because:
+ - FrameLayout instances are not to be directly manipulated, so no animations are
+ expected to be running directly on them.
+ - Fragment Views are not reused between position (one Fragment = one page). Animation
+ running in one of the Fragment Views won't affect another Fragment View.
+ - If a user chooses to violate these assumptions, they are also in the position to
+ correct the state in their code.
+ */
+ return true;
}
private void removeFragment(long itemId) {
diff --git a/webkit/build.gradle b/webkit/build.gradle
index 8c0c18d..622bf29 100644
--- a/webkit/build.gradle
+++ b/webkit/build.gradle
@@ -26,7 +26,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.1.0-rc01")
+ api("androidx.core:core:1.1.0")
androidTestImplementation(OKHTTP_MOCKWEBSERVER)
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
index db29999..f5a667b 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
@@ -395,6 +395,48 @@
assertResponse(response, FakeTextPathHandler.CONTENTS);
}
+ @Test
+ @SmallTest
+ public void testMimeTypeInPathHandlers() throws Throwable {
+ final String testHtmlContents = "<body><div>test</div></body>";
+
+ AssetHelper mockAssetHelper = new MockAssetHelper() {
+ @Override
+ public InputStream openResource(Uri uri) {
+ try {
+ return new ByteArrayInputStream(testHtmlContents.getBytes(ENCODING));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
+ .addPathHandler("/assets/", new AssetsPathHandler(mockAssetHelper))
+ .addPathHandler("/res/", new ResourcesPathHandler(mockAssetHelper))
+ .build();
+
+ WebResourceResponse response = assetLoader.shouldInterceptRequest(
+ Uri.parse("https://appassets.androidplatform.net/res/raw/test"));
+ Assert.assertEquals("File doesn't have an extension, MIME type should be text/plain",
+ AssetHelper.DEFAULT_MIME_TYPE, response.getMimeType());
+
+ response = assetLoader.shouldInterceptRequest(
+ Uri.parse("https://appassets.androidplatform.net/assets/other/test"));
+ Assert.assertEquals("File doesn't have an extension, MIME type should be text/plain",
+ AssetHelper.DEFAULT_MIME_TYPE, response.getMimeType());
+
+ response = assetLoader.shouldInterceptRequest(
+ Uri.parse("https://appassets.androidplatform.net/res/drawable/test.png"));
+ Assert.assertEquals(".png file should have mime type image/png regardless of its content",
+ "image/png", response.getMimeType());
+
+ response = assetLoader.shouldInterceptRequest(
+ Uri.parse("https://appassets.androidplatform.net/assets/images/test.png"));
+ Assert.assertEquals(".png file should have mime type image/png regardless of its content",
+ "image/png", response.getMimeType());
+ }
+
private static void assertResponse(@Nullable WebResourceResponse response,
@NonNull String expectedContent) throws IOException {
Assert.assertNotNull("failed to match the URL and returned null response", response);
diff --git a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
index d74bde7..b34d682 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
@@ -32,7 +32,6 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
@@ -149,6 +148,11 @@
* falling back to network and trying to resolve a path that doesn't exist. A
* {@link WebResourceResponse} with {@code null} {@link InputStream} will be received as an
* HTTP response with status code {@code 404} and no body.
+ * <p class="note">
+ * The MIME type for the file will be determined from the file's extension using
+ * {@link java.net.URLConnection#guessContentTypeFromName}. Developers should ensure that
+ * asset files are named using standard file extensions. If the file does not have a
+ * recognised extension, {@code "text/plain"} will be used by default.
*
* @param path the suffix path to be handled.
* @return {@link WebResourceResponse} for the requested file.
@@ -162,7 +166,7 @@
.build();
InputStream is = mAssetHelper.openAsset(uri);
- String mimeType = URLConnection.guessContentTypeFromName(path);
+ String mimeType = AssetHelper.guessMimeType(path);
return new WebResourceResponse(mimeType, null, is);
}
}
@@ -194,6 +198,11 @@
* falling back to network and trying to resolve a path that doesn't exist. A
* {@link WebResourceResponse} with {@code null} {@link InputStream} will be received as an
* HTTP response with status code {@code 404} and no body.
+ * <p class="note">
+ * The MIME type for the file will be determined from the file's extension using
+ * {@link java.net.URLConnection#guessContentTypeFromName}. Developers should ensure that
+ * resource files are named using standard file extensions. If the file does not have a
+ * recognised extension, {@code "text/plain"} will be used by default.
*
* @param path the suffix path to be handled.
* @return {@link WebResourceResponse} for the requested file.
@@ -207,7 +216,7 @@
.build();
InputStream is = mAssetHelper.openResource(uri);
- String mimeType = URLConnection.guessContentTypeFromName(path);
+ String mimeType = AssetHelper.guessMimeType(path);
return new WebResourceResponse(mimeType, null, is);
}
@@ -298,6 +307,11 @@
* trying to resolve a path that doesn't exist. A {@link WebResourceResponse} with
* {@code null} {@link InputStream} will be received as an HTTP response with status code
* {@code 404} and no body.
+ * <p class="note">
+ * The MIME type for the file will be determined from the file's extension using
+ * {@link java.net.URLConnection#guessContentTypeFromName}. Developers should ensure that
+ * files are named using standard file extensions. If the file does not have a
+ * recognised extension, {@code "text/plain"} will be used by default.
*
* @param path the suffix path to be handled.
* @return {@link WebResourceResponse} for the requested file.
@@ -314,7 +328,7 @@
Log.e(TAG, "The requested file: " + path + " is outside the mounted directory: "
+ mDirectory);
}
- String mimeType = URLConnection.guessContentTypeFromName(path);
+ String mimeType = AssetHelper.guessMimeType(path);
return new WebResourceResponse(mimeType, null, is);
}
}
diff --git a/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java b/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java
index 8294ab2..e6d5d2e 100644
--- a/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java
+++ b/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java
@@ -31,6 +31,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URLConnection;
import java.util.List;
import java.util.zip.GZIPInputStream;
@@ -41,6 +42,11 @@
public class AssetHelper {
private static final String TAG = "AssetHelper";
+ /**
+ * Default value to be used as MIME type if guessing MIME type failed.
+ */
+ public static final String DEFAULT_MIME_TYPE = "text/plain";
+
@NonNull private Context mContext;
public AssetHelper(@NonNull Context context) {
@@ -196,4 +202,16 @@
}
}
+ /**
+ * Use {@link URLConnection#guessContentTypeFromName} to guess MIME type or return the
+ * {@link DEFAULT_MIME_TYPE} if it can't guess.
+ *
+ * @param filePath path of the file to guess its MIME type.
+ * @return MIME type guessed from file extension or {@link DEFAULT_MIME_TYPE}.
+ */
+ @NonNull
+ public static String guessMimeType(@NonNull String filePath) {
+ String mimeType = URLConnection.guessContentTypeFromName(filePath);
+ return mimeType == null ? DEFAULT_MIME_TYPE : mimeType;
+ }
}
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
index aa28fb2..bbc87a0 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
@@ -16,6 +16,8 @@
package androidx.work.impl.background.systemjob;
+import static androidx.work.impl.background.systemjob.SystemJobInfoConverter.EXTRA_WORK_SPEC_ID;
+
import android.app.Application;
import android.app.job.JobParameters;
import android.app.job.JobService;
@@ -24,6 +26,7 @@
import android.text.TextUtils;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.work.Logger;
@@ -86,22 +89,14 @@
}
@Override
- public boolean onStartJob(JobParameters params) {
+ public boolean onStartJob(@NonNull JobParameters params) {
if (mWorkManagerImpl == null) {
Logger.get().debug(TAG, "WorkManager is not initialized; requesting retry.");
jobFinished(params, true);
return false;
}
- PersistableBundle extras = params.getExtras();
- // This can be null, possibly on a device-specific/API-specific (23) situation. b/134028277
- //noinspection ConstantConditions
- if (extras == null) {
- Logger.get().error(TAG, "No extras in JobParameters.");
- return false;
- }
-
- String workSpecId = extras.getString(SystemJobInfoConverter.EXTRA_WORK_SPEC_ID);
+ String workSpecId = getWorkSpecIdFromJobParameters(params);
if (TextUtils.isEmpty(workSpecId)) {
Logger.get().error(TAG, "WorkSpec id not found!");
return false;
@@ -152,21 +147,13 @@
}
@Override
- public boolean onStopJob(JobParameters params) {
+ public boolean onStopJob(@NonNull JobParameters params) {
if (mWorkManagerImpl == null) {
Logger.get().debug(TAG, "WorkManager is not initialized; requesting retry.");
return true;
}
- PersistableBundle extras = params.getExtras();
- // This can be null, possibly on a device-specific/API-specific (23) situation. b/134028277
- //noinspection ConstantConditions
- if (extras == null) {
- Logger.get().error(TAG, "No extras in JobParameters.");
- return false;
- }
-
- String workSpecId = extras.getString(SystemJobInfoConverter.EXTRA_WORK_SPEC_ID);
+ String workSpecId = getWorkSpecIdFromJobParameters(params);
if (TextUtils.isEmpty(workSpecId)) {
Logger.get().error(TAG, "WorkSpec id not found!");
return false;
@@ -192,4 +179,18 @@
jobFinished(parameters, needsReschedule);
}
}
+
+ @Nullable
+ @SuppressWarnings("ConstantConditions")
+ private static String getWorkSpecIdFromJobParameters(@NonNull JobParameters parameters) {
+ try {
+ PersistableBundle extras = parameters.getExtras();
+ if (extras != null && extras.containsKey(EXTRA_WORK_SPEC_ID)) {
+ return extras.getString(EXTRA_WORK_SPEC_ID);
+ }
+ } catch (NullPointerException e) {
+ // b/138441699: BaseBundle.getString sometimes throws an NPE. Ignore and return null.
+ }
+ return null;
+ }
}