├── .gitignore
├── .idea
├── .name
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── gradle.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── noorganization
│ │ └── googlecertificationkotlin
│ │ ├── ExampleInstrumentedTest.kt
│ │ ├── LiveDataTestUtil.kt
│ │ ├── WordDaoTest.kt
│ │ ├── codelab_espresso_onview_ondata
│ │ └── SpinnerExpressoActivityTest.kt
│ │ ├── codelab_flow
│ │ ├── FlowStepOne.kt
│ │ ├── FlowStepThree.kt
│ │ ├── FlowStepTwo.kt
│ │ └── FunctionsUtils.kt
│ │ └── dagger
│ │ ├── ApplicationTest.kt
│ │ ├── base
│ │ ├── MyCustomTestRunner.kt
│ │ └── MyTestApplication.kt
│ │ ├── di
│ │ ├── RepositoryFakeModule.kt
│ │ └── TestAppComponent.kt
│ │ └── fake_domain
│ │ └── LoginRepositoryFakeImp.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── noorganization
│ │ │ ├── certification_completed.png
│ │ │ └── googlecertificationkotlin
│ │ │ ├── GoogleCertificationKotlinApplication.kt
│ │ │ ├── code_lab_unit_test
│ │ │ ├── Calculator.java
│ │ │ ├── Readme.txt
│ │ │ └── UnitTestCalculatorActivity.kt
│ │ │ ├── codelab_espresso_onview_ondata
│ │ │ └── SpinnerEspressoActivity.kt
│ │ │ ├── codelab_flow
│ │ │ ├── NetworkService.kt
│ │ │ ├── PlantDao.kt
│ │ │ ├── PlantListViewModel.kt
│ │ │ ├── PlantRepository.kt
│ │ │ ├── model
│ │ │ │ ├── MaskedCardView.kt
│ │ │ │ └── Plant.kt
│ │ │ ├── ui
│ │ │ │ ├── PlantActivity.kt
│ │ │ │ ├── PlantAdapter.kt
│ │ │ │ ├── PlantDetailBindingAdapters.kt
│ │ │ │ └── PlantListFragment.kt
│ │ │ └── utils
│ │ │ │ ├── AppDatabase.kt
│ │ │ │ ├── CacheOnSuccess.kt
│ │ │ │ ├── Injector.kt
│ │ │ │ ├── NetworkResult.kt
│ │ │ │ └── Utils.kt
│ │ │ ├── codelab_jobscheduler
│ │ │ ├── JobServiceActivity.kt
│ │ │ └── NotificationJobService.kt
│ │ │ ├── codelab_material_component
│ │ │ ├── LoginFragment.kt
│ │ │ ├── MaterialActivity.kt
│ │ │ ├── NavigationHost.kt
│ │ │ ├── NavigationIconClickListener.kt
│ │ │ ├── ProductCardRecyclerViewAdapter.kt
│ │ │ ├── ProductCardViewHolder.kt
│ │ │ ├── ProductGridFragment.kt
│ │ │ ├── ProductGridItemDecoration.kt
│ │ │ ├── network
│ │ │ │ ├── ImageRequester.kt
│ │ │ │ └── ProductEntry.kt
│ │ │ └── staggeredgridlayout
│ │ │ │ ├── StaggeredProductCardRecyclerViewAdapter.kt
│ │ │ │ └── StaggeredProductCardViewHolder.kt
│ │ │ ├── codelab_notification
│ │ │ └── NotificationActivity.kt
│ │ │ ├── codelab_room_coroutines
│ │ │ ├── NewWordActivity.kt
│ │ │ ├── RoomCoroutinesActivity.kt
│ │ │ ├── WordCoroutinesViewModel.kt
│ │ │ ├── WordListAdapter.kt
│ │ │ └── db
│ │ │ │ ├── WordRepository.kt
│ │ │ │ └── WordRoomDatabase.kt
│ │ │ ├── codelab_room_livedata_viewmodel
│ │ │ ├── NewWordActivity.kt
│ │ │ ├── RoomWordsSampleActivity.kt
│ │ │ ├── WordListAdapter.kt
│ │ │ ├── WordViewModel.kt
│ │ │ └── db
│ │ │ │ ├── Word.kt
│ │ │ │ ├── WordDao.kt
│ │ │ │ ├── WordRepository.kt
│ │ │ │ └── WordRoomDatabase.kt
│ │ │ ├── codelab_sharepreference
│ │ │ └── SharedPreferenceActivity.kt
│ │ │ ├── codelab_unit_test_livedata_roboletric
│ │ │ ├── Event.kt
│ │ │ ├── ScrollChildSwipeRefreshLayout.kt
│ │ │ ├── addedittask
│ │ │ │ ├── AddEditTaskFragment.kt
│ │ │ │ └── AddEditTaskViewModel.kt
│ │ │ ├── data
│ │ │ │ ├── Result.kt
│ │ │ │ ├── Task.kt
│ │ │ │ └── source
│ │ │ │ │ ├── DefaultTasksRepository.kt
│ │ │ │ │ ├── TasksDataSource.kt
│ │ │ │ │ ├── local
│ │ │ │ │ ├── TasksDao.kt
│ │ │ │ │ ├── TasksLocalDataSource.kt
│ │ │ │ │ └── ToDoDatabase.kt
│ │ │ │ │ └── remote
│ │ │ │ │ └── TasksRemoteDataSource.kt
│ │ │ ├── statistics
│ │ │ │ ├── StatisticsFragment.kt
│ │ │ │ ├── StatisticsUtils.kt
│ │ │ │ └── StatisticsViewModel.kt
│ │ │ ├── taskdetail
│ │ │ │ ├── TaskDetailFragment.kt
│ │ │ │ └── TaskDetailViewModel.kt
│ │ │ ├── tasks
│ │ │ │ ├── TasksActivity.kt
│ │ │ │ ├── TasksAdapter.kt
│ │ │ │ ├── TasksFilterType.kt
│ │ │ │ ├── TasksFragment.kt
│ │ │ │ ├── TasksListBindings.kt
│ │ │ │ └── TasksViewModel.kt
│ │ │ └── util
│ │ │ │ └── ViewExt.kt
│ │ │ ├── codelab_user_navigation
│ │ │ ├── HamburguerMenuActivity.kt
│ │ │ ├── OrderActivity.kt
│ │ │ ├── PagerAdapter.kt
│ │ │ ├── TabFragment1.kt
│ │ │ ├── TabFragment2.kt
│ │ │ ├── TabFragment3.kt
│ │ │ └── UserNavigationActivity.kt
│ │ │ ├── codelab_workmanager
│ │ │ ├── BlurActivity.kt
│ │ │ ├── BlurViewModel.kt
│ │ │ ├── Constants.kt
│ │ │ ├── SelectImageActivity.kt
│ │ │ └── workers
│ │ │ │ ├── BlurWorker.kt
│ │ │ │ ├── CleanWorker.kt
│ │ │ │ ├── SaveImageToFileWorker.kt
│ │ │ │ └── WorkerUtils.kt
│ │ │ ├── codelab_workmanager_mvvm_repository
│ │ │ └── devbyteviewer
│ │ │ │ ├── database
│ │ │ │ ├── DatabaseEntities.kt
│ │ │ │ └── Room.kt
│ │ │ │ ├── domain
│ │ │ │ └── Models.kt
│ │ │ │ ├── network
│ │ │ │ ├── DataTransferObjects.kt
│ │ │ │ └── Service.kt
│ │ │ │ ├── repository
│ │ │ │ └── VideosRepository.kt
│ │ │ │ ├── ui
│ │ │ │ ├── DevByteActivity.kt
│ │ │ │ └── DevByteFragment.kt
│ │ │ │ ├── util
│ │ │ │ ├── BindingAdapters.kt
│ │ │ │ └── Util.kt
│ │ │ │ ├── viewmodels
│ │ │ │ └── DevByteViewModel.kt
│ │ │ │ └── work
│ │ │ │ └── RefreshDataWorker.kt
│ │ │ └── extra_code_lab_injection
│ │ │ ├── Android_apresentacao_oficial_pdf.pdf
│ │ │ ├── step1
│ │ │ ├── Car.kt
│ │ │ ├── CarInjectNativeActivity.kt
│ │ │ ├── CarInjectedNative.kt
│ │ │ ├── Engine.kt
│ │ │ └── reference.txt
│ │ │ ├── step2
│ │ │ ├── Car.kt
│ │ │ └── Engine.kt
│ │ │ ├── step3
│ │ │ ├── data
│ │ │ │ ├── datasource
│ │ │ │ │ ├── RemoteLoginDataSource.kt
│ │ │ │ │ └── RemoteLoginDataSourceImp.kt
│ │ │ │ └── repository
│ │ │ │ │ ├── LoginRepository.kt
│ │ │ │ │ └── LoginRepositoryImp.kt
│ │ │ ├── di
│ │ │ │ ├── AppComponent.kt
│ │ │ │ ├── AppSubcomponents.kt
│ │ │ │ └── subcomponent
│ │ │ │ │ └── login
│ │ │ │ │ ├── LoginComponent.kt
│ │ │ │ │ └── module
│ │ │ │ │ ├── LoginDataSourceModule.kt
│ │ │ │ │ ├── LoginModule.kt
│ │ │ │ │ ├── LoginRepositoryModule.kt
│ │ │ │ │ └── LoginViewModelModule.kt
│ │ │ └── presentation
│ │ │ │ ├── LoginDaggerActivity.kt
│ │ │ │ └── LoginViewModel.kt
│ │ │ └── step4_espresso_test
│ │ │ └── EspressoActivity.kt
│ └── res
│ │ ├── drawable-v24
│ │ ├── drawer_item_color.xml
│ │ ├── ic_add.xml
│ │ ├── ic_assignment_turned_in_24dp.xml
│ │ ├── ic_check_circle_96dp.xml
│ │ ├── ic_done.xml
│ │ ├── ic_edit.xml
│ │ ├── ic_filter_list.xml
│ │ ├── ic_filter_list_24dp.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── ic_list.xml
│ │ ├── ic_menu.xml
│ │ ├── ic_statistics.xml
│ │ ├── ic_statistics_100dp.xml
│ │ ├── ic_statistics_24dp.xml
│ │ ├── ic_verified_user_96dp.xml
│ │ ├── list_completed_touch_feedback.xml
│ │ ├── logo_no_fill.png
│ │ ├── touch_feedback.xml
│ │ └── trash_icon.png
│ │ ├── drawable
│ │ ├── donut_circle.png
│ │ ├── froyo_circle.png
│ │ ├── ic_add_black_24dp.xml
│ │ ├── ic_android.png
│ │ ├── ic_arrow_downward_black_24dp.xml
│ │ ├── ic_arrow_upward_black_24dp.xml
│ │ ├── ic_favorite.png
│ │ ├── ic_job_running.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_map.png
│ │ ├── ic_play_circle_outline_black_48dp.xml
│ │ ├── ic_shopping_cart.png
│ │ ├── ic_status_info.png
│ │ ├── ic_update.png
│ │ ├── icecream_circle.png
│ │ ├── mascot_1.jpg
│ │ ├── shr_branded_menu.xml
│ │ ├── shr_close_menu.xml
│ │ ├── shr_filter.xml
│ │ ├── shr_logo.xml
│ │ ├── shr_menu.xml
│ │ ├── shr_product_grid_background_shape.xml
│ │ ├── shr_search.xml
│ │ ├── side_nav_bar.xml
│ │ └── test.jpg
│ │ ├── font
│ │ ├── opensans_font.xml
│ │ ├── opensans_regular.ttf
│ │ └── opensans_semibold.ttf
│ │ ├── layout
│ │ ├── activity_blur.xml
│ │ ├── activity_car_inject_native.xml
│ │ ├── activity_dagger.xml
│ │ ├── activity_dagger_step2.xml
│ │ ├── activity_dagger_step3.xml
│ │ ├── activity_dev_byte_viewer.xml
│ │ ├── activity_espresso.xml
│ │ ├── activity_hamburguer_menu.xml
│ │ ├── activity_login_dagger.xml
│ │ ├── activity_main.xml
│ │ ├── activity_new_word.xml
│ │ ├── activity_new_word2.xml
│ │ ├── activity_notification.xml
│ │ ├── activity_order.xml
│ │ ├── activity_plant.xml
│ │ ├── activity_room_coroutines.xml
│ │ ├── activity_room_words_sample.xml
│ │ ├── activity_select.xml
│ │ ├── activity_shared_preference.xml
│ │ ├── activity_spinner_espresso.xml
│ │ ├── activity_unit_test_calculator.xml
│ │ ├── activity_user_navigation.xml
│ │ ├── addtask_frag.xml
│ │ ├── app_bar_main.xml
│ │ ├── content_room_words_sample.xml
│ │ ├── content_user_navigation.xml
│ │ ├── devbyte_item.xml
│ │ ├── fragment_dev_byte.xml
│ │ ├── fragment_plant_list.xml
│ │ ├── list_item_plant.xml
│ │ ├── nav_header.xml
│ │ ├── nav_header_main.xml
│ │ ├── recyclerview_item.xml
│ │ ├── recyclerview_item_v2.xml
│ │ ├── shr_login_fragment.xml
│ │ ├── shr_main_activity.xml
│ │ ├── shr_product_card.xml
│ │ ├── shr_product_grid_fragment.xml
│ │ ├── shr_staggered_product_card_first.xml
│ │ ├── shr_staggered_product_card_second.xml
│ │ ├── shr_staggered_product_card_third.xml
│ │ ├── statistics_frag.xml
│ │ ├── tab_fragment1.xml
│ │ ├── tab_fragment2.xml
│ │ ├── tab_fragment3.xml
│ │ ├── task_item.xml
│ │ ├── taskdetail_frag.xml
│ │ ├── tasks_act.xml
│ │ └── tasks_frag.xml
│ │ ├── menu
│ │ ├── activity_main_drawer.xml
│ │ ├── drawer_actions.xml
│ │ ├── filter_tasks.xml
│ │ ├── main.xml
│ │ ├── menu.xml
│ │ ├── menu_plant_list.xml
│ │ ├── taskdetail_fragment_menu.xml
│ │ └── tasks_fragment_menu.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── navigation
│ │ ├── nav_graph.xml
│ │ └── nav_graph_mockito.xml
│ │ ├── raw
│ │ └── products.json
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── drawables.xml
│ │ ├── integers.xml
│ │ ├── shape.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── noorganization
│ └── googlecertificationkotlin
│ ├── CalculatorUnitTest.kt
│ ├── ExampleUnitTest.kt
│ ├── codelab_flow
│ └── CacheOnSuccessTest.kt
│ ├── extra_code_lab_injection
│ └── step3
│ │ └── presentation
│ │ └── LoginViewModelTest.kt
│ ├── inject
│ └── step2
│ │ └── CarStep2Test.kt
│ └── unit_test
│ ├── ExampleUnitTest.kt
│ ├── LiveDataTestUtil.kt
│ ├── statistics
│ └── StatisticsUtilsTest.kt
│ └── tasks
│ └── TasksViewModelTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | GoogleCertificationKotlin
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.noorganization.googlecertificationkotlin", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/LiveDataTestUtil.kt:
--------------------------------------------------------------------------------
1 | package com.example.android.roomwordssample
2 |
3 | /*
4 | * Copyright (C) 2017 The Android Open Source Project
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import androidx.lifecycle.LiveData
20 | import androidx.lifecycle.Observer
21 |
22 | import java.util.concurrent.CountDownLatch
23 | import java.util.concurrent.TimeUnit
24 |
25 | /**
26 | * Get the value from a LiveData object. We're waiting for LiveData to emit, for 2 seconds.
27 | * Once we got a notification via onChanged, we stop observing.
28 | */
29 | @Throws(InterruptedException::class)
30 | fun LiveData.waitForValue(): T {
31 | val data = arrayOfNulls(1)
32 | val latch = CountDownLatch(1)
33 | val observer = object : Observer {
34 | override fun onChanged(o: T?) {
35 | data[0] = o
36 | latch.countDown()
37 | this@waitForValue.removeObserver(this)
38 | }
39 | }
40 | this.observeForever(observer)
41 | latch.await(2, TimeUnit.SECONDS)
42 |
43 | return data[0] as T
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/codelab_flow/FlowStepOne.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_flow
2 |
3 | import android.util.Log
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 | import kotlinx.coroutines.CoroutineScope
6 | import kotlinx.coroutines.Dispatchers
7 | import kotlinx.coroutines.flow.collect
8 | import kotlinx.coroutines.flow.flow
9 | import kotlinx.coroutines.launch
10 | import org.junit.Test
11 | import org.junit.runner.RunWith
12 |
13 | @RunWith(AndroidJUnit4::class)
14 | class FlowStepOne {
15 | /** https://codelabs.developers.google.com/codelabs/advanced-kotlin-coroutines/#7
16 | *
17 | * You can see how execution bounces between the collect lambda and the flow builder.
18 | * Every time the flow builder calls emit, it suspends until the element is completely processed.
19 | * Then, when another value is requested from the flow, it resumes from where it left off until it
20 | * calls emit again. When the flow builder completes, the Flow is cancelled and collect resumes,
21 | * letting and the calling coroutine prints "flow is completed."
22 |
23 | The call to collect is very important. Flow uses suspending operators like collect instead of
24 | exposing an Iterator interface so that it always knows when it's being actively consumed.
25 | More importantly, it knows when the caller can't request any more values so it can cleanup resources.
26 | */
27 | @Test
28 | fun watchFlowTest() {
29 | CoroutineScope(Dispatchers.IO).launch {
30 | makeFlow().collect { value ->
31 | Log.d("watchFlowTest", "got $value")
32 | }
33 | Log.d("watchFlowTest", "flow is completed")
34 | }
35 | }
36 |
37 | /**
38 | * Response:
39 | * D/watchFlowTest: sending first value
40 | D/watchFlowTest: got 1
41 | first value collected, sending another value
42 | got 2
43 | second value collected, sending a third value
44 | got 3
45 | done
46 | flow is completed
47 | */
48 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/codelab_flow/FlowStepThree.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_flow
2 |
3 | import android.util.Log
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 | import kotlinx.coroutines.*
6 | import kotlinx.coroutines.flow.collect
7 | import kotlinx.coroutines.flow.take
8 | import org.junit.Test
9 | import org.junit.runner.RunWith
10 |
11 | @RunWith(AndroidJUnit4::class)
12 | class FlowStepThree {
13 | /** https://codelabs.developers.google.com/codelabs/advanced-kotlin-coroutines/#7
14 | *
15 | * The flow lambda starts from the top each time collect is called. This is important if the flow
16 | * performed expensive work like making a network request. Also, since we applied the take(2)
17 | * operator, the flow will only produce two values. It will not resume the flow lambda again
18 | * after the second call to emit, so the line "second value collected..." will never print.
19 | *
20 | * By default, a Flow will restart from the top every time a terminal operator is applied.
21 | * This is important if the Flow performs expensive work, such as making a network request.
22 | */
23 | @Test
24 | fun testTakeInFlow() = runBlocking {
25 | val startTime = System.currentTimeMillis()
26 | val job = launch(Dispatchers.Default) {
27 | val repeatableFlow = makeFlow().take(2) // we only care about the first two elements
28 | Log.d("watchFlowTest", "first collection")
29 | repeatableFlow.collect()
30 | Log.d("watchFlowTest", "collecting again")
31 | repeatableFlow.collect()
32 | Log.d("watchFlowTest", "second collection completed")
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/codelab_flow/FlowStepTwo.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_flow
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import kotlinx.coroutines.*
5 | import org.junit.Test
6 | import org.junit.runner.RunWith
7 | import java.lang.System.currentTimeMillis
8 |
9 | @RunWith(AndroidJUnit4::class)
10 | class FlowStepTwo {
11 |
12 | /**
13 | * https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#cancellation-is-cooperative
14 | */
15 | @Test
16 | fun testCancellationIsCooperative() = runBlocking {
17 | val startTime = currentTimeMillis()
18 | val job = launch(Dispatchers.Default) {
19 | var nextPrintTime = startTime
20 | var i = 0
21 | while (i < 5) { // computation loop, just wastes CPU
22 | // print a message twice a second
23 | if (currentTimeMillis() >= nextPrintTime) {
24 | println("job: I'm sleeping ${i++} ...")
25 | nextPrintTime += 2000L
26 | }
27 | }
28 | }
29 | delay(2300L) // delay a bit
30 | println("main: I'm tired of waiting!")
31 | job.cancelAndJoin() // cancels the job and waits for its completion
32 | println("main: Now I can quit.")
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/codelab_flow/FunctionsUtils.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_flow
2 |
3 | import android.util.Log
4 | import kotlinx.coroutines.flow.flow
5 |
6 | fun makeFlow() = flow {
7 | emit(1)
8 | Log.d("watchFlowTest", "sending first value - makeFlow")
9 |
10 | emit(2)
11 | Log.d("watchFlowTest", "second value collected, sending another value - makeFlow")
12 |
13 | emit(3)
14 | Log.d("watchFlowTest", "three value collected, sending a third value - makeFlow")
15 |
16 | Log.d("watchFlowTest", "done")
17 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/dagger/ApplicationTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.dagger
18 |
19 | import androidx.test.core.app.ActivityScenario
20 | import androidx.test.espresso.Espresso.onView
21 | import androidx.test.espresso.action.ViewActions.*
22 | import androidx.test.espresso.assertion.ViewAssertions
23 | import androidx.test.espresso.matcher.ViewMatchers
24 | import androidx.test.espresso.matcher.ViewMatchers.withId
25 | import com.noorganization.googlecertificationkotlin.R
26 | import com.noorganization.googlecertificationkotlin.extra_code_lab_injection.step3.presentation.LoginDaggerActivity
27 | import org.junit.Test
28 |
29 | class ApplicationTest {
30 |
31 | @Test
32 | fun runApp() {
33 | ActivityScenario.launch(LoginDaggerActivity::class.java)
34 |
35 | Thread.sleep(2000)
36 | onView(withId(R.id.username)).perform(typeText("username"), closeSoftKeyboard())
37 | onView(withId(R.id.password)).perform(typeText("password"), closeSoftKeyboard())
38 | onView(withId(R.id.login)).perform(click())
39 |
40 | // Registration/T&Cs
41 | onView(ViewMatchers.withText("Fake implements works!")).check(
42 | ViewAssertions.matches(
43 | ViewMatchers.isDisplayed()
44 | )
45 | )
46 |
47 | Thread.sleep(5000)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/dagger/base/MyCustomTestRunner.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.noorganization.googlecertificationkotlin.dagger.base
18 |
19 | import android.app.Application
20 | import android.content.Context
21 | import androidx.test.runner.AndroidJUnitRunner
22 |
23 | /**
24 | * A custom [AndroidJUnitRunner] used to replace the application used in tests with a
25 | * [MyTestApplication].
26 | */
27 | class MyCustomTestRunner : AndroidJUnitRunner() {
28 |
29 | override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
30 | return super.newApplication(cl, MyTestApplication::class.java.name, context)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/dagger/base/MyTestApplication.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.noorganization.googlecertificationkotlin.dagger.base
18 |
19 | import com.noorganization.googlecertificationkotlin.GoogleCertificationKotlinApplication
20 | import com.noorganization.googlecertificationkotlin.dagger.di.DaggerTestAppComponent
21 | import com.noorganization.googlecertificationkotlin.extra_code_lab_injection.step3.di.AppComponent
22 |
23 | /**
24 | * MyTestApplication will override MyApplication in android tests
25 | */
26 | class MyTestApplication : GoogleCertificationKotlinApplication() {
27 |
28 | override fun initializeComponent(): AppComponent {
29 | // Creates a new TestAppComponent that injects fakes types
30 | return DaggerTestAppComponent.create()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/dagger/di/RepositoryFakeModule.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.noorganization.googlecertificationkotlin.dagger.di
18 |
19 | import com.noorganization.googlecertificationkotlin.dagger.fake_domain.LoginRepositoryFakeImp
20 | import com.noorganization.googlecertificationkotlin.extra_code_lab_injection.step3.data.repository.LoginRepository
21 | import dagger.Binds
22 | import dagger.Module
23 |
24 | // Overrides StorageModule in android tests
25 | @Module
26 | abstract class RepositoryFakeModule {
27 |
28 | // Makes Dagger provide FakeStorage when a Storage type is requested
29 | @Binds
30 | abstract fun provideRepositoryFake(repositoryFake: LoginRepositoryFakeImp): LoginRepository
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/dagger/di/TestAppComponent.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.noorganization.googlecertificationkotlin.dagger.di
18 |
19 | import com.noorganization.googlecertificationkotlin.extra_code_lab_injection.step3.di.AppComponent
20 | import com.noorganization.googlecertificationkotlin.extra_code_lab_injection.step3.di.AppSubcomponents
21 | import com.noorganization.googlecertificationkotlin.extra_code_lab_injection.step3.di.subcomponent.login.module.LoginDataSourceModule
22 | import com.noorganization.googlecertificationkotlin.extra_code_lab_injection.step3.di.subcomponent.login.module.LoginViewModelModule
23 | import dagger.Component
24 | import javax.inject.Singleton
25 |
26 | // Replacement for AppComponent in android tests
27 | @Singleton
28 | // Includes RepositoryFakeModule that overrides objects provided in StorageModule
29 | @Component(modules = [
30 | RepositoryFakeModule::class,
31 | AppSubcomponents::class,
32 | LoginViewModelModule::class,
33 | LoginDataSourceModule::class])
34 | interface TestAppComponent : AppComponent
35 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/noorganization/googlecertificationkotlin/dagger/fake_domain/LoginRepositoryFakeImp.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.dagger.fake_domain
2 |
3 | import com.noorganization.googlecertificationkotlin.extra_code_lab_injection.step3.data.datasource.RemoteLoginDataSource
4 | import com.noorganization.googlecertificationkotlin.extra_code_lab_injection.step3.data.repository.LoginRepository
5 | import javax.inject.Inject
6 |
7 | class LoginRepositoryFakeImp @Inject constructor(
8 | private val remoteLoginDataSource: RemoteLoginDataSource
9 | ) : LoginRepository {
10 | override fun doLogin(): Boolean {
11 | return false
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/certification_completed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicconicco/googlecertificationkotlin2019/aed45aff9869f0089a95419474586c01a8c152f3/app/src/main/java/com/noorganization/certification_completed.png
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/code_lab_unit_test/Calculator.java:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.code_lab_unit_test;
2 |
3 | public class Calculator {
4 |
5 | // Available operations
6 | public enum Operator {ADD, SUB, DIV, MUL}
7 |
8 | /**
9 | * Addition operation
10 | */
11 | public double add(double firstOperand, double secondOperand) {
12 | return firstOperand + secondOperand;
13 | }
14 |
15 | /**
16 | * Subtract operation
17 | */
18 | public double sub(double firstOperand, double secondOperand) {
19 | return firstOperand - secondOperand;
20 | }
21 |
22 | /**
23 | * Divide operation
24 | */
25 | public double div(double firstOperand, double secondOperand) {
26 | return firstOperand / secondOperand;
27 | }
28 |
29 | /**
30 | * Multiply operation
31 | */
32 | public double mul(double firstOperand, double secondOperand) {
33 | return firstOperand * secondOperand;
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/code_lab_unit_test/Readme.txt:
--------------------------------------------------------------------------------
1 | os testes estao na pasta
2 |
3 | src/test/com.noorganization.googlecertificationkotlin.code_lab_unit_test
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_flow/NetworkService.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_flow
2 |
3 | import com.noorganization.googlecertificationkotlin.codelab_flow.model.GrowZone
4 | import com.noorganization.googlecertificationkotlin.codelab_flow.model.Plant
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.withContext
7 | import okhttp3.OkHttpClient
8 | import retrofit2.Retrofit
9 | import retrofit2.converter.gson.GsonConverterFactory
10 | import retrofit2.http.GET
11 |
12 | class NetworkService {
13 |
14 | private val retrofit = Retrofit.Builder()
15 | .baseUrl("https://raw.githubusercontent.com/")
16 | .client(OkHttpClient())
17 | .addConverterFactory(GsonConverterFactory.create())
18 | .build()
19 |
20 | private val sunflowerService = retrofit.create(SunflowerService::class.java)
21 |
22 | suspend fun allPlants(): List = withContext(Dispatchers.Default) {
23 | val result = sunflowerService.getAllPlants()
24 | result.shuffled()
25 | }
26 |
27 | suspend fun plantsByGrowZone(growZone: GrowZone) = withContext(Dispatchers.Default) {
28 | val result = sunflowerService.getAllPlants()
29 | result.filter { it.growZoneNumber == growZone.number }.shuffled()
30 | }
31 |
32 | suspend fun customPlantSortOrder(): List = withContext(Dispatchers.Default) {
33 | val result = sunflowerService.getCustomPlantSortOrder()
34 | result.map { plant -> plant.plantId }
35 | }
36 | }
37 |
38 | interface SunflowerService {
39 | @GET("googlecodelabs/kotlin-coroutines/master/advanced-coroutines-codelab/sunflower/src/main/assets/plants.json")
40 | suspend fun getAllPlants() : List
41 |
42 | @GET("googlecodelabs/kotlin-coroutines/master/advanced-coroutines-codelab/sunflower/src/main/assets/custom_plant_sort_order.json")
43 | suspend fun getCustomPlantSortOrder() : List
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_flow/PlantDao.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.noorganization.googlecertificationkotlin.codelab_flow
18 |
19 | import androidx.room.Dao
20 | import androidx.room.Insert
21 | import androidx.room.OnConflictStrategy
22 | import androidx.room.Query
23 | import com.noorganization.googlecertificationkotlin.codelab_flow.model.Plant
24 | import kotlinx.coroutines.flow.Flow
25 |
26 | /**
27 | * The Data Access Object for the Plant class.
28 | */
29 | @Dao
30 | interface PlantDao {
31 | @Query("SELECT * FROM plants ORDER BY name")
32 | fun getPlantsFlow(): Flow>
33 |
34 | @Query("SELECT * FROM plants WHERE growZoneNumber = :growZoneNumber ORDER BY name")
35 | fun getPlantsWithGrowZoneNumberFlow(growZoneNumber: Int): Flow>
36 |
37 | @Insert(onConflict = OnConflictStrategy.REPLACE)
38 | suspend fun insertAll(plants: List)
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_flow/model/Plant.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_flow.model
2 | import androidx.room.ColumnInfo
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity(tableName = "plants")
7 | data class Plant(
8 | @PrimaryKey @ColumnInfo(name = "id") val plantId: String,
9 | val name: String,
10 | val description: String,
11 | val growZoneNumber: Int,
12 | val wateringInterval: Int = 7, // how often the plant should be watered, in days
13 | val imageUrl: String = ""
14 | ) {
15 | override fun toString() = name
16 | }
17 |
18 |
19 | inline class GrowZone(val number: Int)
20 | val NoGrowZone = GrowZone(-1)
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_flow/ui/PlantActivity.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_flow.ui
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.noorganization.googlecertificationkotlin.R
6 |
7 | class PlantActivity : AppCompatActivity() {
8 |
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | setContentView(R.layout.activity_plant)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_flow/ui/PlantDetailBindingAdapters.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.noorganization.googlecertificationkotlin.codelab_flow.ui
18 |
19 | import android.widget.ImageView
20 | import androidx.databinding.BindingAdapter
21 | import com.bumptech.glide.Glide
22 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
23 | import com.google.android.material.floatingactionbutton.FloatingActionButton
24 |
25 | @BindingAdapter("imageFromUrl")
26 | fun bindImageFromUrl(view: ImageView, imageUrl: String?) {
27 | if (!imageUrl.isNullOrEmpty()) {
28 | Glide.with(view.context)
29 | .load(imageUrl)
30 | .transition(DrawableTransitionOptions.withCrossFade())
31 | .into(view)
32 | }
33 | }
34 |
35 | @BindingAdapter("isGone")
36 | fun bindIsGone(view: FloatingActionButton, isGone: Boolean?) {
37 | if (isGone == null || isGone) {
38 | view.hide()
39 | } else {
40 | view.show()
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_flow/utils/AppDatabase.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.android.advancedcoroutines.utils
18 |
19 | import android.content.Context
20 | import androidx.room.Database
21 | import androidx.room.Room
22 | import androidx.room.RoomDatabase
23 | import com.noorganization.googlecertificationkotlin.codelab_flow.PlantDao
24 | import com.noorganization.googlecertificationkotlin.codelab_flow.model.Plant
25 |
26 | /**
27 | * The Room database for this app
28 | */
29 | @Database(entities = [Plant::class], version = 1, exportSchema = false)
30 | abstract class AppDatabase : RoomDatabase() {
31 | abstract fun plantDao(): PlantDao
32 |
33 | companion object {
34 |
35 | // For Singleton instantiation
36 | @Volatile private var instance: AppDatabase? = null
37 |
38 | fun getInstance(context: Context): AppDatabase {
39 | return instance ?: synchronized(this) {
40 | instance
41 | ?: buildDatabase(
42 | context
43 | ).also { instance = it }
44 | }
45 | }
46 |
47 | private fun buildDatabase(context: Context): AppDatabase {
48 | return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME).build()
49 | }
50 | }
51 | }
52 |
53 | private const val DATABASE_NAME = "sunflower-db"
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_flow/utils/Injector.kt:
--------------------------------------------------------------------------------
1 | package com.example.android.advancedcoroutines.utils
2 |
3 | import android.content.Context
4 | import androidx.annotation.VisibleForTesting
5 | import com.noorganization.googlecertificationkotlin.codelab_flow.NetworkService
6 | import com.noorganization.googlecertificationkotlin.codelab_flow.ui.PlantListViewModelFactory
7 | import com.noorganization.googlecertificationkotlin.codelab_flow.PlantRepository
8 |
9 | interface ViewModelFactoryProvider {
10 | fun providePlantListViewModelFactory(context: Context): PlantListViewModelFactory
11 | }
12 |
13 | val Injector: ViewModelFactoryProvider
14 | get() = currentInjector
15 |
16 | private object DefaultViewModelProvider: ViewModelFactoryProvider {
17 | private fun getPlantRepository(context: Context): PlantRepository {
18 | return PlantRepository.getInstance(
19 | plantDao(context),
20 | plantService()
21 | )
22 | }
23 |
24 | private fun plantService() = NetworkService()
25 |
26 | private fun plantDao(context: Context) =
27 | AppDatabase.getInstance(context.applicationContext).plantDao()
28 |
29 | override fun providePlantListViewModelFactory(context: Context): PlantListViewModelFactory {
30 | val repository = getPlantRepository(context)
31 | return PlantListViewModelFactory(repository)
32 | }
33 | }
34 |
35 | private object Lock
36 |
37 | @Volatile private var currentInjector: ViewModelFactoryProvider =
38 | DefaultViewModelProvider
39 |
40 |
41 | @VisibleForTesting
42 | private fun setInjectorForTesting(injector: ViewModelFactoryProvider?) {
43 | synchronized(Lock) {
44 | currentInjector = injector ?: DefaultViewModelProvider
45 | }
46 | }
47 |
48 | @VisibleForTesting
49 | private fun resetInjector() =
50 | setInjectorForTesting(null)
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_flow/utils/NetworkResult.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_flow.utils
2 |
3 | sealed class NetworkResult
4 |
5 | // By using Nothing as T, Loading is a subtype of all NetworkResult
6 | object Loading: NetworkResult()
7 |
8 | // Successful results are stored in data
9 | data class OK(val data: T): NetworkResult()
10 |
11 | // By using Nothing as T, all NetworkError instances are a subtypes of all NetworkResults
12 | data class NetworkError(val exception: Throwable): NetworkResult()
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_flow/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_flow.utils
2 |
3 | data class ComparablePair, B: Comparable>(
4 | val first: A,
5 | val second: B
6 | ) : Comparable> {
7 | override fun compareTo(other: ComparablePair): Int {
8 | val firstComp = this.first.compareTo(other.first)
9 | if (firstComp != 0) { return firstComp }
10 | return this.second.compareTo(other.second)
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/LoginFragment.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component
2 |
3 | import android.os.Bundle
4 | import android.text.Editable
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.Fragment
9 | import com.noorganization.googlecertificationkotlin.R
10 | import kotlinx.android.synthetic.main.shr_login_fragment.*
11 | import kotlinx.android.synthetic.main.shr_login_fragment.view.*
12 |
13 | /**
14 | * Fragment representing the login screen for Shrine.
15 | */
16 | class LoginFragment : Fragment() {
17 |
18 | override fun onCreateView(
19 | inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
20 | // Inflate the layout for this fragment
21 | val view = inflater.inflate(R.layout.shr_login_fragment, container, false)
22 |
23 | // Set an error if the password is less than 8 characters.
24 | view.next_button.setOnClickListener {
25 | if (!isPasswordValid(password_edit_text.text)) {
26 | password_text_input.error = getString(R.string.shr_error_password)
27 | } else {
28 | password_text_input.error = null // Clear the error
29 | (activity as NavigationHost).navigateTo(ProductGridFragment(), false) // Navigate to the next Fragment
30 | }
31 | }
32 |
33 | // Clear the error once more than 8 characters are typed.
34 | view.password_edit_text.setOnKeyListener { _, _, _ ->
35 | if (isPasswordValid(password_edit_text.text)) {
36 | password_text_input.error = null //Clear the error
37 | }
38 | false
39 | }
40 | return view
41 | }
42 |
43 | /*
44 | In reality, this will have more complex logic including, but not limited to, actual
45 | authentication of the username and password.
46 | */
47 | private fun isPasswordValid(text: Editable?): Boolean {
48 | return text != null && text.length >= 8
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/MaterialActivity.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.fragment.app.Fragment
6 | import com.noorganization.googlecertificationkotlin.R
7 |
8 | class MaterialActivity : AppCompatActivity(), NavigationHost {
9 |
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 | setContentView(R.layout.shr_main_activity)
13 |
14 | if (savedInstanceState == null) {
15 | supportFragmentManager
16 | .beginTransaction()
17 | .add(R.id.container, LoginFragment())
18 | .commit()
19 | }
20 | }
21 |
22 | /**
23 | * Navigate to the given fragment.
24 | *
25 | * @param fragment Fragment to navigate to.
26 | * @param addToBackstack Whether or not the current fragment should be added to the backstack.
27 | */
28 | override fun navigateTo(fragment: Fragment, addToBackstack: Boolean) {
29 | val transaction = supportFragmentManager
30 | .beginTransaction()
31 | .replace(R.id.container, fragment)
32 |
33 | if (addToBackstack) {
34 | transaction.addToBackStack(null)
35 | }
36 |
37 | transaction.commit()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/NavigationHost.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component
2 |
3 | import androidx.fragment.app.Fragment
4 |
5 |
6 | /**
7 | * A host (typically an `Activity`} that can display fragments and knows how to respond to
8 | * navigation events.
9 | */
10 | interface NavigationHost {
11 | /**
12 | * Trigger a navigation to the specified fragment, optionally adding a transaction to the back
13 | * stack to make this navigation reversible.
14 | */
15 | fun navigateTo(fragment: Fragment, addToBackstack: Boolean)
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/ProductCardRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 |
7 | import com.noorganization.googlecertificationkotlin.codelab_material_component.network.ProductEntry
8 | import com.noorganization.googlecertificationkotlin.R
9 | import com.noorganization.googlecertificationkotlin.codelab_material_component.ProductCardViewHolder
10 |
11 | /**
12 | * Adapter used to show a simple grid of products.
13 | */
14 | class ProductCardRecyclerViewAdapter(private val productList: List) : RecyclerView.Adapter() {
15 |
16 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductCardViewHolder {
17 | val layoutView = LayoutInflater.from(parent.context).inflate(R.layout.shr_product_card, parent, false)
18 | return ProductCardViewHolder(layoutView)
19 | }
20 |
21 | override fun onBindViewHolder(holder: ProductCardViewHolder, position: Int) {
22 | // TODO: Put ViewHolder binding code here in MDC-102
23 | }
24 |
25 | override fun getItemCount(): Int {
26 | return productList.size
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/ProductCardViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component
2 |
3 | import android.view.View
4 | import androidx.recyclerview.widget.RecyclerView
5 |
6 | class ProductCardViewHolder(itemView: View) //TODO: Find and store views from itemView
7 | : RecyclerView.ViewHolder(itemView)
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/ProductGridFragment.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import com.noorganization.googlecertificationkotlin.R
9 |
10 | class ProductGridFragment : Fragment() {
11 |
12 | override fun onCreateView(
13 | inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
14 | // Inflate the layout for this fragment
15 | return inflater.inflate(R.layout.shr_product_grid_fragment, container, false)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/ProductGridItemDecoration.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component
2 |
3 | import android.graphics.Rect
4 | import android.view.View
5 | import androidx.recyclerview.widget.RecyclerView
6 |
7 | /**
8 | * Custom item decoration for a vertical [ProductGridFragment] [RecyclerView]. Adds a
9 | * small amount of padding to the left of grid items, and a large amount of padding to the right.
10 | */
11 | class ProductGridItemDecoration(private val largePadding: Int, private val smallPadding: Int) : RecyclerView.ItemDecoration() {
12 |
13 | override fun getItemOffsets(outRect: Rect, view: View,
14 | parent: RecyclerView, state: RecyclerView.State) {
15 | outRect.left = smallPadding
16 | outRect.right = smallPadding
17 | outRect.top = largePadding
18 | outRect.bottom = largePadding
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/network/ProductEntry.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component.network
2 |
3 | import android.content.res.Resources
4 | import android.net.Uri
5 | import com.google.gson.Gson
6 | import com.google.gson.reflect.TypeToken
7 | import com.noorganization.googlecertificationkotlin.R
8 | import java.io.BufferedReader
9 | import java.util.*
10 |
11 | /**
12 | * A product entry in the list of products.
13 | */
14 | class ProductEntry(
15 | val title: String, dynamicUrl: String, val url: String, val price: String, val description: String) {
16 | val dynamicUrl: Uri = Uri.parse(dynamicUrl)
17 |
18 | companion object {
19 | /**
20 | * Loads a raw JSON at R.raw.products and converts it into a list of ProductEntry objects
21 | */
22 | fun initProductEntryList(resources: Resources): List {
23 | val inputStream = resources.openRawResource(R.raw.products)
24 | val jsonProductsString = inputStream.bufferedReader().use(BufferedReader::readText)
25 | val gson = Gson()
26 | val productListType = object : TypeToken>() {}.type
27 | return gson.fromJson>(jsonProductsString, productListType)
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/staggeredgridlayout/StaggeredProductCardRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component.staggeredgridlayout
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 |
7 | import com.noorganization.googlecertificationkotlin.codelab_material_component.network.ImageRequester
8 | import com.noorganization.googlecertificationkotlin.codelab_material_component.network.ProductEntry
9 | import com.noorganization.googlecertificationkotlin.R
10 |
11 | /**
12 | * Adapter used to show an asymmetric grid of products, with 2 items in the first column, and 1
13 | * item in the second column, and so on.
14 | */
15 | class StaggeredProductCardRecyclerViewAdapter(private val productList: List?) : RecyclerView.Adapter() {
16 |
17 | override fun getItemViewType(position: Int): Int {
18 | return position % 3
19 | }
20 |
21 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StaggeredProductCardViewHolder {
22 | var layoutId = R.layout.shr_staggered_product_card_first
23 | if (viewType == 1) {
24 | layoutId = R.layout.shr_staggered_product_card_second
25 | } else if (viewType == 2) {
26 | layoutId = R.layout.shr_staggered_product_card_third
27 | }
28 |
29 | val layoutView = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
30 | return StaggeredProductCardViewHolder(layoutView)
31 | }
32 |
33 | override fun onBindViewHolder(holder: StaggeredProductCardViewHolder, position: Int) {
34 | if (productList != null && position < productList.size) {
35 | val product = productList[position]
36 | holder.productTitle.text = product.title
37 | holder.productPrice.text = product.price
38 | ImageRequester.setImageFromUrl(holder.productImage, product.url)
39 | }
40 | }
41 |
42 | override fun getItemCount(): Int {
43 | return productList?.size ?: 0
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_material_component/staggeredgridlayout/StaggeredProductCardViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_material_component.staggeredgridlayout
2 |
3 | import android.view.View
4 | import android.widget.TextView
5 | import androidx.recyclerview.widget.RecyclerView
6 |
7 | import com.android.volley.toolbox.NetworkImageView
8 | import com.noorganization.googlecertificationkotlin.R
9 |
10 | class StaggeredProductCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
11 |
12 | var productImage: NetworkImageView = itemView.findViewById(R.id.product_image)
13 | var productTitle: TextView = itemView.findViewById(R.id.product_title)
14 | var productPrice: TextView = itemView.findViewById(R.id.product_price)
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/noorganization/googlecertificationkotlin/codelab_room_coroutines/NewWordActivity.kt:
--------------------------------------------------------------------------------
1 | package com.noorganization.googlecertificationkotlin.codelab_room_coroutines
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import androidx.appcompat.app.AppCompatActivity
6 | import android.os.Bundle
7 | import android.text.TextUtils
8 | import android.widget.Button
9 | import android.widget.EditText
10 | import com.noorganization.googlecertificationkotlin.R
11 | import com.noorganization.googlecertificationkotlin.codelab_room_livedata_viewmodel.db.Word
12 |
13 | class NewWordActivity : AppCompatActivity() {
14 | private lateinit var editWordView: EditText
15 |
16 | public override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | setContentView(R.layout.activity_new_word)
19 | editWordView = findViewById(R.id.edit_word)
20 |
21 | val button = findViewById