diff --git a/BasicRxJavaSample/versions.gradle b/BasicRxJavaSample/versions.gradle index 3323585c7..2a9797b6c 100644 --- a/BasicRxJavaSample/versions.gradle +++ b/BasicRxJavaSample/versions.gradle @@ -23,7 +23,7 @@ ext.deps = [:] def versions = [:] versions.arch_core = "1.1.1" -versions.room = "1.1.0-rc1" +versions.room = "1.1.0" versions.lifecycle = "1.1.1" versions.support = "27.1.1" versions.dagger = "2.15" @@ -39,14 +39,16 @@ versions.dexmaker = "2.2.0" versions.constraint_layout = "1.0.2" versions.glide = "4.7.1" versions.timber = "4.5.1" -versions.android_gradle_plugin = "3.1.1" +versions.android_gradle_plugin = "3.1.2" versions.rxjava2 = "2.1.3" versions.rx_android = "2.0.1" versions.atsl_runner = "1.0.1" versions.atsl_rules = "1.0.1" versions.hamcrest = "1.3" versions.kotlin = "1.2.41" -versions.paging = "1.0.0-rc1" +versions.paging = "1.0.0" +versions.work = "1.0.0-alpha01" +versions.navigation = "1.0.0-alpha01" def deps = [:] def support = [:] @@ -139,6 +141,22 @@ build_versions.target_sdk = 26 build_versions.build_tools = "27.0.3" ext.build_versions = build_versions +def work = [:] +work.runtime = "android.arch.work:work-runtime:$versions.work" +work.testing = "android.arch.work:work-testing:$versions.work" +work.firebase = "android.arch.work:work-firebase:$versions.work" +deps.work = work + +def navigation = [:] +navigation.runtime = "android.arch.navigation:navigation-runtime:$versions.navigation" +navigation.runtime_ktx = "android.arch.navigation:navigation-runtime-ktx:$versions.navigation" +navigation.fragment = "android.arch.navigation:navigation-fragment:$versions.navigation" +navigation.fragment_ktx = "android.arch.navigation:navigation-fragment-ktx:$versions.navigation" +navigation.safe_args_plugin = "android.arch.navigation:navigation-safe-args-gradle-plugin:$versions.navigation" +navigation.testing_ktx = "android.arch.navigation:navigation-testing-ktx:$versions.navigation" +deps.navigation = navigation + +ext.deps = deps def addRepos(RepositoryHandler handler) { handler.google() diff --git a/BasicRxJavaSampleKotlin/versions.gradle b/BasicRxJavaSampleKotlin/versions.gradle index 3323585c7..2a9797b6c 100644 --- a/BasicRxJavaSampleKotlin/versions.gradle +++ b/BasicRxJavaSampleKotlin/versions.gradle @@ -23,7 +23,7 @@ ext.deps = [:] def versions = [:] versions.arch_core = "1.1.1" -versions.room = "1.1.0-rc1" +versions.room = "1.1.0" versions.lifecycle = "1.1.1" versions.support = "27.1.1" versions.dagger = "2.15" @@ -39,14 +39,16 @@ versions.dexmaker = "2.2.0" versions.constraint_layout = "1.0.2" versions.glide = "4.7.1" versions.timber = "4.5.1" -versions.android_gradle_plugin = "3.1.1" +versions.android_gradle_plugin = "3.1.2" versions.rxjava2 = "2.1.3" versions.rx_android = "2.0.1" versions.atsl_runner = "1.0.1" versions.atsl_rules = "1.0.1" versions.hamcrest = "1.3" versions.kotlin = "1.2.41" -versions.paging = "1.0.0-rc1" +versions.paging = "1.0.0" +versions.work = "1.0.0-alpha01" +versions.navigation = "1.0.0-alpha01" def deps = [:] def support = [:] @@ -139,6 +141,22 @@ build_versions.target_sdk = 26 build_versions.build_tools = "27.0.3" ext.build_versions = build_versions +def work = [:] +work.runtime = "android.arch.work:work-runtime:$versions.work" +work.testing = "android.arch.work:work-testing:$versions.work" +work.firebase = "android.arch.work:work-firebase:$versions.work" +deps.work = work + +def navigation = [:] +navigation.runtime = "android.arch.navigation:navigation-runtime:$versions.navigation" +navigation.runtime_ktx = "android.arch.navigation:navigation-runtime-ktx:$versions.navigation" +navigation.fragment = "android.arch.navigation:navigation-fragment:$versions.navigation" +navigation.fragment_ktx = "android.arch.navigation:navigation-fragment-ktx:$versions.navigation" +navigation.safe_args_plugin = "android.arch.navigation:navigation-safe-args-gradle-plugin:$versions.navigation" +navigation.testing_ktx = "android.arch.navigation:navigation-testing-ktx:$versions.navigation" +deps.navigation = navigation + +ext.deps = deps def addRepos(RepositoryHandler handler) { handler.google() diff --git a/BasicSample/versions.gradle b/BasicSample/versions.gradle index 3323585c7..2a9797b6c 100644 --- a/BasicSample/versions.gradle +++ b/BasicSample/versions.gradle @@ -23,7 +23,7 @@ ext.deps = [:] def versions = [:] versions.arch_core = "1.1.1" -versions.room = "1.1.0-rc1" +versions.room = "1.1.0" versions.lifecycle = "1.1.1" versions.support = "27.1.1" versions.dagger = "2.15" @@ -39,14 +39,16 @@ versions.dexmaker = "2.2.0" versions.constraint_layout = "1.0.2" versions.glide = "4.7.1" versions.timber = "4.5.1" -versions.android_gradle_plugin = "3.1.1" +versions.android_gradle_plugin = "3.1.2" versions.rxjava2 = "2.1.3" versions.rx_android = "2.0.1" versions.atsl_runner = "1.0.1" versions.atsl_rules = "1.0.1" versions.hamcrest = "1.3" versions.kotlin = "1.2.41" -versions.paging = "1.0.0-rc1" +versions.paging = "1.0.0" +versions.work = "1.0.0-alpha01" +versions.navigation = "1.0.0-alpha01" def deps = [:] def support = [:] @@ -139,6 +141,22 @@ build_versions.target_sdk = 26 build_versions.build_tools = "27.0.3" ext.build_versions = build_versions +def work = [:] +work.runtime = "android.arch.work:work-runtime:$versions.work" +work.testing = "android.arch.work:work-testing:$versions.work" +work.firebase = "android.arch.work:work-firebase:$versions.work" +deps.work = work + +def navigation = [:] +navigation.runtime = "android.arch.navigation:navigation-runtime:$versions.navigation" +navigation.runtime_ktx = "android.arch.navigation:navigation-runtime-ktx:$versions.navigation" +navigation.fragment = "android.arch.navigation:navigation-fragment:$versions.navigation" +navigation.fragment_ktx = "android.arch.navigation:navigation-fragment-ktx:$versions.navigation" +navigation.safe_args_plugin = "android.arch.navigation:navigation-safe-args-gradle-plugin:$versions.navigation" +navigation.testing_ktx = "android.arch.navigation:navigation-testing-ktx:$versions.navigation" +deps.navigation = navigation + +ext.deps = deps def addRepos(RepositoryHandler handler) { handler.google() diff --git a/GithubBrowserSample/app/build.gradle b/GithubBrowserSample/app/build.gradle index eb1b8fc35..72c694a04 100644 --- a/GithubBrowserSample/app/build.gradle +++ b/GithubBrowserSample/app/build.gradle @@ -20,6 +20,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-allopen' +apply plugin: 'androidx.navigation.safeargs' allOpen { // allows mocking for classes w/o directly opening them for release builds @@ -70,6 +71,7 @@ dependencies { implementation deps.support.recyclerview implementation deps.support.cardview implementation deps.support.design + implementation deps.navigation.fragment_ktx implementation deps.room.runtime implementation deps.lifecycle.runtime implementation deps.lifecycle.extensions @@ -100,6 +102,7 @@ dependencies { }) testImplementation deps.mockito.core + androidTestImplementation deps.navigation.testing_ktx androidTestImplementation deps.support.app_compat androidTestImplementation deps.support.recyclerview androidTestImplementation deps.support.cardview diff --git a/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/repo/RepoFragmentTest.kt b/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/repo/RepoFragmentTest.kt index 617141665..bb359e6d0 100644 --- a/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/repo/RepoFragmentTest.kt +++ b/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/repo/RepoFragmentTest.kt @@ -30,16 +30,18 @@ import android.support.test.espresso.matcher.ViewMatchers.withId import android.support.test.espresso.matcher.ViewMatchers.withText import android.support.test.rule.ActivityTestRule import android.support.test.runner.AndroidJUnit4 +import androidx.navigation.NavController import com.android.example.github.R import com.android.example.github.binding.FragmentBindingAdapters import com.android.example.github.testing.SingleFragmentActivity -import com.android.example.github.ui.common.NavigationController import com.android.example.github.util.CountingAppExecutorsRule import com.android.example.github.util.EspressoTestUtil import com.android.example.github.util.RecyclerViewMatcher import com.android.example.github.util.TaskExecutorWithIdlingResourceRule import com.android.example.github.util.TestUtil import com.android.example.github.util.ViewModelUtil +import com.android.example.github.util.matcher +import com.android.example.github.util.mock import com.android.example.github.vo.Contributor import com.android.example.github.vo.Repo import com.android.example.github.vo.Resource @@ -67,19 +69,17 @@ class RepoFragmentTest { val countingAppExecutors = CountingAppExecutorsRule() private val repoLiveData = MutableLiveData>() private val contributorsLiveData = MutableLiveData>>() - private lateinit var repoFragment: RepoFragment private lateinit var viewModel: RepoViewModel private lateinit var mockBindingAdapter: FragmentBindingAdapters - private lateinit var navigationController: NavigationController + private val repoFragment = TestRepoFragment().apply { + arguments = RepoFragmentArgs.Builder("a", "b").build().toBundle() + } @Before fun init() { - EspressoTestUtil.disableProgressBarAnimations(activityRule) - repoFragment = RepoFragment.create("a", "b") viewModel = mock(RepoViewModel::class.java) mockBindingAdapter = mock(FragmentBindingAdapters::class.java) - navigationController = mock(NavigationController::class.java) doNothing().`when`(viewModel).setId(anyString(), anyString()) `when`(viewModel.repo).thenReturn(repoLiveData) `when`(viewModel.contributors).thenReturn(contributorsLiveData) @@ -90,8 +90,8 @@ class RepoFragmentTest { return mockBindingAdapter } } - repoFragment.navigationController = navigationController activityRule.activity.setFragment(repoFragment) + EspressoTestUtil.disableProgressBarAnimations(activityRule) } @Test @@ -168,7 +168,9 @@ class RepoFragmentTest { fun testContributorClick() { setContributors("aa", "bb", "cc") onView(withText("cc")).perform(click()) - verify(navigationController).navigateToUser("cc") + verify(repoFragment.navController).navigate( + RepoFragmentDirections.showUser("cc").matcher() + ) } @Test @@ -200,4 +202,9 @@ class RepoFragmentTest { private fun getString(@StringRes id: Int, vararg args: Any): String { return InstrumentationRegistry.getTargetContext().getString(id, *args) } + + class TestRepoFragment : RepoFragment() { + val navController = mock() + override fun navController() = navController + } } \ No newline at end of file diff --git a/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/search/SearchFragmentTest.kt b/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/search/SearchFragmentTest.kt index fab1f8ca9..5e0c3f6a0 100644 --- a/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/search/SearchFragmentTest.kt +++ b/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/search/SearchFragmentTest.kt @@ -34,16 +34,18 @@ import android.support.test.rule.ActivityTestRule import android.support.test.runner.AndroidJUnit4 import android.support.v7.widget.RecyclerView import android.view.KeyEvent +import androidx.navigation.NavController import com.android.example.github.R import com.android.example.github.binding.FragmentBindingAdapters import com.android.example.github.testing.SingleFragmentActivity -import com.android.example.github.ui.common.NavigationController import com.android.example.github.util.CountingAppExecutorsRule import com.android.example.github.util.EspressoTestUtil import com.android.example.github.util.RecyclerViewMatcher import com.android.example.github.util.TaskExecutorWithIdlingResourceRule import com.android.example.github.util.TestUtil import com.android.example.github.util.ViewModelUtil +import com.android.example.github.util.matcher +import com.android.example.github.util.mock import com.android.example.github.vo.Repo import com.android.example.github.vo.Resource import org.hamcrest.CoreMatchers.not @@ -70,21 +72,18 @@ class SearchFragmentTest { val countingAppExecutors = CountingAppExecutorsRule() private lateinit var mockBindingAdapter: FragmentBindingAdapters - private lateinit var navigationController: NavigationController private lateinit var viewModel: SearchViewModel private val results = MutableLiveData>>() private val loadMoreStatus = MutableLiveData() + private val searchFragment = TestSearchFragment() @Before fun init() { - EspressoTestUtil.disableProgressBarAnimations(activityRule) - val searchFragment = SearchFragment() viewModel = mock(SearchViewModel::class.java) doReturn(loadMoreStatus).`when`(viewModel).loadMoreStatus `when`(viewModel.results).thenReturn(results) mockBindingAdapter = mock(FragmentBindingAdapters::class.java) - navigationController = mock(NavigationController::class.java) searchFragment.appExecutors = countingAppExecutors.appExecutors searchFragment.viewModelFactory = ViewModelUtil.createFor(viewModel) @@ -93,8 +92,8 @@ class SearchFragmentTest { return mockBindingAdapter } } - searchFragment.navigationController = navigationController activityRule.activity.setFragment(searchFragment) + EspressoTestUtil.disableProgressBarAnimations(activityRule) } @Test @@ -147,7 +146,9 @@ class SearchFragmentTest { val repo = TestUtil.createRepo("foo", "bar", "desc") results.postValue(Resource.success(arrayListOf(repo))) onView(withText("desc")).perform(click()) - verify(navigationController).navigateToRepo("foo", "bar") + verify(searchFragment.navController).navigate( + SearchFragmentDirections.showRepo("foo", "bar").matcher() + ) } @Test @@ -171,4 +172,9 @@ class SearchFragmentTest { private fun listMatcher(): RecyclerViewMatcher { return RecyclerViewMatcher(R.id.repo_list) } + + class TestSearchFragment : SearchFragment() { + val navController = mock() + override fun navController() = navController + } } \ No newline at end of file diff --git a/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/user/UserFragmentTest.kt b/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/user/UserFragmentTest.kt index 65c2ffadb..742f48b0d 100644 --- a/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/user/UserFragmentTest.kt +++ b/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/ui/user/UserFragmentTest.kt @@ -28,16 +28,18 @@ import android.support.test.espresso.matcher.ViewMatchers.withId import android.support.test.espresso.matcher.ViewMatchers.withText import android.support.test.rule.ActivityTestRule import android.support.test.runner.AndroidJUnit4 +import androidx.navigation.NavController import com.android.example.github.R import com.android.example.github.binding.FragmentBindingAdapters import com.android.example.github.testing.SingleFragmentActivity -import com.android.example.github.ui.common.NavigationController import com.android.example.github.util.CountingAppExecutorsRule import com.android.example.github.util.EspressoTestUtil import com.android.example.github.util.RecyclerViewMatcher import com.android.example.github.util.TaskExecutorWithIdlingResourceRule import com.android.example.github.util.TestUtil import com.android.example.github.util.ViewModelUtil +import com.android.example.github.util.matcher +import com.android.example.github.util.mock import com.android.example.github.vo.Repo import com.android.example.github.vo.Resource import com.android.example.github.vo.User @@ -64,34 +66,33 @@ class UserFragmentTest { @JvmField val countingAppExecutors = CountingAppExecutorsRule() private lateinit var viewModel: UserViewModel - private lateinit var navigationController: NavigationController private lateinit var mockBindingAdapter: FragmentBindingAdapters private val userData = MutableLiveData>() private val repoListData = MutableLiveData>>() + private val testFragment = TestUserFragment().apply { + arguments = UserFragmentArgs.Builder("foo").build().toBundle() + } @Before fun init() { - EspressoTestUtil.disableProgressBarAnimations(activityRule) - val fragment = UserFragment.create("foo") viewModel = mock(UserViewModel::class.java) `when`(viewModel.user).thenReturn(userData) `when`(viewModel.repositories).thenReturn(repoListData) doNothing().`when`(viewModel).setLogin(anyString()) - navigationController = mock(NavigationController::class.java) mockBindingAdapter = mock(FragmentBindingAdapters::class.java) - fragment.appExecutors = countingAppExecutors.appExecutors - fragment.viewModelFactory = ViewModelUtil.createFor(viewModel) - fragment.navigationController = navigationController - fragment.dataBindingComponent = object : DataBindingComponent { + testFragment.appExecutors = countingAppExecutors.appExecutors + testFragment.viewModelFactory = ViewModelUtil.createFor(viewModel) + testFragment.dataBindingComponent = object : DataBindingComponent { override fun getFragmentBindingAdapters(): FragmentBindingAdapters { return mockBindingAdapter } } - activityRule.activity.setFragment(fragment) + activityRule.activity.setFragment(testFragment) activityRule.runOnUiThread { - fragment.binding.repoList.itemAnimator = null + testFragment.binding.repoList.itemAnimator = null } + EspressoTestUtil.disableProgressBarAnimations(activityRule) } @Test @@ -141,7 +142,7 @@ class UserFragmentTest { } val repo3 = setRepos(3)[2] onView(listMatcher().atPosition(2)).check( - matches(hasDescendant(withText(repo3.name))) + matches(hasDescendant(withText(repo3.name))) ) } @@ -150,7 +151,9 @@ class UserFragmentTest { val repos = setRepos(2) val selected = repos[1] onView(withText(selected.description)).perform(click()) - verify(navigationController).navigateToRepo(selected.owner.login, selected.name) + verify(testFragment.navController).navigate( + UserFragmentDirections.showRepo(selected.owner.login, selected.name).matcher() + ) } @Test @@ -191,4 +194,9 @@ class UserFragmentTest { repoListData.postValue(Resource.success(repos)) return repos } + + class TestUserFragment : UserFragment() { + val navController = mock() + override fun navController() = navController + } } \ No newline at end of file diff --git a/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/util/NavDirectionsMatcher.kt b/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/util/NavDirectionsMatcher.kt new file mode 100644 index 000000000..71eb15f0c --- /dev/null +++ b/GithubBrowserSample/app/src/androidTest/java/com/android/example/github/util/NavDirectionsMatcher.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.example.github.util + +import android.os.Bundle +import androidx.navigation.NavDirections +import org.mockito.Mockito + +private fun Bundle?.eq(other : Bundle?) : Boolean { + if (this == null) { + return other == null + } + if (other == null) { + return false + } + if (keySet().size != other.keySet().size) { + return false + } + return keySet().all { + get(it) == other.get(it) + } +} + +/** + * A convenience method to create a Mockito argument matcher for navigation directions. + */ +fun NavDirections.matcher() = Mockito.argThat { + it.actionId == this.actionId && arguments.eq(it.arguments) +} \ No newline at end of file diff --git a/GithubBrowserSample/app/src/debug/java/com/android/example/github/testing/SingleFragmentActivity.kt b/GithubBrowserSample/app/src/debug/java/com/android/example/github/testing/SingleFragmentActivity.kt index a22fa477e..c60868ace 100644 --- a/GithubBrowserSample/app/src/debug/java/com/android/example/github/testing/SingleFragmentActivity.kt +++ b/GithubBrowserSample/app/src/debug/java/com/android/example/github/testing/SingleFragmentActivity.kt @@ -21,7 +21,6 @@ import android.support.v4.app.Fragment import android.support.v7.app.AppCompatActivity import android.view.ViewGroup import android.widget.FrameLayout - import com.android.example.github.R /** diff --git a/GithubBrowserSample/app/src/main/java/com/android/example/github/MainActivity.kt b/GithubBrowserSample/app/src/main/java/com/android/example/github/MainActivity.kt index 105246451..09f3dc27e 100644 --- a/GithubBrowserSample/app/src/main/java/com/android/example/github/MainActivity.kt +++ b/GithubBrowserSample/app/src/main/java/com/android/example/github/MainActivity.kt @@ -19,7 +19,6 @@ package com.android.example.github import android.os.Bundle import android.support.v4.app.Fragment import android.support.v7.app.AppCompatActivity -import com.android.example.github.ui.common.NavigationController import dagger.android.DispatchingAndroidInjector import dagger.android.support.HasSupportFragmentInjector import javax.inject.Inject @@ -27,15 +26,10 @@ import javax.inject.Inject class MainActivity : AppCompatActivity(), HasSupportFragmentInjector { @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector - @Inject - lateinit var navigationController: NavigationController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) - if (savedInstanceState == null) { - navigationController.navigateToSearch() - } } override fun supportFragmentInjector() = dispatchingAndroidInjector diff --git a/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/common/NavigationController.kt b/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/common/NavigationController.kt deleted file mode 100644 index 732d8f0fe..000000000 --- a/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/common/NavigationController.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.example.github.ui.common - -import com.android.example.github.MainActivity -import com.android.example.github.R -import com.android.example.github.testing.OpenForTesting -import com.android.example.github.ui.repo.RepoFragment -import com.android.example.github.ui.search.SearchFragment -import com.android.example.github.ui.user.UserFragment -import javax.inject.Inject - -/** - * A utility class that handles navigation in [MainActivity]. - */ -@OpenForTesting -class NavigationController @Inject constructor(mainActivity: MainActivity) { - private val containerId = R.id.container - private val fragmentManager = mainActivity.supportFragmentManager - - fun navigateToSearch() { - val searchFragment = SearchFragment() - fragmentManager.beginTransaction() - .replace(containerId, searchFragment) - .commitAllowingStateLoss() - } - - fun navigateToRepo(owner: String, name: String) { - val fragment = RepoFragment.create(owner, name) - val tag = "repo/$owner/$name" - fragmentManager.beginTransaction() - .replace(containerId, fragment, tag) - .addToBackStack(null) - .commitAllowingStateLoss() - } - - fun navigateToUser(login: String) { - val tag = "user/$login" - val userFragment = UserFragment.create(login) - fragmentManager.beginTransaction() - .replace(containerId, userFragment, tag) - .addToBackStack(null) - .commitAllowingStateLoss() - } -} diff --git a/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/repo/RepoFragment.kt b/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/repo/RepoFragment.kt index 3ac1b75bb..dbd38595c 100644 --- a/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/repo/RepoFragment.kt +++ b/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/repo/RepoFragment.kt @@ -26,12 +26,13 @@ import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.navigation.fragment.findNavController import com.android.example.github.AppExecutors import com.android.example.github.R import com.android.example.github.binding.FragmentDataBindingComponent import com.android.example.github.databinding.RepoFragmentBinding import com.android.example.github.di.Injectable -import com.android.example.github.ui.common.NavigationController +import com.android.example.github.testing.OpenForTesting import com.android.example.github.ui.common.RetryCallback import com.android.example.github.util.autoCleared import javax.inject.Inject @@ -39,6 +40,7 @@ import javax.inject.Inject /** * The UI Controller for displaying a Github Repo's information with its contributors. */ +@OpenForTesting class RepoFragment : Fragment(), Injectable { @Inject @@ -46,9 +48,6 @@ class RepoFragment : Fragment(), Injectable { lateinit var repoViewModel: RepoViewModel - @Inject - lateinit var navigationController: NavigationController - @Inject lateinit var appExecutors: AppExecutors @@ -62,10 +61,8 @@ class RepoFragment : Fragment(), Injectable { super.onActivityCreated(savedInstanceState) repoViewModel = ViewModelProviders.of(this, viewModelFactory) .get(RepoViewModel::class.java) - val args = arguments - val ownerParam = args?.getString(REPO_OWNER_KEY) - val repoParam = args?.getString(REPO_NAME_KEY) - repoViewModel.setId(ownerParam, repoParam) + val params = RepoFragmentArgs.fromBundle(arguments) + repoViewModel.setId(params.owner, params.name) val repo = repoViewModel.repo repo.observe(this, Observer { resource -> @@ -75,7 +72,9 @@ class RepoFragment : Fragment(), Injectable { }) val adapter = ContributorAdapter(dataBindingComponent, appExecutors) { contributor -> - navigationController.navigateToUser(contributor.login) + navController().navigate( + RepoFragmentDirections.showUser(contributor.login) + ) } this.adapter = adapter binding.contributorList.adapter = adapter @@ -113,19 +112,8 @@ class RepoFragment : Fragment(), Injectable { return dataBinding.root } - companion object { - - private const val REPO_OWNER_KEY = "repo_owner" - - private const val REPO_NAME_KEY = "repo_name" - - fun create(owner: String, name: String): RepoFragment { - val repoFragment = RepoFragment() - val args = Bundle() - args.putString(REPO_OWNER_KEY, owner) - args.putString(REPO_NAME_KEY, name) - repoFragment.arguments = args - return repoFragment - } - } + /** + * Created to be able to override in tests + */ + fun navController() = findNavController() } diff --git a/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/search/SearchFragment.kt b/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/search/SearchFragment.kt index 4c60bac70..11ac7f009 100644 --- a/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/search/SearchFragment.kt +++ b/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/search/SearchFragment.kt @@ -34,25 +34,24 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager +import androidx.navigation.fragment.findNavController import com.android.example.github.AppExecutors import com.android.example.github.R import com.android.example.github.binding.FragmentDataBindingComponent import com.android.example.github.databinding.SearchFragmentBinding import com.android.example.github.di.Injectable -import com.android.example.github.ui.common.NavigationController +import com.android.example.github.testing.OpenForTesting import com.android.example.github.ui.common.RepoListAdapter import com.android.example.github.ui.common.RetryCallback import com.android.example.github.util.autoCleared import javax.inject.Inject +@OpenForTesting class SearchFragment : Fragment(), Injectable { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory - @Inject - lateinit var navigationController: NavigationController - @Inject lateinit var appExecutors: AppExecutors @@ -89,9 +88,8 @@ class SearchFragment : Fragment(), Injectable { appExecutors = appExecutors, showFullName = true ) { repo -> - navigationController.navigateToRepo( - owner = repo.owner.login, - name = repo.name + navController().navigate( + SearchFragmentDirections.showRepo(repo.owner.login, repo.name) ) } binding.repoList.adapter = rvAdapter @@ -169,4 +167,9 @@ class SearchFragment : Fragment(), Injectable { val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager imm?.hideSoftInputFromWindow(windowToken, 0) } + + /** + * Created to be able to override in tests + */ + fun navController() = findNavController() } diff --git a/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/user/UserFragment.kt b/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/user/UserFragment.kt index 1e112eed2..477e997db 100644 --- a/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/user/UserFragment.kt +++ b/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/user/UserFragment.kt @@ -26,23 +26,23 @@ import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.navigation.fragment.findNavController import com.android.example.github.AppExecutors import com.android.example.github.R import com.android.example.github.binding.FragmentDataBindingComponent import com.android.example.github.databinding.UserFragmentBinding import com.android.example.github.di.Injectable -import com.android.example.github.ui.common.NavigationController +import com.android.example.github.testing.OpenForTesting import com.android.example.github.ui.common.RepoListAdapter import com.android.example.github.ui.common.RetryCallback import com.android.example.github.util.autoCleared import javax.inject.Inject +@OpenForTesting class UserFragment : Fragment(), Injectable { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @Inject - lateinit var navigationController: NavigationController - @Inject lateinit var appExecutors: AppExecutors var binding by autoCleared() @@ -75,8 +75,8 @@ class UserFragment : Fragment(), Injectable { super.onActivityCreated(savedInstanceState) userViewModel = ViewModelProviders.of(this, viewModelFactory) .get(UserViewModel::class.java) - - userViewModel.setLogin(arguments?.getString(LOGIN_KEY)) + val params = UserFragmentArgs.fromBundle(arguments) + userViewModel.setLogin(params.login) userViewModel.user.observe(this, Observer { userResource -> binding.user = userResource?.data @@ -89,10 +89,7 @@ class UserFragment : Fragment(), Injectable { appExecutors = appExecutors, showFullName = false ) { repo -> - navigationController.navigateToRepo( - owner = repo.owner.login, - name = repo.name - ) + navController().navigate(UserFragmentDirections.showRepo(repo.owner.login, repo.name)) } binding.repoList.adapter = rvAdapter this.adapter = rvAdapter @@ -101,21 +98,12 @@ class UserFragment : Fragment(), Injectable { private fun initRepoList() { userViewModel.repositories.observe(this, Observer { repos -> - // no null checks for adapter since LiveData guarantees that we'll not receive - // the event if fragment is now show. adapter.submitList(repos?.data) }) } - companion object { - private const val LOGIN_KEY = "login" - - fun create(login: String): UserFragment { - val userFragment = UserFragment() - val bundle = Bundle() - bundle.putString(LOGIN_KEY, login) - userFragment.arguments = bundle - return userFragment - } - } + /** + * Created to be able to override in tests + */ + fun navController() = findNavController() } diff --git a/GithubBrowserSample/app/src/main/res/layout/main_activity.xml b/GithubBrowserSample/app/src/main/res/layout/main_activity.xml index c6a0ce334..e73ec1e88 100644 --- a/GithubBrowserSample/app/src/main/res/layout/main_activity.xml +++ b/GithubBrowserSample/app/src/main/res/layout/main_activity.xml @@ -16,11 +16,15 @@ - diff --git a/GithubBrowserSample/app/src/main/res/navigation/main.xml b/GithubBrowserSample/app/src/main/res/navigation/main.xml new file mode 100644 index 000000000..1add42912 --- /dev/null +++ b/GithubBrowserSample/app/src/main/res/navigation/main.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GithubBrowserSample/app/src/test/java/com/android/example/github/util/MockitoExt.kt b/GithubBrowserSample/app/src/test-common/java/com/android/example/github/util/MockitoExt.kt similarity index 100% rename from GithubBrowserSample/app/src/test/java/com/android/example/github/util/MockitoExt.kt rename to GithubBrowserSample/app/src/test-common/java/com/android/example/github/util/MockitoExt.kt diff --git a/GithubBrowserSample/build.gradle b/GithubBrowserSample/build.gradle index b1e6f9da7..e7cbea4d5 100644 --- a/GithubBrowserSample/build.gradle +++ b/GithubBrowserSample/build.gradle @@ -22,6 +22,7 @@ buildscript { classpath deps.android_gradle_plugin classpath deps.kotlin.plugin classpath deps.kotlin.allopen + classpath deps.navigation.safe_args_plugin // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/GithubBrowserSample/versions.gradle b/GithubBrowserSample/versions.gradle index 3323585c7..2a9797b6c 100644 --- a/GithubBrowserSample/versions.gradle +++ b/GithubBrowserSample/versions.gradle @@ -23,7 +23,7 @@ ext.deps = [:] def versions = [:] versions.arch_core = "1.1.1" -versions.room = "1.1.0-rc1" +versions.room = "1.1.0" versions.lifecycle = "1.1.1" versions.support = "27.1.1" versions.dagger = "2.15" @@ -39,14 +39,16 @@ versions.dexmaker = "2.2.0" versions.constraint_layout = "1.0.2" versions.glide = "4.7.1" versions.timber = "4.5.1" -versions.android_gradle_plugin = "3.1.1" +versions.android_gradle_plugin = "3.1.2" versions.rxjava2 = "2.1.3" versions.rx_android = "2.0.1" versions.atsl_runner = "1.0.1" versions.atsl_rules = "1.0.1" versions.hamcrest = "1.3" versions.kotlin = "1.2.41" -versions.paging = "1.0.0-rc1" +versions.paging = "1.0.0" +versions.work = "1.0.0-alpha01" +versions.navigation = "1.0.0-alpha01" def deps = [:] def support = [:] @@ -139,6 +141,22 @@ build_versions.target_sdk = 26 build_versions.build_tools = "27.0.3" ext.build_versions = build_versions +def work = [:] +work.runtime = "android.arch.work:work-runtime:$versions.work" +work.testing = "android.arch.work:work-testing:$versions.work" +work.firebase = "android.arch.work:work-firebase:$versions.work" +deps.work = work + +def navigation = [:] +navigation.runtime = "android.arch.navigation:navigation-runtime:$versions.navigation" +navigation.runtime_ktx = "android.arch.navigation:navigation-runtime-ktx:$versions.navigation" +navigation.fragment = "android.arch.navigation:navigation-fragment:$versions.navigation" +navigation.fragment_ktx = "android.arch.navigation:navigation-fragment-ktx:$versions.navigation" +navigation.safe_args_plugin = "android.arch.navigation:navigation-safe-args-gradle-plugin:$versions.navigation" +navigation.testing_ktx = "android.arch.navigation:navigation-testing-ktx:$versions.navigation" +deps.navigation = navigation + +ext.deps = deps def addRepos(RepositoryHandler handler) { handler.google() diff --git a/NavigationBasicSample/.gitignore b/NavigationBasicSample/.gitignore new file mode 100644 index 000000000..09b993d06 --- /dev/null +++ b/NavigationBasicSample/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/NavigationBasicSample/CONTRIBUTING.md b/NavigationBasicSample/CONTRIBUTING.md new file mode 100644 index 000000000..7b86f95d7 --- /dev/null +++ b/NavigationBasicSample/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# How to become a contributor and submit your own code + +## Contributor License Agreements + +We'd love to accept your sample apps and patches! Before we can take them, we +have to jump a couple of legal hurdles. + +Please fill out either the individual or corporate Contributor License Agreement (CLA). + + * If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign an [individual CLA] + (https://cla.developers.google.com). + * If you work for a company that wants to allow you to contribute your work, + then you'll need to sign a [corporate CLA] + (https://cla.developers.google.com). + * Please make sure you sign both, Android and Google CLA + +Follow either of the two links above to access the appropriate CLA and +instructions for how to sign and return it. Once we receive it, we'll be able to +accept your pull requests. + +## Contributing A Patch + +1. Submit an issue describing your proposed change to the repo in question. +1. The repo owner will respond to your issue promptly. +1. If your proposed change is accepted, and you haven't already done so, sign a + Contributor License Agreement (see details above). +1. Fork the desired repo, develop and test your code changes. +1. Ensure that your code adheres to the existing style in the sample to which + you are contributing. Refer to the + [Android Code Style Guide] + (https://source.android.com/source/code-style.html) for the + recommended coding standards for this organization. +1. Ensure that your code has an appropriate set of unit tests which all pass. +1. Submit a pull request. diff --git a/NavigationBasicSample/LICENSE b/NavigationBasicSample/LICENSE new file mode 100644 index 000000000..b79cfb703 --- /dev/null +++ b/NavigationBasicSample/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NavigationBasicSample/README.md b/NavigationBasicSample/README.md new file mode 100644 index 000000000..dfc14745d --- /dev/null +++ b/NavigationBasicSample/README.md @@ -0,0 +1,41 @@ +Android Architecture Components Navigation Basic Sample +============================================== + +### Features + +This sample showcases the following features of the Navigation component: + + * Navigating via actions + * Transitions + * Popping destinations from the back stack + * Arguments (profile screen receives a user name) + * Deep links (`www.example.com/user/{user name}` opens the profile screen) + + +### Other Resources + + * Particularly In Java, consider using `Navigation.createNavigateOnClickListener()` to quickly + create click listeners. + * Consider including the [Navigation KTX libraries](https://developer.android.com/topic/libraries/architecture/adding-components#navigation) + for more concise uses of the Navigation component. For example, calls to `Navigation.findNavController(view)` can + be expressed as `view.findNavController()`. + +License +------- + +Copyright 2018 The Android Open Source Project, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you 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. diff --git a/NavigationBasicSample/app/.gitignore b/NavigationBasicSample/app/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/NavigationBasicSample/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/NavigationBasicSample/app/build.gradle b/NavigationBasicSample/app/build.gradle new file mode 100644 index 000000000..40c2ca68b --- /dev/null +++ b/NavigationBasicSample/app/build.gradle @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.application' + +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion build_versions.target_sdk + defaultConfig { + applicationId "com.example.android.navigationsample" + minSdkVersion build_versions.min_sdk + targetSdkVersion build_versions.target_sdk + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + vectorDrawables.useSupportLibrary = true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation deps.kotlin.stdlib + implementation deps.support.app_compat + implementation deps.support.design + implementation deps.support.v4 + implementation deps.constraint_layout + + implementation 'androidx.core:core-ktx:0.3' + + // Navigation + + implementation deps.navigation.runtime + implementation deps.navigation.fragment + +} diff --git a/NavigationBasicSample/app/proguard-rules.pro b/NavigationBasicSample/app/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/NavigationBasicSample/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/NavigationBasicSample/app/sampledata/rank.json b/NavigationBasicSample/app/sampledata/rank.json new file mode 100644 index 000000000..648013b48 --- /dev/null +++ b/NavigationBasicSample/app/sampledata/rank.json @@ -0,0 +1,14 @@ +{ + "data" :[ + { "rank":"#1"}, + { "rank":"#2"}, + { "rank":"#3"}, + { "rank":"#4"}, + { "rank":"#5"}, + { "rank":"#6"}, + { "rank":"#7"}, + { "rank":"#8"}, + { "rank":"#9"}, + { "rank":"#10"} + ] +} \ No newline at end of file diff --git a/NavigationBasicSample/app/src/main/AndroidManifest.xml b/NavigationBasicSample/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..e6a089e3c --- /dev/null +++ b/NavigationBasicSample/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/GameOver.kt b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/GameOver.kt new file mode 100644 index 000000000..92712e914 --- /dev/null +++ b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/GameOver.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.navigationsample + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.Navigation + + +/** + * Shows a game over screen and a button to start over. + */ +class GameOver : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + val view = inflater.inflate(R.layout.fragment_game_over, container, false) + + view.findViewById(R.id.play_btn4).setOnClickListener { + Navigation.findNavController(view).navigate(R.id.action_game_over_to_match) + } + return view + } +} diff --git a/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/InGame.kt b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/InGame.kt new file mode 100644 index 000000000..2161fe505 --- /dev/null +++ b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/InGame.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.navigationsample + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.Navigation + +/** + * Shows a question and four answers. + */ +class InGame : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + val view = inflater.inflate(R.layout.fragment_in_game, container, false) + + val gameOverListener: (View) -> Unit = { + Navigation.findNavController(view).navigate(R.id.action_in_game_to_gameOver) + } + + view.findViewById(R.id.checkBox).setOnClickListener(gameOverListener) + view.findViewById(R.id.checkBox2).setOnClickListener(gameOverListener) + view.findViewById(R.id.checkBox4).setOnClickListener(gameOverListener) + + view.findViewById(R.id.checkBox3).setOnClickListener { + Navigation.findNavController(view).navigate(R.id.action_in_game_to_resultsWinner) + } + + return view + } +} diff --git a/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/Leaderboard.kt b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/Leaderboard.kt new file mode 100644 index 000000000..a49c2170b --- /dev/null +++ b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/Leaderboard.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.navigationsample + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v7.recyclerview.extensions.ListAdapter +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.core.os.bundleOf +import androidx.navigation.Navigation + +/** + * Shows a static leaderboard with three users. + */ +class Leaderboard : Fragment() { + + private lateinit var recyclerView: RecyclerView + private lateinit var viewAdapter: RecyclerView.Adapter<*> + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + val view = inflater.inflate(R.layout.fragment_leaderboard, container, false) + + viewAdapter = MyAdapter(arrayOf("Flo", "Ly", "Jo")) + + recyclerView = view.findViewById(R.id.leaderboard_list).apply { + // use this setting to improve performance if you know that changes + // in content do not change the layout size of the RecyclerView + setHasFixedSize(true) + + // specify an viewAdapter (see also next example) + adapter = viewAdapter + + } + return view + } + +} + +class MyAdapter(private val myDataset: Array) : + RecyclerView.Adapter() { + + // Provide a reference to the views for each data item + // Complex data items may need more than one view per item, and + // you provide access to all the views for a data item in a view holder. + // Each data item is just a string in this case that is shown in a TextView. + class ViewHolder(val item: View) : RecyclerView.ViewHolder(item) + + + // Create new views (invoked by the layout manager) + override fun onCreateViewHolder(parent: ViewGroup, + viewType: Int): ViewHolder { + // create a new view + val itemView = LayoutInflater.from(parent.context) + .inflate(R.layout.list_view_item, parent, false) + + + return ViewHolder(itemView) + } + + // Replace the contents of a view (invoked by the layout manager) + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + // - get element from your dataset at this position + // - replace the contents of the view with that element + holder.item.findViewById(R.id.user_name_text).text = myDataset[position] + + holder.item.findViewById(R.id.user_avatar_image) + .setImageResource(listOfAvatars[position]) + + holder.item.setOnClickListener { + val bundle = bundleOf("userName" to myDataset[position]) + + Navigation.findNavController(holder.item).navigate( + R.id.action_leaderboard_to_userProfile, + bundle) + } + } + + // Return the size of your dataset (invoked by the layout manager) + override fun getItemCount() = myDataset.size +} + +private val listOfAvatars = listOf( + R.drawable.avatar_1_raster, R.drawable.avatar_2_raster, R.drawable.avatar_3_raster) diff --git a/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/MainActivity.kt b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/MainActivity.kt new file mode 100644 index 000000000..8d2629416 --- /dev/null +++ b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/MainActivity.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.navigationsample + +import android.os.Bundle +import android.support.v7.app.AppCompatActivity + +/** + * An activity that inflates a layout that has a NavHostFragment. + */ +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + } +} diff --git a/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/Match.kt b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/Match.kt new file mode 100644 index 000000000..46d03fd43 --- /dev/null +++ b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/Match.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.navigationsample + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.Navigation + +/** + * Shows a warning-up screen. + */ +class Match : Fragment() { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + val view = inflater.inflate(R.layout.fragment_match, container, false) + + view.findViewById(R.id.play_btn3).setOnClickListener { + Navigation.findNavController(view).navigate(R.id.action_match_to_in_game) + } + return view + } + +} diff --git a/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/Register.kt b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/Register.kt new file mode 100644 index 000000000..4d6a3ca2c --- /dev/null +++ b/NavigationBasicSample/app/src/main/java/com/example/android/navigationsample/Register.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.navigationsample + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import androidx.navigation.Navigation + + +/** + * Shows a register form (that does nothing, for simplicity) + */ +class Register : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + val view = inflater.inflate(R.layout.fragment_register, container, false) + + view.findViewById