├── .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 | 5 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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