> { videos ->
+ videos?.apply {
+ viewModelAdapter?.videos = videos
+ }
+ })
+ }
+
+ /**
+ * Called to have the fragment instantiate its user interface view.
+ *
+ * If you return a View from here, you will later be called in
+ * {@link #onDestroyView} when the view is being released.
+ *
+ * @param inflater The LayoutInflater object that can be used to inflate
+ * any views in the fragment,
+ * @param container If non-null, this is the parent view that the fragment's
+ * UI should be attached to. The fragment should not add the view itself,
+ * but this can be used to generate the LayoutParams of the view.
+ * @param savedInstanceState If non-null, this fragment is being re-constructed
+ * from a previous saved state as given here.
+ *
+ * @return Return the View for the fragment's UI.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val binding: FragmentDevByteBinding = DataBindingUtil.inflate(
+ inflater,
+ R.layout.fragment_dev_byte,
+ container,
+ false)
+ // Set the lifecycleOwner so DataBinding can observe LiveData
+ binding.setLifecycleOwner(viewLifecycleOwner)
+
+ binding.viewModel = viewModel
+
+ viewModelAdapter = DevByteAdapter(VideoClick {
+ // When a video is clicked this block or lambda will be called by DevByteAdapter
+
+ // context is not around, we can safely discard this click since the Fragment is no
+ // longer on the screen
+ val packageManager = context?.packageManager ?: return@VideoClick
+
+ // Try to generate a direct intent to the YouTube app
+ var intent = Intent(Intent.ACTION_VIEW, it.launchUri)
+ if(intent.resolveActivity(packageManager) == null) {
+ // YouTube app isn't found, use the web url
+ intent = Intent(Intent.ACTION_VIEW, Uri.parse(it.url))
+ }
+
+ startActivity(intent)
+ })
+
+ binding.root.findViewById(R.id.recycler_view).apply {
+ layoutManager = LinearLayoutManager(context)
+ adapter = viewModelAdapter
+ }
+
+
+ // Observer for the network error.
+ viewModel.eventNetworkError.observe(this, Observer { isNetworkError ->
+ if (isNetworkError) onNetworkError()
+ })
+
+ return binding.root
+ }
+
+ /**
+ * Method for displaying a Toast error message for network errors.
+ */
+ private fun onNetworkError() {
+ if(!viewModel.isNetworkErrorShown.value!!) {
+ Toast.makeText(activity, "Network Error", Toast.LENGTH_LONG).show()
+ viewModel.onNetworkErrorShown()
+ }
+ }
+
+ /**
+ * Helper method to generate YouTube app links
+ */
+ private val DevByteVideo.launchUri: Uri
+ get() {
+ val httpUri = Uri.parse(url)
+ return Uri.parse("vnd.youtube:" + httpUri.getQueryParameter("v"))
+ }
+}
+
+/**
+ * Click listener for Videos. By giving the block a name it helps a reader understand what it does.
+ *
+ */
+class VideoClick(val block: (DevByteVideo) -> Unit) {
+ /**
+ * Called when a video is clicked
+ *
+ * @param video the video that was clicked
+ */
+ fun onClick(video: DevByteVideo) = block(video)
+}
+
+/**
+ * RecyclerView Adapter for setting up data binding on the items in the list.
+ */
+class DevByteAdapter(val callback: VideoClick) : RecyclerView.Adapter() {
+
+ /**
+ * The videos that our Adapter will show
+ */
+ var videos: List = emptyList()
+ set(value) {
+ field = value
+ // For an extra challenge, update this to use the paging library.
+
+ // Notify any registered observers that the data set has changed. This will cause every
+ // element in our RecyclerView to be invalidated.
+ notifyDataSetChanged()
+ }
+
+ /**
+ * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
+ * an item.
+ */
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DevByteViewHolder {
+ val withDataBinding: DevbyteItemBinding = DataBindingUtil.inflate(
+ LayoutInflater.from(parent.context),
+ DevByteViewHolder.LAYOUT,
+ parent,
+ false)
+ return DevByteViewHolder(withDataBinding)
+ }
+
+ override fun getItemCount() = videos.size
+
+ /**
+ * Called by RecyclerView to display the data at the specified position. This method should
+ * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
+ * position.
+ */
+ override fun onBindViewHolder(holder: DevByteViewHolder, position: Int) {
+ holder.viewDataBinding.also {
+ it.video = videos[position]
+ it.videoCallback = callback
+ }
+ }
+
+}
+
+/**
+ * ViewHolder for DevByte items. All work is done by data binding.
+ */
+class DevByteViewHolder(val viewDataBinding: DevbyteItemBinding) :
+ RecyclerView.ViewHolder(viewDataBinding.root) {
+ companion object {
+ @LayoutRes
+ val LAYOUT = R.layout.devbyte_item
+ }
+}
\ No newline at end of file
diff --git a/DevBytesRepository/app/src/main/java/com/example/android/devbyteviewer/util/BindingAdapters.kt b/DevBytesRepository/app/src/main/java/com/example/android/devbyteviewer/util/BindingAdapters.kt
new file mode 100755
index 0000000..e03a6d4
--- /dev/null
+++ b/DevBytesRepository/app/src/main/java/com/example/android/devbyteviewer/util/BindingAdapters.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.util
+
+import android.view.View
+import android.widget.ImageView
+import androidx.databinding.BindingAdapter
+import com.bumptech.glide.Glide
+
+/**
+ * Binding adapter used to hide the spinner once data is available.
+ */
+@BindingAdapter("isNetworkError", "playlist")
+fun hideIfNetworkError(view: View, isNetWorkError: Boolean, playlist: Any?) {
+ view.visibility = if (playlist != null) View.GONE else View.VISIBLE
+
+ if(isNetWorkError) {
+ view.visibility = View.GONE
+ }
+}
+
+/**
+ * Binding adapter used to display images from URL using Glide
+ */
+@BindingAdapter("imageUrl")
+fun setImageUrl(imageView: ImageView, url: String) {
+ Glide.with(imageView.context).load(url).into(imageView)
+}
\ No newline at end of file
diff --git a/DevBytesRepository/app/src/main/java/com/example/android/devbyteviewer/util/Util.kt b/DevBytesRepository/app/src/main/java/com/example/android/devbyteviewer/util/Util.kt
new file mode 100755
index 0000000..8da9996
--- /dev/null
+++ b/DevBytesRepository/app/src/main/java/com/example/android/devbyteviewer/util/Util.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.util
+
+private val PUNCTUATION = listOf(", ", "; ", ": ", " ")
+
+/**
+ * Truncate long text with a preference for word boundaries and without trailing punctuation.
+ */
+fun String.smartTruncate(length: Int): String {
+ val words = split(" ")
+ var added = 0
+ var hasMore = false
+ val builder = StringBuilder()
+ for (word in words) {
+ if (builder.length > length) {
+ hasMore = true
+ break
+ }
+ builder.append(word)
+ builder.append(" ")
+ added += 1
+ }
+
+ PUNCTUATION.map {
+ if (builder.endsWith(it)) {
+ builder.replace(builder.length - it.length, builder.length, "")
+ }
+ }
+
+ if (hasMore) {
+ builder.append("...")
+ }
+ return builder.toString()
+}
\ No newline at end of file
diff --git a/DevBytesRepository/app/src/main/java/com/example/android/devbyteviewer/viewmodels/DevByteViewModel.kt b/DevBytesRepository/app/src/main/java/com/example/android/devbyteviewer/viewmodels/DevByteViewModel.kt
new file mode 100755
index 0000000..77d91ad
--- /dev/null
+++ b/DevBytesRepository/app/src/main/java/com/example/android/devbyteviewer/viewmodels/DevByteViewModel.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.viewmodels
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.devbyteviewer.database.getDatabase
+import com.example.android.devbyteviewer.domain.DevByteVideo
+import com.example.android.devbyteviewer.network.DevByteNetwork
+import com.example.android.devbyteviewer.network.asDomainModel
+import com.example.android.devbyteviewer.repository.VideosRepository
+import kotlinx.coroutines.*
+import java.io.IOException
+
+/**
+ * DevByteViewModel designed to store and manage UI-related data in a lifecycle conscious way. This
+ * allows data to survive configuration changes such as screen rotations. In addition, background
+ * work such as fetching network results can continue through configuration changes and deliver
+ * results after the new Fragment or Activity is available.
+ *
+ * @param application The application that this viewmodel is attached to, it's safe to hold a
+ * reference to applications across rotation since Application is never recreated during actiivty
+ * or fragment lifecycle events.
+ */
+class DevByteViewModel(application: Application) : AndroidViewModel(application) {
+
+
+ /**
+ * The data source this ViewModel will fetch results from.
+ */
+ private val videosRepository = VideosRepository(getDatabase(application))
+
+ /**
+ * A playlist of videos displayed on the screen.
+ */
+ val playlist = videosRepository.videos
+
+ /**
+ * This is the job for all coroutines started by this ViewModel.
+ *
+ * Cancelling this job will cancel all coroutines started by this ViewModel.
+ */
+ private val viewModelJob = SupervisorJob()
+
+ /**
+ * This is the main scope for all coroutines launched by MainViewModel.
+ *
+ * Since we pass viewModelJob, you can cancel all coroutines launched by uiScope by calling
+ * viewModelJob.cancel()
+ */
+ private val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Main)
+
+ /**
+ * Event triggered for network error. This is private to avoid exposing a
+ * way to set this value to observers.
+ */
+ private var _eventNetworkError = MutableLiveData(false)
+
+ /**
+ * Event triggered for network error. Views should use this to get access
+ * to the data.
+ */
+ val eventNetworkError: LiveData
+ get() = _eventNetworkError
+
+ /**
+ * Flag to display the error message. This is private to avoid exposing a
+ * way to set this value to observers.
+ */
+ private var _isNetworkErrorShown = MutableLiveData(false)
+
+ /**
+ * Flag to display the error message. Views should use this to get access
+ * to the data.
+ */
+ val isNetworkErrorShown: LiveData
+ get() = _isNetworkErrorShown
+
+ /**
+ * init{} is called immediately when this ViewModel is created.
+ */
+ init {
+ refreshDataFromRepository()
+ }
+
+ /**
+ * Refresh data from the repository. Use a coroutine launch to run in a
+ * background thread.
+ */
+ private fun refreshDataFromRepository() {
+ viewModelScope.launch {
+ try {
+ videosRepository.refreshVideos()
+ _eventNetworkError.value = false
+ _isNetworkErrorShown.value = false
+
+ } catch (networkError: IOException) {
+ // Show a Toast error message and hide the progress bar.
+ if(playlist.value!!.isEmpty())
+ _eventNetworkError.value = true
+ }
+ }
+ }
+
+
+ /**
+ * Resets the network error flag.
+ */
+ fun onNetworkErrorShown() {
+ _isNetworkErrorShown.value = true
+ }
+
+
+ /**
+ * Cancel all coroutines when the ViewModel is cleared
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+ /**
+ * Factory for constructing DevByteViewModel with parameter
+ */
+ class Factory(val app: Application) : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(DevByteViewModel::class.java)) {
+ @Suppress("UNCHECKED_CAST")
+ return DevByteViewModel(app) as T
+ }
+ throw IllegalArgumentException("Unable to construct viewmodel")
+ }
+ }
+}
diff --git a/DevBytesRepository/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/DevBytesRepository/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100755
index 0000000..56a2561
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DevBytesRepository/app/src/main/res/drawable/ic_launcher_background.xml b/DevBytesRepository/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100755
index 0000000..4f6c04c
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DevBytesRepository/app/src/main/res/drawable/ic_play_circle_outline_black_48dp.xml b/DevBytesRepository/app/src/main/res/drawable/ic_play_circle_outline_black_48dp.xml
new file mode 100755
index 0000000..0d2e285
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/drawable/ic_play_circle_outline_black_48dp.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/DevBytesRepository/app/src/main/res/layout/activity_dev_byte_viewer.xml b/DevBytesRepository/app/src/main/res/layout/activity_dev_byte_viewer.xml
new file mode 100755
index 0000000..547d362
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/layout/activity_dev_byte_viewer.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesRepository/app/src/main/res/layout/devbyte_item.xml b/DevBytesRepository/app/src/main/res/layout/devbyte_item.xml
new file mode 100755
index 0000000..926d2f4
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/layout/devbyte_item.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesRepository/app/src/main/res/layout/fragment_dev_byte.xml b/DevBytesRepository/app/src/main/res/layout/fragment_dev_byte.xml
new file mode 100755
index 0000000..2357206
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/layout/fragment_dev_byte.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DevBytesRepository/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/DevBytesRepository/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100755
index 0000000..9b4f225
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesRepository/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/DevBytesRepository/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100755
index 0000000..9b4f225
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesRepository/app/src/main/res/mipmap-hdpi/ic_launcher.png b/DevBytesRepository/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..898f3ed
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/DevBytesRepository/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/DevBytesRepository/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dffca36
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/DevBytesRepository/app/src/main/res/mipmap-mdpi/ic_launcher.png b/DevBytesRepository/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..64ba76f
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/DevBytesRepository/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/DevBytesRepository/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dae5e08
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/DevBytesRepository/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/DevBytesRepository/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..e5ed465
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/DevBytesRepository/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/DevBytesRepository/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..14ed0af
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/DevBytesRepository/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/DevBytesRepository/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..b0907ca
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/DevBytesRepository/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/DevBytesRepository/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..d8ae031
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/DevBytesRepository/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/DevBytesRepository/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..2c18de9
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/DevBytesRepository/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/DevBytesRepository/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..beed3cd
Binary files /dev/null and b/DevBytesRepository/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/DevBytesRepository/app/src/main/res/navigation/nav_graph.xml b/DevBytesRepository/app/src/main/res/navigation/nav_graph.xml
new file mode 100755
index 0000000..76fa28b
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesRepository/app/src/main/res/values/colors.xml b/DevBytesRepository/app/src/main/res/values/colors.xml
new file mode 100755
index 0000000..2a770ef
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ #1E8E3E
+ #0D652D
+ #E37400
+
+ #ffDDDDDD
+ #ff666666
+ #ff222222
+
diff --git a/DevBytesRepository/app/src/main/res/values/strings.xml b/DevBytesRepository/app/src/main/res/values/strings.xml
new file mode 100755
index 0000000..91f4c4d
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/values/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ DevByte Viewer
+
diff --git a/DevBytesRepository/app/src/main/res/values/styles.xml b/DevBytesRepository/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..8fc17a5
--- /dev/null
+++ b/DevBytesRepository/app/src/main/res/values/styles.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/DevBytesRepository/build.gradle b/DevBytesRepository/build.gradle
new file mode 100755
index 0000000..678b933
--- /dev/null
+++ b/DevBytesRepository/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext.kotlin_version = '1.3.31'
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.4.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ maven { url "https://kotlin.bintray.com/kotlinx/" }
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/DevBytesRepository/gradle.properties b/DevBytesRepository/gradle.properties
new file mode 100755
index 0000000..92cca3f
--- /dev/null
+++ b/DevBytesRepository/gradle.properties
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2019 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/DevBytesRepository/gradle/wrapper/gradle-wrapper.jar b/DevBytesRepository/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..f6b961f
Binary files /dev/null and b/DevBytesRepository/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/DevBytesRepository/gradle/wrapper/gradle-wrapper.properties b/DevBytesRepository/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..f7d554f
--- /dev/null
+++ b/DevBytesRepository/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Apr 25 13:35:31 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/DevBytesRepository/gradlew b/DevBytesRepository/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/DevBytesRepository/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/DevBytesRepository/gradlew.bat b/DevBytesRepository/gradlew.bat
new file mode 100755
index 0000000..e95643d
--- /dev/null
+++ b/DevBytesRepository/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/DevBytesRepository/settings.gradle b/DevBytesRepository/settings.gradle
new file mode 100755
index 0000000..116b21c
--- /dev/null
+++ b/DevBytesRepository/settings.gradle
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+include ':app'
diff --git a/DevBytesWorkManager/README.md b/DevBytesWorkManager/README.md
new file mode 100755
index 0000000..9477fac
--- /dev/null
+++ b/DevBytesWorkManager/README.md
@@ -0,0 +1,56 @@
+DevByteWorkManager - Solution Code
+==================================
+
+Solution code for the WorkManager codelab.
+
+Introduction
+------------
+
+DevByteWorkManager app displays a list of DevByte videos. DevByte videos are
+short videos made by the Google Android developer relations team to introduce
+new developer features on Android. This app fetches the DevByte video playlist
+from the network using the Retrofit library and displays it on the screen. The
+network fetch is scheduled periodically once a day using the WorkManager.
+Constraints like device idle, unmettered network and so on are added to the work
+request to optimise the battery performance.
+
+
+Pre-requisites
+--------------
+
+You need to know:
+- How to open, build, and run Android apps with Android Studio.
+- The Android Architecture Components like, ViewModel, LiveData, and Room.
+- Building and launching a coroutine.
+- Read the logs using the Logcat.
+- Binding adapters in Data Binding.
+- How to use Retrofit network library.
+- Loading cached data using a Repository pattern.
+
+
+
+Getting Started
+---------------
+
+1. Download and run the app.
+2. You need Android Studio 3.2 or higher to build this project.
+
+License
+-------
+
+Copyright 2019 Google, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements. See the NOTICE file distributed with this work for
+additional information regarding copyright ownership. The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/DevBytesWorkManager/app/build.gradle b/DevBytesWorkManager/app/build.gradle
new file mode 100755
index 0000000..b7bfca5
--- /dev/null
+++ b/DevBytesWorkManager/app/build.gradle
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
+apply plugin: "androidx.navigation.safeargs"
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.example.android.devbyteviewer"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ dataBinding {
+ enabled = true
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ // support libraries
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'com.google.android.material:material:1.0.0'
+
+ // Android KTX
+ implementation 'androidx.core:core-ktx:1.0.2'
+
+ // constraint layout
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+
+ // navigation
+ def nav_version = "1.0.0"
+ implementation "android.arch.navigation:navigation-fragment-ktx:$nav_version"
+ implementation "android.arch.navigation:navigation-ui-ktx:$nav_version"
+
+ // coroutines for getting off the UI thread
+ def coroutines = "1.0.1"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
+
+ // retrofit for networking
+ implementation 'com.squareup.retrofit2:retrofit:2.5.0'
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-moshi:2.5.0'
+
+ // moshi for parsing the JSON format
+ def moshi_version = "1.6.0"
+ implementation "com.squareup.moshi:moshi:$moshi_version"
+ implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
+ kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
+
+ // joda time library for dealing with time
+ implementation 'joda-time:joda-time:2.10'
+
+ // arch components
+ // ViewModel and LiveData
+ def lifecycle_version = "2.2.0-alpha01"
+ implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
+
+ // logging
+ implementation 'com.jakewharton.timber:timber:4.7.1'
+
+ // glide for images
+ implementation 'com.github.bumptech.glide:glide:4.8.0'
+ kapt 'com.github.bumptech.glide:compiler:4.7.1'
+
+ // Room dependency
+ def room_version = "2.1.0-beta01"
+ implementation "androidx.room:room-runtime:$room_version"
+ kapt "androidx.room:room-compiler:$room_version"
+
+ // WorkManager dependency
+ def work_version = "1.0.1"
+ implementation "android.arch.work:work-runtime-ktx:$work_version"
+}
diff --git a/DevBytesWorkManager/app/proguard-rules.pro b/DevBytesWorkManager/app/proguard-rules.pro
new file mode 100755
index 0000000..f1b4245
--- /dev/null
+++ b/DevBytesWorkManager/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/DevBytesWorkManager/app/src/main/AndroidManifest.xml b/DevBytesWorkManager/app/src/main/AndroidManifest.xml
new file mode 100755
index 0000000..464fed3
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/DevByteApplication.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/DevByteApplication.kt
new file mode 100755
index 0000000..900b1a6
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/DevByteApplication.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer
+
+import android.app.Application
+import android.os.Build
+import androidx.work.*
+
+import com.example.android.devbyteviewer.work.RefreshDataWorker
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import java.util.concurrent.TimeUnit
+
+/**
+ * Override application to setup background work via WorkManager
+ */
+class DevByteApplication : Application() {
+
+ private val applicationScope = CoroutineScope(Dispatchers.Default)
+
+ /**
+ * onCreate is called before the first screen is shown to the user.
+ *
+ * Use it to setup any background tasks, running expensive setup operations in a background
+ * thread to avoid delaying app start.
+ */
+ override fun onCreate() {
+ super.onCreate()
+ delayedInit()
+ }
+
+ private fun delayedInit() {
+ applicationScope.launch {
+ Timber.plant(Timber.DebugTree())
+ setupRecurringWork()
+ }
+ }
+
+ /**
+ * Setup WorkManager background job to 'fetch' new network data daily.
+ */
+ private fun setupRecurringWork() {
+
+ val constraints = Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.UNMETERED)
+ .setRequiresCharging(true)
+ .setRequiresBatteryNotLow(true)
+ .apply {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ setRequiresDeviceIdle(true)
+ }
+ }
+ .build()
+
+ val repeatingRequest = PeriodicWorkRequestBuilder(1, TimeUnit.DAYS)
+ .setConstraints(constraints)
+ .build()
+
+ Timber.d("WorkManager: Periodic Work request for sync is scheduled")
+ WorkManager.getInstance().enqueueUniquePeriodicWork(
+ RefreshDataWorker.WORK_NAME,
+ ExistingPeriodicWorkPolicy.KEEP,
+ repeatingRequest)
+ }
+}
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/database/DatabaseEntities.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/database/DatabaseEntities.kt
new file mode 100755
index 0000000..1e39a26
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/database/DatabaseEntities.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.database
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.example.android.devbyteviewer.domain.DevByteVideo
+
+
+/**
+ * Database entities go in this file. These are responsible for reading and writing from the
+ * database.
+ */
+
+
+/**
+ * DatabaseVideo represents a video entity in the database.
+ */
+@Entity
+data class DatabaseVideo constructor(
+ @PrimaryKey
+ val url: String,
+ val updated: String,
+ val title: String,
+ val description: String,
+ val thumbnail: String)
+
+
+/**
+ * Map DatabaseVideos to domain entities
+ */
+fun List.asDomainModel(): List {
+ return map {
+ DevByteVideo(
+ url = it.url,
+ title = it.title,
+ description = it.description,
+ updated = it.updated,
+ thumbnail = it.thumbnail)
+ }
+}
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/database/Room.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/database/Room.kt
new file mode 100755
index 0000000..be12dcd
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/database/Room.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.database
+
+import android.content.Context
+import androidx.lifecycle.LiveData
+import androidx.room.*
+
+@Dao
+interface VideoDao {
+ @Query("select * from databasevideo")
+ fun getVideos(): LiveData>
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insertAll( videos: List)
+}
+
+
+
+@Database(entities = [DatabaseVideo::class], version = 1)
+abstract class VideosDatabase: RoomDatabase() {
+ abstract val videoDao: VideoDao
+}
+
+private lateinit var INSTANCE: VideosDatabase
+
+fun getDatabase(context: Context): VideosDatabase {
+ synchronized(VideosDatabase::class.java) {
+ if (!::INSTANCE.isInitialized) {
+ INSTANCE = Room.databaseBuilder(context.applicationContext,
+ VideosDatabase::class.java,
+ "videos").build()
+ }
+ }
+ return INSTANCE
+}
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/domain/Models.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/domain/Models.kt
new file mode 100755
index 0000000..b7d5169
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/domain/Models.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.domain
+
+import com.example.android.devbyteviewer.util.smartTruncate
+
+/**
+ * Domain objects are plain Kotlin data classes that represent the things in our app. These are the
+ * objects that should be displayed on screen, or manipulated by the app.
+ *
+ * @see database for objects that are mapped to the database
+ * @see network for objects that parse or prepare network calls
+ */
+
+/**
+ * Videos represent a devbyte that can be played.
+ */
+data class DevByteVideo(val title: String,
+ val description: String,
+ val url: String,
+ val updated: String,
+ val thumbnail: String) {
+
+ /**
+ * Short description is used for displaying truncated descriptions in the UI
+ */
+ val shortDescription: String
+ get() = description.smartTruncate(200)
+}
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/network/DataTransferObjects.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/network/DataTransferObjects.kt
new file mode 100755
index 0000000..d0dcfb5
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/network/DataTransferObjects.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.network
+
+import com.example.android.devbyteviewer.database.DatabaseVideo
+import com.example.android.devbyteviewer.domain.DevByteVideo
+import com.squareup.moshi.JsonClass
+
+/**
+ * DataTransferObjects go in this file. These are responsible for parsing responses from the server
+ * or formatting objects to send to the server. You should convert these to domain objects before
+ * using them.
+ *
+ * @see domain package for
+ */
+
+/**
+ * VideoHolder holds a list of Videos.
+ *
+ * This is to parse first level of our network result which looks like
+ *
+ * {
+ * "videos": []
+ * }
+ */
+@JsonClass(generateAdapter = true)
+data class NetworkVideoContainer(val videos: List)
+
+/**
+ * Videos represent a devbyte that can be played.
+ */
+@JsonClass(generateAdapter = true)
+data class NetworkVideo(
+ val title: String,
+ val description: String,
+ val url: String,
+ val updated: String,
+ val thumbnail: String,
+ val closedCaptions: String?)
+
+/**
+ * Convert Network results to database objects
+ */
+fun NetworkVideoContainer.asDomainModel(): List {
+ return videos.map {
+ DevByteVideo(
+ title = it.title,
+ description = it.description,
+ url = it.url,
+ updated = it.updated,
+ thumbnail = it.thumbnail)
+ }
+}
+
+
+/**
+ * Convert Network results to database objects
+ */
+fun NetworkVideoContainer.asDatabaseModel(): List {
+ return videos.map {
+ DatabaseVideo(
+ title = it.title,
+ description = it.description,
+ url = it.url,
+ updated = it.updated,
+ thumbnail = it.thumbnail)
+ }
+}
+
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/network/Service.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/network/Service.kt
new file mode 100755
index 0000000..8db91dc
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/network/Service.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.network
+
+import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
+import kotlinx.coroutines.Deferred
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+import retrofit2.http.GET
+
+// Since we only have one service, this can all go in one file.
+// If you add more services, split this to multiple files and make sure to share the retrofit
+// object between services.
+
+/**
+ * A retrofit service to fetch devbyte playlist.
+ */
+
+interface DevbyteService {
+ @GET("devbytes")
+ fun getPlaylist(): Deferred
+}
+
+/**
+ * Main entry point for network access. Call like `DevByteNetwork.devbytes.getPlaylist()`
+ */
+object DevByteNetwork {
+
+ // Configure retrofit to parse JSON and use coroutines
+ private val retrofit = Retrofit.Builder()
+ .baseUrl("https://android-kotlin-fun-mars-server.appspot.com/")
+ .addConverterFactory(MoshiConverterFactory.create())
+ .addCallAdapterFactory(CoroutineCallAdapterFactory())
+ .build()
+
+ val devbytes = retrofit.create(DevbyteService::class.java)
+
+}
+
+
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/repository/VideosRepository.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/repository/VideosRepository.kt
new file mode 100755
index 0000000..331f28e
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/repository/VideosRepository.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.repository
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Transformations
+import com.example.android.devbyteviewer.database.VideosDatabase
+import com.example.android.devbyteviewer.database.asDomainModel
+import com.example.android.devbyteviewer.domain.DevByteVideo
+import com.example.android.devbyteviewer.network.DevByteNetwork
+import com.example.android.devbyteviewer.network.asDatabaseModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import timber.log.Timber
+
+/**
+ * Repository for fetching devbyte videos from the network and storing them on disk
+ */
+class VideosRepository(private val database: VideosDatabase) {
+
+ val videos: LiveData> = Transformations.map(database.videoDao.getVideos()) {
+ it.asDomainModel()
+ }
+
+ /**
+ * Refresh the videos stored in the offline cache.
+ *
+ * This function uses the IO dispatcher to ensure the database insert database operation
+ * happens on the IO dispatcher. By switching to the IO dispatcher using `withContext` this
+ * function is now safe to call from any thread including the Main thread.
+ *
+ */
+ suspend fun refreshVideos() {
+ withContext(Dispatchers.IO) {
+ Timber.d("refresh videos is called");
+ val playlist = DevByteNetwork.devbytes.getPlaylist().await()
+ database.videoDao.insertAll(playlist.asDatabaseModel())
+ }
+ }
+}
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/ui/DevByteActivity.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/ui/DevByteActivity.kt
new file mode 100755
index 0000000..b9a3647
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/ui/DevByteActivity.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.ui
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.example.android.devbyteviewer.R
+
+/**
+ * This is a single activity application that uses the Navigation library. Content is displayed
+ * by Fragments.
+ */
+class DevByteActivity : AppCompatActivity() {
+
+ /**
+ * Called when the activity is starting. This is where most initialization
+ * should go
+ */
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_dev_byte_viewer)
+ }
+}
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/ui/DevByteFragment.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/ui/DevByteFragment.kt
new file mode 100755
index 0000000..8d88b1f
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/ui/DevByteFragment.kt
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.ui
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.annotation.LayoutRes
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.devbyteviewer.R
+import com.example.android.devbyteviewer.databinding.DevbyteItemBinding
+import com.example.android.devbyteviewer.databinding.FragmentDevByteBinding
+import com.example.android.devbyteviewer.domain.DevByteVideo
+import com.example.android.devbyteviewer.viewmodels.DevByteViewModel
+
+/**
+ * Show a list of DevBytes on screen.
+ */
+class DevByteFragment : Fragment() {
+
+ /**
+ * One way to delay creation of the viewModel until an appropriate lifecycle method is to use
+ * lazy. This requires that viewModel not be referenced before onActivityCreated, which we
+ * do in this Fragment.
+ */
+ private val viewModel: DevByteViewModel by lazy {
+ val activity = requireNotNull(this.activity) {
+ "You can only access the viewModel after onActivityCreated()"
+ }
+ ViewModelProviders.of(this, DevByteViewModel.Factory(activity.application))
+ .get(DevByteViewModel::class.java)
+ }
+
+ /**
+ * RecyclerView Adapter for converting a list of Video to cards.
+ */
+ private var viewModelAdapter: DevByteAdapter? = null
+
+ /**
+ * Called when the fragment's activity has been created and this
+ * fragment's view hierarchy instantiated. It can be used to do final
+ * initialization once these pieces are in place, such as retrieving
+ * views or restoring state.
+ */
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ viewModel.playlist.observe(viewLifecycleOwner, Observer> { videos ->
+ videos?.apply {
+ viewModelAdapter?.videos = videos
+ }
+ })
+ }
+
+ /**
+ * Called to have the fragment instantiate its user interface view.
+ *
+ * If you return a View from here, you will later be called in
+ * {@link #onDestroyView} when the view is being released.
+ *
+ * @param inflater The LayoutInflater object that can be used to inflate
+ * any views in the fragment,
+ * @param container If non-null, this is the parent view that the fragment's
+ * UI should be attached to. The fragment should not add the view itself,
+ * but this can be used to generate the LayoutParams of the view.
+ * @param savedInstanceState If non-null, this fragment is being re-constructed
+ * from a previous saved state as given here.
+ *
+ * @return Return the View for the fragment's UI.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val binding: FragmentDevByteBinding = DataBindingUtil.inflate(
+ inflater,
+ R.layout.fragment_dev_byte,
+ container,
+ false)
+ // Set the lifecycleOwner so DataBinding can observe LiveData
+ binding.setLifecycleOwner(viewLifecycleOwner)
+
+ binding.viewModel = viewModel
+
+ viewModelAdapter = DevByteAdapter(VideoClick {
+ // When a video is clicked this block or lambda will be called by DevByteAdapter
+
+ // context is not around, we can safely discard this click since the Fragment is no
+ // longer on the screen
+ val packageManager = context?.packageManager ?: return@VideoClick
+
+ // Try to generate a direct intent to the YouTube app
+ var intent = Intent(Intent.ACTION_VIEW, it.launchUri)
+ if(intent.resolveActivity(packageManager) == null) {
+ // YouTube app isn't found, use the web url
+ intent = Intent(Intent.ACTION_VIEW, Uri.parse(it.url))
+ }
+
+ startActivity(intent)
+ })
+
+ binding.root.findViewById(R.id.recycler_view).apply {
+ layoutManager = LinearLayoutManager(context)
+ adapter = viewModelAdapter
+ }
+
+
+ // Observer for the network error.
+ viewModel.eventNetworkError.observe(this, Observer { isNetworkError ->
+ if (isNetworkError) onNetworkError()
+ })
+
+ return binding.root
+ }
+
+ /**
+ * Method for displaying a Toast error message for network errors.
+ */
+ private fun onNetworkError() {
+ if(!viewModel.isNetworkErrorShown.value!!) {
+ Toast.makeText(activity, "Network Error", Toast.LENGTH_LONG).show()
+ viewModel.onNetworkErrorShown()
+ }
+ }
+
+ /**
+ * Helper method to generate YouTube app links
+ */
+ private val DevByteVideo.launchUri: Uri
+ get() {
+ val httpUri = Uri.parse(url)
+ return Uri.parse("vnd.youtube:" + httpUri.getQueryParameter("v"))
+ }
+}
+
+/**
+ * Click listener for Videos. By giving the block a name it helps a reader understand what it does.
+ *
+ */
+class VideoClick(val block: (DevByteVideo) -> Unit) {
+ /**
+ * Called when a video is clicked
+ *
+ * @param video the video that was clicked
+ */
+ fun onClick(video: DevByteVideo) = block(video)
+}
+
+/**
+ * RecyclerView Adapter for setting up data binding on the items in the list.
+ */
+class DevByteAdapter(val callback: VideoClick) : RecyclerView.Adapter() {
+
+ /**
+ * The videos that our Adapter will show
+ */
+ var videos: List = emptyList()
+ set(value) {
+ field = value
+ // For an extra challenge, update this to use the paging library.
+
+ // Notify any registered observers that the data set has changed. This will cause every
+ // element in our RecyclerView to be invalidated.
+ notifyDataSetChanged()
+ }
+
+ /**
+ * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
+ * an item.
+ */
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DevByteViewHolder {
+ val withDataBinding: DevbyteItemBinding = DataBindingUtil.inflate(
+ LayoutInflater.from(parent.context),
+ DevByteViewHolder.LAYOUT,
+ parent,
+ false)
+ return DevByteViewHolder(withDataBinding)
+ }
+
+ override fun getItemCount() = videos.size
+
+ /**
+ * Called by RecyclerView to display the data at the specified position. This method should
+ * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
+ * position.
+ */
+ override fun onBindViewHolder(holder: DevByteViewHolder, position: Int) {
+ holder.viewDataBinding.also {
+ it.video = videos[position]
+ it.videoCallback = callback
+ }
+ }
+
+}
+
+/**
+ * ViewHolder for DevByte items. All work is done by data binding.
+ */
+class DevByteViewHolder(val viewDataBinding: DevbyteItemBinding) :
+ RecyclerView.ViewHolder(viewDataBinding.root) {
+ companion object {
+ @LayoutRes
+ val LAYOUT = R.layout.devbyte_item
+ }
+}
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/util/BindingAdapters.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/util/BindingAdapters.kt
new file mode 100755
index 0000000..e03a6d4
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/util/BindingAdapters.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.util
+
+import android.view.View
+import android.widget.ImageView
+import androidx.databinding.BindingAdapter
+import com.bumptech.glide.Glide
+
+/**
+ * Binding adapter used to hide the spinner once data is available.
+ */
+@BindingAdapter("isNetworkError", "playlist")
+fun hideIfNetworkError(view: View, isNetWorkError: Boolean, playlist: Any?) {
+ view.visibility = if (playlist != null) View.GONE else View.VISIBLE
+
+ if(isNetWorkError) {
+ view.visibility = View.GONE
+ }
+}
+
+/**
+ * Binding adapter used to display images from URL using Glide
+ */
+@BindingAdapter("imageUrl")
+fun setImageUrl(imageView: ImageView, url: String) {
+ Glide.with(imageView.context).load(url).into(imageView)
+}
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/util/Util.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/util/Util.kt
new file mode 100755
index 0000000..8da9996
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/util/Util.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.util
+
+private val PUNCTUATION = listOf(", ", "; ", ": ", " ")
+
+/**
+ * Truncate long text with a preference for word boundaries and without trailing punctuation.
+ */
+fun String.smartTruncate(length: Int): String {
+ val words = split(" ")
+ var added = 0
+ var hasMore = false
+ val builder = StringBuilder()
+ for (word in words) {
+ if (builder.length > length) {
+ hasMore = true
+ break
+ }
+ builder.append(word)
+ builder.append(" ")
+ added += 1
+ }
+
+ PUNCTUATION.map {
+ if (builder.endsWith(it)) {
+ builder.replace(builder.length - it.length, builder.length, "")
+ }
+ }
+
+ if (hasMore) {
+ builder.append("...")
+ }
+ return builder.toString()
+}
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/viewmodels/DevByteViewModel.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/viewmodels/DevByteViewModel.kt
new file mode 100755
index 0000000..77d91ad
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/viewmodels/DevByteViewModel.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.viewmodels
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.devbyteviewer.database.getDatabase
+import com.example.android.devbyteviewer.domain.DevByteVideo
+import com.example.android.devbyteviewer.network.DevByteNetwork
+import com.example.android.devbyteviewer.network.asDomainModel
+import com.example.android.devbyteviewer.repository.VideosRepository
+import kotlinx.coroutines.*
+import java.io.IOException
+
+/**
+ * DevByteViewModel designed to store and manage UI-related data in a lifecycle conscious way. This
+ * allows data to survive configuration changes such as screen rotations. In addition, background
+ * work such as fetching network results can continue through configuration changes and deliver
+ * results after the new Fragment or Activity is available.
+ *
+ * @param application The application that this viewmodel is attached to, it's safe to hold a
+ * reference to applications across rotation since Application is never recreated during actiivty
+ * or fragment lifecycle events.
+ */
+class DevByteViewModel(application: Application) : AndroidViewModel(application) {
+
+
+ /**
+ * The data source this ViewModel will fetch results from.
+ */
+ private val videosRepository = VideosRepository(getDatabase(application))
+
+ /**
+ * A playlist of videos displayed on the screen.
+ */
+ val playlist = videosRepository.videos
+
+ /**
+ * This is the job for all coroutines started by this ViewModel.
+ *
+ * Cancelling this job will cancel all coroutines started by this ViewModel.
+ */
+ private val viewModelJob = SupervisorJob()
+
+ /**
+ * This is the main scope for all coroutines launched by MainViewModel.
+ *
+ * Since we pass viewModelJob, you can cancel all coroutines launched by uiScope by calling
+ * viewModelJob.cancel()
+ */
+ private val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Main)
+
+ /**
+ * Event triggered for network error. This is private to avoid exposing a
+ * way to set this value to observers.
+ */
+ private var _eventNetworkError = MutableLiveData(false)
+
+ /**
+ * Event triggered for network error. Views should use this to get access
+ * to the data.
+ */
+ val eventNetworkError: LiveData
+ get() = _eventNetworkError
+
+ /**
+ * Flag to display the error message. This is private to avoid exposing a
+ * way to set this value to observers.
+ */
+ private var _isNetworkErrorShown = MutableLiveData(false)
+
+ /**
+ * Flag to display the error message. Views should use this to get access
+ * to the data.
+ */
+ val isNetworkErrorShown: LiveData
+ get() = _isNetworkErrorShown
+
+ /**
+ * init{} is called immediately when this ViewModel is created.
+ */
+ init {
+ refreshDataFromRepository()
+ }
+
+ /**
+ * Refresh data from the repository. Use a coroutine launch to run in a
+ * background thread.
+ */
+ private fun refreshDataFromRepository() {
+ viewModelScope.launch {
+ try {
+ videosRepository.refreshVideos()
+ _eventNetworkError.value = false
+ _isNetworkErrorShown.value = false
+
+ } catch (networkError: IOException) {
+ // Show a Toast error message and hide the progress bar.
+ if(playlist.value!!.isEmpty())
+ _eventNetworkError.value = true
+ }
+ }
+ }
+
+
+ /**
+ * Resets the network error flag.
+ */
+ fun onNetworkErrorShown() {
+ _isNetworkErrorShown.value = true
+ }
+
+
+ /**
+ * Cancel all coroutines when the ViewModel is cleared
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+ /**
+ * Factory for constructing DevByteViewModel with parameter
+ */
+ class Factory(val app: Application) : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(DevByteViewModel::class.java)) {
+ @Suppress("UNCHECKED_CAST")
+ return DevByteViewModel(app) as T
+ }
+ throw IllegalArgumentException("Unable to construct viewmodel")
+ }
+ }
+}
diff --git a/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/work/RefreshDataWorker.kt b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/work/RefreshDataWorker.kt
new file mode 100644
index 0000000..14d5693
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/java/com/example/android/devbyteviewer/work/RefreshDataWorker.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.devbyteviewer.work
+
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+import com.example.android.devbyteviewer.database.getDatabase
+import com.example.android.devbyteviewer.repository.VideosRepository
+import retrofit2.HttpException
+import timber.log.Timber
+
+class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
+ CoroutineWorker(appContext, params) {
+
+ companion object {
+ const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
+ }
+ override suspend fun doWork(): Result {
+ val database = getDatabase(applicationContext)
+ val repository = VideosRepository(database)
+
+ try {
+ repository.refreshVideos( )
+ Timber.d("WorkManager: Work request for sync is run")
+ } catch (e: HttpException) {
+ return Result.retry()
+ }
+
+ return Result.success()
+ }
+}
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/DevBytesWorkManager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100755
index 0000000..56a2561
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DevBytesWorkManager/app/src/main/res/drawable/ic_launcher_background.xml b/DevBytesWorkManager/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100755
index 0000000..4f6c04c
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DevBytesWorkManager/app/src/main/res/drawable/ic_play_circle_outline_black_48dp.xml b/DevBytesWorkManager/app/src/main/res/drawable/ic_play_circle_outline_black_48dp.xml
new file mode 100755
index 0000000..0d2e285
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/drawable/ic_play_circle_outline_black_48dp.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/DevBytesWorkManager/app/src/main/res/layout/activity_dev_byte_viewer.xml b/DevBytesWorkManager/app/src/main/res/layout/activity_dev_byte_viewer.xml
new file mode 100755
index 0000000..547d362
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/layout/activity_dev_byte_viewer.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/res/layout/devbyte_item.xml b/DevBytesWorkManager/app/src/main/res/layout/devbyte_item.xml
new file mode 100755
index 0000000..926d2f4
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/layout/devbyte_item.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/res/layout/fragment_dev_byte.xml b/DevBytesWorkManager/app/src/main/res/layout/fragment_dev_byte.xml
new file mode 100755
index 0000000..2357206
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/layout/fragment_dev_byte.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/DevBytesWorkManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100755
index 0000000..9b4f225
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/DevBytesWorkManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100755
index 0000000..9b4f225
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-hdpi/ic_launcher.png b/DevBytesWorkManager/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..898f3ed
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/DevBytesWorkManager/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dffca36
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-mdpi/ic_launcher.png b/DevBytesWorkManager/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..64ba76f
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/DevBytesWorkManager/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dae5e08
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/DevBytesWorkManager/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..e5ed465
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/DevBytesWorkManager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..14ed0af
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/DevBytesWorkManager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..b0907ca
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/DevBytesWorkManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..d8ae031
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/DevBytesWorkManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..2c18de9
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/DevBytesWorkManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..beed3cd
Binary files /dev/null and b/DevBytesWorkManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/DevBytesWorkManager/app/src/main/res/navigation/nav_graph.xml b/DevBytesWorkManager/app/src/main/res/navigation/nav_graph.xml
new file mode 100755
index 0000000..76fa28b
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DevBytesWorkManager/app/src/main/res/values/colors.xml b/DevBytesWorkManager/app/src/main/res/values/colors.xml
new file mode 100755
index 0000000..2a770ef
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ #1E8E3E
+ #0D652D
+ #E37400
+
+ #ffDDDDDD
+ #ff666666
+ #ff222222
+
diff --git a/DevBytesWorkManager/app/src/main/res/values/strings.xml b/DevBytesWorkManager/app/src/main/res/values/strings.xml
new file mode 100755
index 0000000..91f4c4d
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/values/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ DevByte Viewer
+
diff --git a/DevBytesWorkManager/app/src/main/res/values/styles.xml b/DevBytesWorkManager/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..8fc17a5
--- /dev/null
+++ b/DevBytesWorkManager/app/src/main/res/values/styles.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/DevBytesWorkManager/build.gradle b/DevBytesWorkManager/build.gradle
new file mode 100755
index 0000000..678b933
--- /dev/null
+++ b/DevBytesWorkManager/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext.kotlin_version = '1.3.31'
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.4.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ maven { url "https://kotlin.bintray.com/kotlinx/" }
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/DevBytesWorkManager/gradle.properties b/DevBytesWorkManager/gradle.properties
new file mode 100755
index 0000000..92cca3f
--- /dev/null
+++ b/DevBytesWorkManager/gradle.properties
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2019 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/DevBytesWorkManager/gradle/wrapper/gradle-wrapper.jar b/DevBytesWorkManager/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..f6b961f
Binary files /dev/null and b/DevBytesWorkManager/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/DevBytesWorkManager/gradle/wrapper/gradle-wrapper.properties b/DevBytesWorkManager/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..f7d554f
--- /dev/null
+++ b/DevBytesWorkManager/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Apr 25 13:35:31 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/DevBytesWorkManager/gradlew b/DevBytesWorkManager/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/DevBytesWorkManager/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/DevBytesWorkManager/gradlew.bat b/DevBytesWorkManager/gradlew.bat
new file mode 100755
index 0000000..e95643d
--- /dev/null
+++ b/DevBytesWorkManager/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/DevBytesWorkManager/settings.gradle b/DevBytesWorkManager/settings.gradle
new file mode 100755
index 0000000..116b21c
--- /dev/null
+++ b/DevBytesWorkManager/settings.gradle
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+include ':app'
diff --git a/MarsRealEstateFinal/README.md b/MarsRealEstateFinal/README.md
new file mode 100644
index 0000000..59bcc00
--- /dev/null
+++ b/MarsRealEstateFinal/README.md
@@ -0,0 +1,56 @@
+MarsRealEstateFinal - Solution Code
+===================================
+
+Solution code for Android Kotlin Fundamentals Codelab 8.3 Filtering and detail views with
+internet data.
+
+Introduction
+------------
+
+MarsRealEstate is a demo app that shows available properties for sale and for rent on Mars.
+The property data is stored on a Web server as a REST web service. This app demonstrated
+the use of [Retrofit](https://square.github.io/retrofit/) to make REST requests to the
+web service, [Moshi](https://github.com/square/moshi) to handle the deserialization of the
+returned JSON to Kotlin data objects, and [Glide](https://bumptech.github.io/glide/) to load and
+cache images by URL.
+
+The app also leverages [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel),
+[LiveData](https://developer.android.com/topic/libraries/architecture/livedata),
+[Data Binding](https://developer.android.com/topic/libraries/data-binding/) with binding
+adapters, and [Navigation](https://developer.android.com/topic/libraries/architecture/navigation/)
+with the SafeArgs plugin for parameter passing between fragments.
+
+Pre-requisites
+--------------
+
+You need to know:
+- How to create and use fragments.
+- How to navigate between fragments, and use safeArgs to pass data between fragments.
+- How to use architecture components including ViewModel, ViewModelProvider.Factory, LiveData, and LiveData transformations.
+- How to use coroutines for long-running tasks.
+
+
+Getting Started
+---------------
+
+1. Download and run the app.
+
+License
+-------
+
+Copyright 2019 Google, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements. See the NOTICE file distributed with this work for
+additional information regarding copyright ownership. The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
\ No newline at end of file
diff --git a/MarsRealEstateFinal/app/.gitignore b/MarsRealEstateFinal/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/MarsRealEstateFinal/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/MarsRealEstateFinal/app/build.gradle b/MarsRealEstateFinal/app/build.gradle
new file mode 100644
index 0000000..c3b8b2b
--- /dev/null
+++ b/MarsRealEstateFinal/app/build.gradle
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+apply plugin: "androidx.navigation.safeargs"
+
+android {
+ compileSdkVersion 28
+ dataBinding {
+ enabled = true
+ }
+ defaultConfig {
+ applicationId "com.example.android.marsrealestate"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ androidExtensions {
+ experimental = true
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ // Kotlin
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$version_kotlin"
+
+ // Constraint Layout
+ implementation "androidx.constraintlayout:constraintlayout:$version_constraint_layout"
+
+ // ViewModel and LiveData
+ implementation "androidx.lifecycle:lifecycle-extensions:$version_lifecycle_extensions"
+
+ // Navigation
+ implementation "android.arch.navigation:navigation-fragment-ktx:$version_navigation"
+ implementation "android.arch.navigation:navigation-ui-ktx:$version_navigation"
+
+ // Core with Ktx
+ implementation "androidx.core:core-ktx:$version_core"
+
+ // Moshi
+ implementation "com.squareup.moshi:moshi:$version_moshi"
+ implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
+
+ // Retrofit with Moshi Converter
+ implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
+ implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
+
+ // Coroutines
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines"
+
+ // Retrofit Coroutines Support
+ implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"
+
+ // Glide
+ implementation "com.github.bumptech.glide:glide:$version_glide"
+
+ // RecyclerView
+ implementation "androidx.recyclerview:recyclerview:$version_recyclerview"
+}
diff --git a/MarsRealEstateFinal/app/proguard-rules.pro b/MarsRealEstateFinal/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/MarsRealEstateFinal/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/MarsRealEstateFinal/app/src/main/AndroidManifest.xml b/MarsRealEstateFinal/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0de9e05
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/AndroidManifest.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateFinal/app/src/main/ic_launcher-web.png b/MarsRealEstateFinal/app/src/main/ic_launcher-web.png
new file mode 100644
index 0000000..87e04ff
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/ic_launcher-web.png differ
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/BindingAdapters.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/BindingAdapters.kt
new file mode 100644
index 0000000..5a52f09
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/BindingAdapters.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate
+
+import android.view.View
+import android.widget.ImageView
+import androidx.core.net.toUri
+import androidx.databinding.BindingAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.RequestOptions
+import com.example.android.marsrealestate.network.MarsProperty
+import com.example.android.marsrealestate.overview.MarsApiStatus
+import com.example.android.marsrealestate.overview.PhotoGridAdapter
+
+/**
+ * When there is no Mars property data (data is null), hide the [RecyclerView], otherwise show it.
+ */
+@BindingAdapter("listData")
+fun bindRecyclerView(recyclerView: RecyclerView, data: List?) {
+ val adapter = recyclerView.adapter as PhotoGridAdapter
+ adapter.submitList(data)
+}
+
+/**
+ * Uses the Glide library to load an image by URL into an [ImageView]
+ */
+@BindingAdapter("imageUrl")
+fun bindImage(imgView: ImageView, imgUrl: String?) {
+ imgUrl?.let {
+ val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
+ Glide.with(imgView.context)
+ .load(imgUri)
+ .apply(RequestOptions()
+ .placeholder(R.drawable.loading_animation)
+ .error(R.drawable.ic_broken_image))
+ .into(imgView)
+ }
+}
+
+/**
+ * This binding adapter displays the [MarsApiStatus] of the network request in an image view. When
+ * the request is loading, it displays a loading_animation. If the request has an error, it
+ * displays a broken image to reflect the connection error. When the request is finished, it
+ * hides the image view.
+ */
+@BindingAdapter("marsApiStatus")
+fun bindStatus(statusImageView: ImageView, status: MarsApiStatus?) {
+ when (status) {
+ MarsApiStatus.LOADING -> {
+ statusImageView.visibility = View.VISIBLE
+ statusImageView.setImageResource(R.drawable.loading_animation)
+ }
+ MarsApiStatus.ERROR -> {
+ statusImageView.visibility = View.VISIBLE
+ statusImageView.setImageResource(R.drawable.ic_connection_error)
+ }
+ MarsApiStatus.DONE -> {
+ statusImageView.visibility = View.GONE
+ }
+ }
+}
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/MainActivity.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/MainActivity.kt
new file mode 100644
index 0000000..e1eb3ff
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/MainActivity.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+class MainActivity : AppCompatActivity() {
+
+ /**
+ * Our MainActivity is only responsible for setting the content view that contains the
+ * Navigation Host.
+ */
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ }
+}
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/detail/DetailFragment.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/detail/DetailFragment.kt
new file mode 100644
index 0000000..b3604c0
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/detail/DetailFragment.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.marsrealestate.detail
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProviders
+import com.example.android.marsrealestate.databinding.FragmentDetailBinding
+
+/**
+ * This [Fragment] shows the detailed information about a selected piece of Mars real estate.
+ * It sets this information in the [DetailViewModel], which it gets as a Parcelable property
+ * through Jetpack Navigation's SafeArgs.
+ */
+class DetailFragment : Fragment() {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ val application = requireNotNull(activity).application
+ val binding = FragmentDetailBinding.inflate(inflater)
+ binding.setLifecycleOwner(this)
+
+ val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty
+ val viewModelFactory = DetailViewModelFactory(marsProperty, application)
+ binding.viewModel = ViewModelProviders.of(
+ this, viewModelFactory).get(DetailViewModel::class.java)
+
+ return binding.root
+ }
+}
\ No newline at end of file
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModel.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModel.kt
new file mode 100644
index 0000000..8e0bd68
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModel.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.marsrealestate.detail
+
+import android.app.Application
+import androidx.lifecycle.*
+import com.example.android.marsrealestate.network.MarsProperty
+import com.example.android.marsrealestate.R
+
+/**
+ * The [ViewModel] associated with the [DetailFragment], containing information about the selected
+ * [MarsProperty].
+ */
+class DetailViewModel( marsProperty: MarsProperty,
+ app: Application) : AndroidViewModel(app) {
+
+ // The internal MutableLiveData for the selected property
+ private val _selectedProperty = MutableLiveData()
+
+ // The external LiveData for the SelectedProperty
+ val selectedProperty: LiveData
+ get() = _selectedProperty
+
+ // Initialize the _selectedProperty MutableLiveData
+ init {
+ _selectedProperty.value = marsProperty
+ }
+
+ // The displayPropertyPrice formatted Transformation Map LiveData, which displays the sale
+ // or rental price.
+ val displayPropertyPrice = Transformations.map(selectedProperty) {
+ app.applicationContext.getString(
+ when (it.isRental) {
+ true -> R.string.display_price_monthly_rental
+ false -> R.string.display_price
+ }, it.price)
+ }
+
+ // The displayPropertyType formatted Transformation Map LiveData, which displays the
+ // "For Rent/Sale" String
+ val displayPropertyType = Transformations.map(selectedProperty) {
+ app.applicationContext.getString(R.string.display_type,
+ app.applicationContext.getString(
+ when(it.isRental) {
+ true -> R.string.type_rent
+ false -> R.string.type_sale
+ }))
+ }
+}
+
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModelFactory.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModelFactory.kt
new file mode 100644
index 0000000..2a74937
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModelFactory.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.marsrealestate.detail
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.marsrealestate.network.MarsProperty
+
+/**
+ * Simple ViewModel factory that provides the MarsProperty and context to the ViewModel.
+ */
+class DetailViewModelFactory(
+ private val marsProperty: MarsProperty,
+ private val application: Application) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(DetailViewModel::class.java)) {
+ return DetailViewModel(marsProperty, application) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/network/MarsApiService.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/network/MarsApiService.kt
new file mode 100644
index 0000000..000208c
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/network/MarsApiService.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.network
+
+import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+import retrofit2.http.GET
+import kotlinx.coroutines.Deferred
+import retrofit2.http.Query
+
+enum class MarsApiFilter(val value: String) {
+ SHOW_RENT("rent"),
+ SHOW_BUY("buy"),
+ SHOW_ALL("all") }
+
+private const val BASE_URL = " https://android-kotlin-fun-mars-server.appspot.com/"
+
+/**
+ * Build the Moshi object that Retrofit will be using, making sure to add the Kotlin adapter for
+ * full Kotlin compatibility.
+ */
+private val moshi = Moshi.Builder()
+ .add(KotlinJsonAdapterFactory())
+ .build()
+
+/**
+ * Use the Retrofit builder to build a retrofit object using a Moshi converter with our Moshi
+ * object.
+ */
+private val retrofit = Retrofit.Builder()
+ .addConverterFactory(MoshiConverterFactory.create(moshi))
+ .addCallAdapterFactory(CoroutineCallAdapterFactory())
+ .baseUrl(BASE_URL)
+ .build()
+
+/**
+ * A public interface that exposes the [getProperties] method
+ */
+interface MarsApiService {
+ /**
+ * Returns a Coroutine [Deferred] [List] of [MarsProperty] which can be fetched with await() if
+ * in a Coroutine scope.
+ * The @GET annotation indicates that the "realestate" endpoint will be requested with the GET
+ * HTTP method
+ */
+ @GET("realestate")
+ fun getProperties(@Query("filter") type: String):
+ // The Coroutine Call Adapter allows us to return a Deferred, a Job with a result
+ Deferred>
+}
+
+/**
+ * A public Api object that exposes the lazy-initialized Retrofit service
+ */
+object MarsApi {
+ val retrofitService : MarsApiService by lazy { retrofit.create(MarsApiService::class.java) }
+}
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/network/MarsProperty.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/network/MarsProperty.kt
new file mode 100644
index 0000000..7b2bcfa
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/network/MarsProperty.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.network
+
+import android.os.Parcelable
+import com.squareup.moshi.Json
+import kotlinx.android.parcel.Parcelize
+
+/**
+ * This data class defines a Mars property which includes an ID, the image URL, the type (sale
+ * or rental) and the price (monthly if it's a rental).
+ * The property names of this data class are used by Moshi to match the names of values in JSON.
+ */
+@Parcelize
+data class MarsProperty (
+ val id: String,
+ // used to map img_src from the JSON to imgSrcUrl in our class
+ @Json(name = "img_src") val imgSrcUrl: String,
+ val type: String,
+ val price: Double) : Parcelable {
+ val isRental
+ get() = type == "rent"
+}
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/overview/OverviewFragment.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/overview/OverviewFragment.kt
new file mode 100644
index 0000000..0e7d99e
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/overview/OverviewFragment.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.overview
+
+import android.os.Bundle
+import android.view.*
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.marsrealestate.R
+import com.example.android.marsrealestate.databinding.FragmentOverviewBinding
+import com.example.android.marsrealestate.network.MarsApiFilter
+
+/**
+ * This fragment shows the the status of the Mars real-estate web services transaction.
+ */
+class OverviewFragment : Fragment() {
+
+ /**
+ * Lazily initialize our [OverviewViewModel].
+ */
+ private val viewModel: OverviewViewModel by lazy {
+ ViewModelProviders.of(this).get(OverviewViewModel::class.java)
+ }
+
+ /**
+ * Inflates the layout with Data Binding, sets its lifecycle owner to the OverviewFragment
+ * to enable Data Binding to observe LiveData, and sets up the RecyclerView with an adapter.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val binding = FragmentOverviewBinding.inflate(inflater)
+ //val binding = GridViewItemBinding.inflate(inflater)
+
+ // Allows Data Binding to Observe LiveData with the lifecycle of this Fragment
+ binding.setLifecycleOwner(this)
+
+ // Giving the binding access to the OverviewViewModel
+ binding.viewModel = viewModel
+
+ // Sets the adapter of the photosGrid RecyclerView with clickHandler lambda that
+ // tells the viewModel when our property is clicked
+ binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
+ viewModel.displayPropertyDetails(it)
+ })
+
+ // Observe the navigateToSelectedProperty LiveData and Navigate when it isn't null
+ // After navigating, call displayPropertyDetailsComplete() so that the ViewModel is ready
+ // for another navigation event.
+ viewModel.navigateToSelectedProperty.observe(this, Observer {
+ if ( null != it ) {
+ // Must find the NavController from the Fragment
+ this.findNavController().navigate(OverviewFragmentDirections.actionShowDetail(it))
+ // Tell the ViewModel we've made the navigate call to prevent multiple navigation
+ viewModel.displayPropertyDetailsComplete()
+ }
+ })
+
+ setHasOptionsMenu(true)
+ return binding.root
+ }
+
+ /**
+ * Inflates the overflow menu that contains filtering options.
+ */
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ inflater.inflate(R.menu.overflow_menu, menu)
+ super.onCreateOptionsMenu(menu, inflater)
+ }
+
+ /**
+ * Updates the filter in the [OverviewViewModel] when the menu items are selected from the
+ * overflow menu.
+ */
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ viewModel.updateFilter(
+ when (item.itemId) {
+ R.id.show_rent_menu -> MarsApiFilter.SHOW_RENT
+ R.id.show_buy_menu -> MarsApiFilter.SHOW_BUY
+ else -> MarsApiFilter.SHOW_ALL
+ }
+ )
+ return true
+ }
+}
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/overview/OverviewViewModel.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/overview/OverviewViewModel.kt
new file mode 100644
index 0000000..a38014e
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/overview/OverviewViewModel.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.overview
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.marsrealestate.network.MarsApi
+import com.example.android.marsrealestate.network.MarsApiFilter
+import com.example.android.marsrealestate.network.MarsProperty
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+enum class MarsApiStatus { LOADING, ERROR, DONE }
+
+/**
+ * The [ViewModel] that is attached to the [OverviewFragment].
+ */
+class OverviewViewModel : ViewModel() {
+
+ // The internal MutableLiveData that stores the status of the most recent request
+ private val _status = MutableLiveData()
+
+ // The external immutable LiveData for the request status
+ val status: LiveData
+ get() = _status
+
+ // Internally, we use a MutableLiveData, because we will be updating the List of MarsProperty
+ // with new values
+ private val _properties = MutableLiveData>()
+
+ // The external LiveData interface to the property is immutable, so only this class can modify
+ val properties: LiveData>
+ get() = _properties
+
+ // LiveData to handle navigation to the selected property
+ private val _navigateToSelectedProperty = MutableLiveData()
+ val navigateToSelectedProperty: LiveData
+ get() = _navigateToSelectedProperty
+
+ // Create a Coroutine scope using a job to be able to cancel when needed
+ private var viewModelJob = Job()
+
+ // the Coroutine runs using the Main (UI) dispatcher
+ private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main)
+
+ /**
+ * Call getMarsRealEstateProperties() on init so we can display status immediately.
+ */
+ init {
+ getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
+ }
+
+ /**
+ * Gets filtered Mars real estate property information from the Mars API Retrofit service and
+ * updates the [MarsProperty] [List] and [MarsApiStatus] [LiveData]. The Retrofit service
+ * returns a coroutine Deferred, which we await to get the result of the transaction.
+ * @param filter the [MarsApiFilter] that is sent as part of the web server request
+ */
+ private fun getMarsRealEstateProperties(filter: MarsApiFilter) {
+ coroutineScope.launch {
+ // Get the Deferred object for our Retrofit request
+ var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)
+ try {
+ _status.value = MarsApiStatus.LOADING
+ // this will run on a thread managed by Retrofit
+ val listResult = getPropertiesDeferred.await()
+ _status.value = MarsApiStatus.DONE
+ _properties.value = listResult
+ } catch (e: Exception) {
+ _status.value = MarsApiStatus.ERROR
+ _properties.value = ArrayList()
+ }
+ }
+ }
+
+ /**
+ * When the [ViewModel] is finished, we cancel our coroutine [viewModelJob], which tells the
+ * Retrofit service to stop.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+ /**
+ * Updates the data set filter for the web services by querying the data with the new filter
+ * by calling [getMarsRealEstateProperties]
+ * @param filter the [MarsApiFilter] that is sent as part of the web server request
+ */
+ fun updateFilter(filter: MarsApiFilter) {
+ getMarsRealEstateProperties(filter)
+ }
+
+ /**
+ * When the property is clicked, set the [_navigateToSelectedProperty] [MutableLiveData]
+ * @param marsProperty The [MarsProperty] that was clicked on.
+ */
+ fun displayPropertyDetails(marsProperty: MarsProperty) {
+ _navigateToSelectedProperty.value = marsProperty
+ }
+
+ /**
+ * After the navigation has taken place, make sure navigateToSelectedProperty is set to null
+ */
+ fun displayPropertyDetailsComplete() {
+ _navigateToSelectedProperty.value = null
+ }
+}
diff --git a/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/overview/PhotoGridAdapter.kt b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/overview/PhotoGridAdapter.kt
new file mode 100644
index 0000000..470250c
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/java/com/example/android/marsrealestate/overview/PhotoGridAdapter.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.overview
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.marsrealestate.databinding.GridViewItemBinding
+import com.example.android.marsrealestate.network.MarsProperty
+
+/**
+ * This class implements a [RecyclerView] [ListAdapter] which uses Data Binding to present [List]
+ * data, including computing diffs between lists.
+ */
+class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
+ ListAdapter(DiffCallback) {
+
+ /**
+ * The MarsPropertyViewHolder constructor takes the binding variable from the associated
+ * GridViewItem, which nicely gives it access to the full [MarsProperty] information.
+ */
+ class MarsPropertyViewHolder(private var binding: GridViewItemBinding):
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(marsProperty: MarsProperty) {
+ binding.property = marsProperty
+ // This is important, because it forces the data binding to execute immediately,
+ // which allows the RecyclerView to make the correct view size measurements
+ binding.executePendingBindings()
+ }
+ }
+
+ /**
+ * Allows the RecyclerView to determine which items have changed when the [List] of [MarsProperty]
+ * has been updated.
+ */
+ companion object DiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
+ return oldItem === newItem
+ }
+
+ override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
+ return oldItem.id == newItem.id
+ }
+ }
+
+ /**
+ * Create new [RecyclerView] item views (invoked by the layout manager)
+ */
+ override fun onCreateViewHolder(parent: ViewGroup,
+ viewType: Int): MarsPropertyViewHolder {
+ return MarsPropertyViewHolder(GridViewItemBinding.inflate(LayoutInflater.from(parent.context)))
+ }
+
+ /**
+ * Replaces the contents of a view (invoked by the layout manager)
+ */
+ override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
+ val marsProperty = getItem(position)
+ holder.itemView.setOnClickListener {
+ onClickListener.onClick(marsProperty)
+ }
+ holder.bind(marsProperty)
+ }
+
+ /**
+ * Custom listener that handles clicks on [RecyclerView] items. Passes the [MarsProperty]
+ * associated with the current item to the [onClick] function.
+ * @param clickListener lambda that will be called with the current [MarsProperty]
+ */
+ class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
+ fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
+ }
+}
+
diff --git a/MarsRealEstateFinal/app/src/main/res/drawable/ic_broken_image.xml b/MarsRealEstateFinal/app/src/main/res/drawable/ic_broken_image.xml
new file mode 100644
index 0000000..d7284d3
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/drawable/ic_broken_image.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/drawable/ic_connection_error.xml b/MarsRealEstateFinal/app/src/main/res/drawable/ic_connection_error.xml
new file mode 100644
index 0000000..70a71a6
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/drawable/ic_connection_error.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/drawable/ic_for_sale_outline.xml b/MarsRealEstateFinal/app/src/main/res/drawable/ic_for_sale_outline.xml
new file mode 100644
index 0000000..dc2a807
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/drawable/ic_for_sale_outline.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/drawable/ic_launcher_background.xml b/MarsRealEstateFinal/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..ed74491
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/drawable/ic_launcher_foreground.xml b/MarsRealEstateFinal/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..d91e222
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/drawable/loading_animation.xml b/MarsRealEstateFinal/app/src/main/res/drawable/loading_animation.xml
new file mode 100644
index 0000000..4b4721c
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/drawable/loading_animation.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/drawable/loading_img.xml b/MarsRealEstateFinal/app/src/main/res/drawable/loading_img.xml
new file mode 100644
index 0000000..c656871
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/drawable/loading_img.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/layout/activity_main.xml b/MarsRealEstateFinal/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..0435a9b
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/layout/fragment_detail.xml b/MarsRealEstateFinal/app/src/main/res/layout/fragment_detail.xml
new file mode 100644
index 0000000..7af084c
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/layout/fragment_detail.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateFinal/app/src/main/res/layout/fragment_overview.xml b/MarsRealEstateFinal/app/src/main/res/layout/fragment_overview.xml
new file mode 100644
index 0000000..6902172
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/layout/fragment_overview.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateFinal/app/src/main/res/layout/grid_view_item.xml b/MarsRealEstateFinal/app/src/main/res/layout/grid_view_item.xml
new file mode 100644
index 0000000..c6d3a9a
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/layout/grid_view_item.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/menu/overflow_menu.xml b/MarsRealEstateFinal/app/src/main/res/menu/overflow_menu.xml
new file mode 100755
index 0000000..90714fd
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/menu/overflow_menu.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/MarsRealEstateFinal/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/MarsRealEstateFinal/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-hdpi/ic_launcher.png b/MarsRealEstateFinal/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..0b24188
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/MarsRealEstateFinal/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4a8354b
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-mdpi/ic_launcher.png b/MarsRealEstateFinal/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..2f8a7d3
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/MarsRealEstateFinal/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..f39fc86
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/MarsRealEstateFinal/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..9a4a0c4
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/MarsRealEstateFinal/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..ea902dd
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/MarsRealEstateFinal/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..a17e8cf
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/MarsRealEstateFinal/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..815c41b
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/MarsRealEstateFinal/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..30fd705
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/MarsRealEstateFinal/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4b947a8
Binary files /dev/null and b/MarsRealEstateFinal/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateFinal/app/src/main/res/navigation/nav_graph.xml b/MarsRealEstateFinal/app/src/main/res/navigation/nav_graph.xml
new file mode 100644
index 0000000..8fce019
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateFinal/app/src/main/res/values/colors.xml b/MarsRealEstateFinal/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..871190d
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/values/colors.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/MarsRealEstateFinal/app/src/main/res/values/strings.xml b/MarsRealEstateFinal/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a46a3d6
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/values/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+ Real Estate on Mars
+ Show all
+ Rent
+ Buy
+ Rent
+ Sale
+ For %s
+ $%,.0f/month
+ $%,.0f
+
diff --git a/MarsRealEstateFinal/app/src/main/res/values/styles.xml b/MarsRealEstateFinal/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..57b1542
--- /dev/null
+++ b/MarsRealEstateFinal/app/src/main/res/values/styles.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateFinal/build.gradle b/MarsRealEstateFinal/build.gradle
new file mode 100644
index 0000000..a5f6ed2
--- /dev/null
+++ b/MarsRealEstateFinal/build.gradle
@@ -0,0 +1,42 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext {
+ // Versions for all the dependencies we plan to use. It's particularly useful for kotlin and
+ // navigation where the versions of the plugin needs to be the same as the version of the
+ // library defined in the app Gradle file
+ version_android_gradle_plugin = "3.3.2"
+ version_core = "1.0.1"
+ version_constraint_layout = "1.1.3"
+ version_glide = "4.8.0"
+ version_kotlin = "1.3.21"
+ version_kotlin_coroutines = "1.1.0"
+ version_lifecycle_extensions = "2.0.0"
+ version_moshi = "1.8.0"
+ version_navigation = "1.0.0"
+ version_retrofit = "2.5.0"
+ version_retrofit_coroutines_adapter = "0.9.2"
+ version_recyclerview = "1.0.0"
+ }
+
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$version_android_gradle_plugin"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$version_kotlin"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$version_navigation"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/MarsRealEstateFinal/gradle.properties b/MarsRealEstateFinal/gradle.properties
new file mode 100644
index 0000000..51dc505
--- /dev/null
+++ b/MarsRealEstateFinal/gradle.properties
@@ -0,0 +1,36 @@
+#
+# Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/MarsRealEstateFinal/gradle/wrapper/gradle-wrapper.jar b/MarsRealEstateFinal/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/MarsRealEstateFinal/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/MarsRealEstateFinal/gradle/wrapper/gradle-wrapper.properties b/MarsRealEstateFinal/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4278fe2
--- /dev/null
+++ b/MarsRealEstateFinal/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,23 @@
+#
+# Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#Tue Dec 18 16:00:56 PST 2018
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+android.databinding.enableV2=true
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
diff --git a/MarsRealEstateFinal/gradlew b/MarsRealEstateFinal/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/MarsRealEstateFinal/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/MarsRealEstateFinal/gradlew.bat b/MarsRealEstateFinal/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/MarsRealEstateFinal/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/MarsRealEstateFinal/settings.gradle b/MarsRealEstateFinal/settings.gradle
new file mode 100644
index 0000000..245db96
--- /dev/null
+++ b/MarsRealEstateFinal/settings.gradle
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+include ':app'
diff --git a/MarsRealEstateGrid/README.md b/MarsRealEstateGrid/README.md
new file mode 100644
index 0000000..007b182
--- /dev/null
+++ b/MarsRealEstateGrid/README.md
@@ -0,0 +1,56 @@
+MarsRealEstateGrid - Solution Code
+==================================
+
+Solution code for Android Kotlin Fundamentals Codelab 8.2 Loading and displaying
+images from the internet.
+
+Introduction
+------------
+
+MarsRealEstate is a demo app that shows available properties for sale and for rent on Mars.
+The property data is stored on a Web server as a REST web service. This app demonstrated
+the use of [Retrofit](https://square.github.io/retrofit/) to make REST requests to the
+web service, [Moshi](https://github.com/square/moshi) to handle the deserialization of the
+returned JSON to Kotlin data objects, and [Glide](https://bumptech.github.io/glide/) to load and
+cache images by URL.
+
+The app also leverages [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel),
+[LiveData](https://developer.android.com/topic/libraries/architecture/livedata),
+[Data Binding](https://developer.android.com/topic/libraries/data-binding/) with binding
+adapters, and [Navigation](https://developer.android.com/topic/libraries/architecture/navigation/)
+with the SafeArgs plugin for parameter passing between fragments.
+
+Pre-requisites
+--------------
+
+You need to know:
+- How to create and use fragments.
+- How to navigate between fragments, and use safeArgs to pass data between fragments.
+- How to use architecture components including ViewModel, ViewModelProvider.Factory, LiveData, and LiveData transformations.
+- How to use coroutines for long-running tasks.
+
+
+Getting Started
+---------------
+
+1. Download and run the app.
+
+License
+-------
+
+Copyright 2019 Google, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements. See the NOTICE file distributed with this work for
+additional information regarding copyright ownership. The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
\ No newline at end of file
diff --git a/MarsRealEstateGrid/app/.gitignore b/MarsRealEstateGrid/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/MarsRealEstateGrid/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/MarsRealEstateGrid/app/build.gradle b/MarsRealEstateGrid/app/build.gradle
new file mode 100644
index 0000000..c3b8b2b
--- /dev/null
+++ b/MarsRealEstateGrid/app/build.gradle
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+apply plugin: "androidx.navigation.safeargs"
+
+android {
+ compileSdkVersion 28
+ dataBinding {
+ enabled = true
+ }
+ defaultConfig {
+ applicationId "com.example.android.marsrealestate"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ androidExtensions {
+ experimental = true
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ // Kotlin
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$version_kotlin"
+
+ // Constraint Layout
+ implementation "androidx.constraintlayout:constraintlayout:$version_constraint_layout"
+
+ // ViewModel and LiveData
+ implementation "androidx.lifecycle:lifecycle-extensions:$version_lifecycle_extensions"
+
+ // Navigation
+ implementation "android.arch.navigation:navigation-fragment-ktx:$version_navigation"
+ implementation "android.arch.navigation:navigation-ui-ktx:$version_navigation"
+
+ // Core with Ktx
+ implementation "androidx.core:core-ktx:$version_core"
+
+ // Moshi
+ implementation "com.squareup.moshi:moshi:$version_moshi"
+ implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
+
+ // Retrofit with Moshi Converter
+ implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
+ implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
+
+ // Coroutines
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines"
+
+ // Retrofit Coroutines Support
+ implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"
+
+ // Glide
+ implementation "com.github.bumptech.glide:glide:$version_glide"
+
+ // RecyclerView
+ implementation "androidx.recyclerview:recyclerview:$version_recyclerview"
+}
diff --git a/MarsRealEstateGrid/app/proguard-rules.pro b/MarsRealEstateGrid/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/MarsRealEstateGrid/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/MarsRealEstateGrid/app/src/main/AndroidManifest.xml b/MarsRealEstateGrid/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0de9e05
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/AndroidManifest.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateGrid/app/src/main/ic_launcher-web.png b/MarsRealEstateGrid/app/src/main/ic_launcher-web.png
new file mode 100644
index 0000000..87e04ff
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/ic_launcher-web.png differ
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/BindingAdapters.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/BindingAdapters.kt
new file mode 100644
index 0000000..5a52f09
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/BindingAdapters.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate
+
+import android.view.View
+import android.widget.ImageView
+import androidx.core.net.toUri
+import androidx.databinding.BindingAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.RequestOptions
+import com.example.android.marsrealestate.network.MarsProperty
+import com.example.android.marsrealestate.overview.MarsApiStatus
+import com.example.android.marsrealestate.overview.PhotoGridAdapter
+
+/**
+ * When there is no Mars property data (data is null), hide the [RecyclerView], otherwise show it.
+ */
+@BindingAdapter("listData")
+fun bindRecyclerView(recyclerView: RecyclerView, data: List?) {
+ val adapter = recyclerView.adapter as PhotoGridAdapter
+ adapter.submitList(data)
+}
+
+/**
+ * Uses the Glide library to load an image by URL into an [ImageView]
+ */
+@BindingAdapter("imageUrl")
+fun bindImage(imgView: ImageView, imgUrl: String?) {
+ imgUrl?.let {
+ val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
+ Glide.with(imgView.context)
+ .load(imgUri)
+ .apply(RequestOptions()
+ .placeholder(R.drawable.loading_animation)
+ .error(R.drawable.ic_broken_image))
+ .into(imgView)
+ }
+}
+
+/**
+ * This binding adapter displays the [MarsApiStatus] of the network request in an image view. When
+ * the request is loading, it displays a loading_animation. If the request has an error, it
+ * displays a broken image to reflect the connection error. When the request is finished, it
+ * hides the image view.
+ */
+@BindingAdapter("marsApiStatus")
+fun bindStatus(statusImageView: ImageView, status: MarsApiStatus?) {
+ when (status) {
+ MarsApiStatus.LOADING -> {
+ statusImageView.visibility = View.VISIBLE
+ statusImageView.setImageResource(R.drawable.loading_animation)
+ }
+ MarsApiStatus.ERROR -> {
+ statusImageView.visibility = View.VISIBLE
+ statusImageView.setImageResource(R.drawable.ic_connection_error)
+ }
+ MarsApiStatus.DONE -> {
+ statusImageView.visibility = View.GONE
+ }
+ }
+}
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/MainActivity.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/MainActivity.kt
new file mode 100644
index 0000000..e1eb3ff
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/MainActivity.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+class MainActivity : AppCompatActivity() {
+
+ /**
+ * Our MainActivity is only responsible for setting the content view that contains the
+ * Navigation Host.
+ */
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ }
+}
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/detail/DetailFragment.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/detail/DetailFragment.kt
new file mode 100644
index 0000000..f4a02c9
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/detail/DetailFragment.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.marsrealestate.detail
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProviders
+import com.example.android.marsrealestate.databinding.FragmentDetailBinding
+
+/**
+ * This [Fragment] will show the detailed information about a selected piece of Mars real estate.
+ */
+class DetailFragment : Fragment() {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ @Suppress("UNUSED_VARIABLE")
+ val application = requireNotNull(activity).application
+ val binding = FragmentDetailBinding.inflate(inflater)
+ binding.setLifecycleOwner(this)
+ return binding.root
+ }
+}
\ No newline at end of file
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModel.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModel.kt
new file mode 100644
index 0000000..915d91e
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModel.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.marsrealestate.detail
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.ViewModel
+import com.example.android.marsrealestate.detail.DetailFragment
+import com.example.android.marsrealestate.network.MarsProperty
+
+/**
+ * The [ViewModel] that is associated with the [DetailFragment].
+ */
+class DetailViewModel(@Suppress("UNUSED_PARAMETER")marsProperty: MarsProperty, app: Application) : AndroidViewModel(app) {
+}
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModelFactory.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModelFactory.kt
new file mode 100644
index 0000000..2a74937
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModelFactory.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.marsrealestate.detail
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.marsrealestate.network.MarsProperty
+
+/**
+ * Simple ViewModel factory that provides the MarsProperty and context to the ViewModel.
+ */
+class DetailViewModelFactory(
+ private val marsProperty: MarsProperty,
+ private val application: Application) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(DetailViewModel::class.java)) {
+ return DetailViewModel(marsProperty, application) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/network/MarsApiService.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/network/MarsApiService.kt
new file mode 100644
index 0000000..f010ccf
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/network/MarsApiService.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.network
+
+import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+import retrofit2.http.GET
+import kotlinx.coroutines.Deferred
+
+private const val BASE_URL = " https://android-kotlin-fun-mars-server.appspot.com/"
+
+/**
+ * Build the Moshi object that Retrofit will be using, making sure to add the Kotlin adapter for
+ * full Kotlin compatibility.
+ */
+private val moshi = Moshi.Builder()
+ .add(KotlinJsonAdapterFactory())
+ .build()
+
+/**
+ * Use the Retrofit builder to build a retrofit object using a Moshi converter with our Moshi
+ * object.
+ */
+private val retrofit = Retrofit.Builder()
+ .addConverterFactory(MoshiConverterFactory.create(moshi))
+ .addCallAdapterFactory(CoroutineCallAdapterFactory())
+ .baseUrl(BASE_URL)
+ .build()
+
+/**
+ * A public interface that exposes the [getProperties] method
+ */
+interface MarsApiService {
+ /**
+ * Returns a Coroutine [Deferred] [List] of [MarsProperty] which can be fetched with await() if
+ * in a Coroutine scope.
+ * The @GET annotation indicates that the "realestate" endpoint will be requested with the GET
+ * HTTP method
+ */
+ @GET("realestate")
+ fun getProperties():
+ // The Coroutine Call Adapter allows us to return a Deferred, a Job with a result
+ Deferred>
+}
+
+/**
+ * A public Api object that exposes the lazy-initialized Retrofit service
+ */
+object MarsApi {
+ val retrofitService : MarsApiService by lazy { retrofit.create(MarsApiService::class.java) }
+}
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/network/MarsProperty.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/network/MarsProperty.kt
new file mode 100644
index 0000000..b686056
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/network/MarsProperty.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.network
+
+import com.squareup.moshi.Json
+
+/**
+ * This data class defines a Mars property which includes an ID, the image URL, the type (sale
+ * or rental) and the price (monthly if it's a rental).
+ * The property names of this data class are used by Moshi to match the names of values in JSON.
+ */
+data class MarsProperty(
+ val id: String,
+ // used to map img_src from the JSON to imgSrcUrl in our class
+ @Json(name = "img_src") val imgSrcUrl: String,
+ val type: String,
+ val price: Double)
\ No newline at end of file
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/overview/OverviewFragment.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/overview/OverviewFragment.kt
new file mode 100644
index 0000000..7065ad2
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/overview/OverviewFragment.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.overview
+
+import android.os.Bundle
+import android.view.*
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProviders
+import com.example.android.marsrealestate.R
+import com.example.android.marsrealestate.databinding.FragmentOverviewBinding
+
+/**
+ * This fragment shows the the status of the Mars real-estate web services transaction.
+ */
+class OverviewFragment : Fragment() {
+
+ /**
+ * Lazily initialize our [OverviewViewModel].
+ */
+ private val viewModel: OverviewViewModel by lazy {
+ ViewModelProviders.of(this).get(OverviewViewModel::class.java)
+ }
+
+ /**
+ * Inflates the layout with Data Binding, sets its lifecycle owner to the OverviewFragment
+ * to enable Data Binding to observe LiveData, and sets up the RecyclerView with an adapter.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val binding = FragmentOverviewBinding.inflate(inflater)
+ //val binding = GridViewItemBinding.inflate(inflater)
+
+ // Allows Data Binding to Observe LiveData with the lifecycle of this Fragment
+ binding.setLifecycleOwner(this)
+
+ // Giving the binding access to the OverviewViewModel
+ binding.viewModel = viewModel
+
+ // Sets the adapter of the photosGrid RecyclerView
+ binding.photosGrid.adapter = PhotoGridAdapter()
+
+ setHasOptionsMenu(true)
+ return binding.root
+ }
+
+ /**
+ * Inflates the overflow menu that contains filtering options.
+ */
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ inflater.inflate(R.menu.overflow_menu, menu)
+ super.onCreateOptionsMenu(menu, inflater)
+ }
+}
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/overview/OverviewViewModel.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/overview/OverviewViewModel.kt
new file mode 100644
index 0000000..0aa2d85
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/overview/OverviewViewModel.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.overview
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.marsrealestate.network.MarsApi
+import com.example.android.marsrealestate.network.MarsProperty
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+enum class MarsApiStatus { LOADING, ERROR, DONE }
+
+/**
+ * The [ViewModel] that is attached to the [OverviewFragment].
+ */
+class OverviewViewModel : ViewModel() {
+
+ // The internal MutableLiveData that stores the status of the most recent request
+ private val _status = MutableLiveData()
+
+ // The external immutable LiveData for the request status
+ val status: LiveData
+ get() = _status
+
+ // Internally, we use a MutableLiveData, because we will be updating the List of MarsProperty
+ // with new values
+ private val _properties = MutableLiveData>()
+
+ // The external LiveData interface to the property is immutable, so only this class can modify
+ val properties: LiveData>
+ get() = _properties
+
+ // Create a Coroutine scope using a job to be able to cancel when needed
+ private var viewModelJob = Job()
+
+ // the Coroutine runs using the Main (UI) dispatcher
+ private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main)
+
+ /**
+ * Call getMarsRealEstateProperties() on init so we can display status immediately.
+ */
+ init {
+ getMarsRealEstateProperties()
+ }
+
+ /**
+ * Gets Mars real estate property information from the Mars API Retrofit service and updates the
+ * [MarsProperty] [List] [LiveData]. The Retrofit service returns a coroutine Deferred, which we
+ * await to get the result of the transaction.
+ */
+ private fun getMarsRealEstateProperties() {
+ coroutineScope.launch {
+ _status.value = MarsApiStatus.LOADING
+ // this will run on a thread managed by Retrofit
+ var getPropertiesDeferred = MarsApi.retrofitService.getProperties()
+ try {
+ _status.value = MarsApiStatus.LOADING
+ // this will run on a thread managed by Retrofit
+ val listResult = getPropertiesDeferred.await()
+ _status.value = MarsApiStatus.DONE
+ _properties.value = listResult
+ } catch (e: Exception) {
+ _status.value = MarsApiStatus.ERROR
+ _properties.value = ArrayList()
+ }
+ }
+ }
+
+ /**
+ * When the [ViewModel] is finished, we cancel our coroutine [viewModelJob], which tells the
+ * Retrofit service to stop.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+}
diff --git a/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/overview/PhotoGridAdapter.kt b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/overview/PhotoGridAdapter.kt
new file mode 100644
index 0000000..89f4cc0
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/java/com/example/android/marsrealestate/overview/PhotoGridAdapter.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.overview
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.marsrealestate.databinding.GridViewItemBinding
+import com.example.android.marsrealestate.network.MarsProperty
+
+/**
+ * This class implements a [RecyclerView] [ListAdapter] which uses Data Binding to present [List]
+ * data, including computing diffs between lists.
+ */
+class PhotoGridAdapter : ListAdapter(DiffCallback) {
+
+ /**
+ * The MarsPropertyViewHolder constructor takes the binding variable from the associated
+ * GridViewItem, which nicely gives it access to the full [MarsProperty] information.
+ */
+ class MarsPropertyViewHolder(private var binding: GridViewItemBinding):
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(marsProperty: MarsProperty) {
+ binding.property = marsProperty
+ // This is important, because it forces the data binding to execute immediately,
+ // which allows the RecyclerView to make the correct view size measurements
+ binding.executePendingBindings()
+ }
+ }
+
+ /**
+ * Allows the RecyclerView to determine which items have changed when the [List] of [MarsProperty]
+ * has been updated.
+ */
+ companion object DiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
+ return oldItem === newItem
+ }
+
+ override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
+ return oldItem.id == newItem.id
+ }
+ }
+
+ /**
+ * Create new [RecyclerView] item views (invoked by the layout manager)
+ */
+ override fun onCreateViewHolder(parent: ViewGroup,
+ viewType: Int): MarsPropertyViewHolder {
+ return MarsPropertyViewHolder(GridViewItemBinding.inflate(LayoutInflater.from(parent.context)))
+ }
+
+ /**
+ * Replaces the contents of a view (invoked by the layout manager)
+ */
+ override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
+ val marsProperty = getItem(position)
+ holder.bind(marsProperty)
+ }
+}
diff --git a/MarsRealEstateGrid/app/src/main/res/drawable/ic_broken_image.xml b/MarsRealEstateGrid/app/src/main/res/drawable/ic_broken_image.xml
new file mode 100644
index 0000000..d7284d3
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/drawable/ic_broken_image.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/drawable/ic_connection_error.xml b/MarsRealEstateGrid/app/src/main/res/drawable/ic_connection_error.xml
new file mode 100644
index 0000000..70a71a6
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/drawable/ic_connection_error.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/drawable/ic_for_sale_outline.xml b/MarsRealEstateGrid/app/src/main/res/drawable/ic_for_sale_outline.xml
new file mode 100644
index 0000000..dc2a807
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/drawable/ic_for_sale_outline.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/drawable/ic_launcher_background.xml b/MarsRealEstateGrid/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..ed74491
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/drawable/ic_launcher_foreground.xml b/MarsRealEstateGrid/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..d91e222
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/drawable/loading_animation.xml b/MarsRealEstateGrid/app/src/main/res/drawable/loading_animation.xml
new file mode 100644
index 0000000..4b4721c
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/drawable/loading_animation.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/drawable/loading_img.xml b/MarsRealEstateGrid/app/src/main/res/drawable/loading_img.xml
new file mode 100644
index 0000000..c656871
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/drawable/loading_img.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/layout/activity_main.xml b/MarsRealEstateGrid/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..0435a9b
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/layout/fragment_detail.xml b/MarsRealEstateGrid/app/src/main/res/layout/fragment_detail.xml
new file mode 100644
index 0000000..002f892
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/layout/fragment_detail.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateGrid/app/src/main/res/layout/fragment_overview.xml b/MarsRealEstateGrid/app/src/main/res/layout/fragment_overview.xml
new file mode 100644
index 0000000..6902172
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/layout/fragment_overview.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateGrid/app/src/main/res/layout/grid_view_item.xml b/MarsRealEstateGrid/app/src/main/res/layout/grid_view_item.xml
new file mode 100644
index 0000000..f65c0a0
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/layout/grid_view_item.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/menu/overflow_menu.xml b/MarsRealEstateGrid/app/src/main/res/menu/overflow_menu.xml
new file mode 100755
index 0000000..90714fd
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/menu/overflow_menu.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/MarsRealEstateGrid/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/MarsRealEstateGrid/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-hdpi/ic_launcher.png b/MarsRealEstateGrid/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..0b24188
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/MarsRealEstateGrid/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4a8354b
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-mdpi/ic_launcher.png b/MarsRealEstateGrid/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..2f8a7d3
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/MarsRealEstateGrid/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..f39fc86
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/MarsRealEstateGrid/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..9a4a0c4
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/MarsRealEstateGrid/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..ea902dd
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/MarsRealEstateGrid/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..a17e8cf
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/MarsRealEstateGrid/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..815c41b
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/MarsRealEstateGrid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..30fd705
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/MarsRealEstateGrid/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4b947a8
Binary files /dev/null and b/MarsRealEstateGrid/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateGrid/app/src/main/res/navigation/nav_graph.xml b/MarsRealEstateGrid/app/src/main/res/navigation/nav_graph.xml
new file mode 100644
index 0000000..0f4c7c6
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateGrid/app/src/main/res/values/colors.xml b/MarsRealEstateGrid/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..871190d
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/values/colors.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/MarsRealEstateGrid/app/src/main/res/values/strings.xml b/MarsRealEstateGrid/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a46a3d6
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/values/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+ Real Estate on Mars
+ Show all
+ Rent
+ Buy
+ Rent
+ Sale
+ For %s
+ $%,.0f/month
+ $%,.0f
+
diff --git a/MarsRealEstateGrid/app/src/main/res/values/styles.xml b/MarsRealEstateGrid/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..57b1542
--- /dev/null
+++ b/MarsRealEstateGrid/app/src/main/res/values/styles.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateGrid/build.gradle b/MarsRealEstateGrid/build.gradle
new file mode 100644
index 0000000..a5f6ed2
--- /dev/null
+++ b/MarsRealEstateGrid/build.gradle
@@ -0,0 +1,42 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext {
+ // Versions for all the dependencies we plan to use. It's particularly useful for kotlin and
+ // navigation where the versions of the plugin needs to be the same as the version of the
+ // library defined in the app Gradle file
+ version_android_gradle_plugin = "3.3.2"
+ version_core = "1.0.1"
+ version_constraint_layout = "1.1.3"
+ version_glide = "4.8.0"
+ version_kotlin = "1.3.21"
+ version_kotlin_coroutines = "1.1.0"
+ version_lifecycle_extensions = "2.0.0"
+ version_moshi = "1.8.0"
+ version_navigation = "1.0.0"
+ version_retrofit = "2.5.0"
+ version_retrofit_coroutines_adapter = "0.9.2"
+ version_recyclerview = "1.0.0"
+ }
+
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$version_android_gradle_plugin"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$version_kotlin"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$version_navigation"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/MarsRealEstateGrid/gradle.properties b/MarsRealEstateGrid/gradle.properties
new file mode 100644
index 0000000..51dc505
--- /dev/null
+++ b/MarsRealEstateGrid/gradle.properties
@@ -0,0 +1,36 @@
+#
+# Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/MarsRealEstateGrid/gradle/wrapper/gradle-wrapper.jar b/MarsRealEstateGrid/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/MarsRealEstateGrid/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/MarsRealEstateGrid/gradle/wrapper/gradle-wrapper.properties b/MarsRealEstateGrid/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4278fe2
--- /dev/null
+++ b/MarsRealEstateGrid/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,23 @@
+#
+# Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#Tue Dec 18 16:00:56 PST 2018
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+android.databinding.enableV2=true
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
diff --git a/MarsRealEstateGrid/gradlew b/MarsRealEstateGrid/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/MarsRealEstateGrid/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/MarsRealEstateGrid/gradlew.bat b/MarsRealEstateGrid/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/MarsRealEstateGrid/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/MarsRealEstateGrid/settings.gradle b/MarsRealEstateGrid/settings.gradle
new file mode 100644
index 0000000..245db96
--- /dev/null
+++ b/MarsRealEstateGrid/settings.gradle
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+include ':app'
diff --git a/MarsRealEstateNetwork/README.md b/MarsRealEstateNetwork/README.md
new file mode 100644
index 0000000..733807e
--- /dev/null
+++ b/MarsRealEstateNetwork/README.md
@@ -0,0 +1,55 @@
+MarsRealEstateNetwork - Solution Code
+=====================================
+
+Solution code for Android Kotlin Fundamentals Codelab 8.1 Getting data from the internet
+
+Introduction
+------------
+
+MarsRealEstate is a demo app that shows available properties for sale and for rent on Mars.
+The property data is stored on a Web server as a REST web service. This app demonstrated
+the use of [Retrofit](https://square.github.io/retrofit/) to make REST requests to the
+web service, [Moshi](https://github.com/square/moshi) to handle the deserialization of the
+returned JSON to Kotlin data objects, and [Glide](https://bumptech.github.io/glide/) to load and
+cache images by URL.
+
+The app also leverages [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel),
+[LiveData](https://developer.android.com/topic/libraries/architecture/livedata),
+[Data Binding](https://developer.android.com/topic/libraries/data-binding/) with binding
+adapters, and [Navigation](https://developer.android.com/topic/libraries/architecture/navigation/)
+with the SafeArgs plugin for parameter passing between fragments.
+
+Pre-requisites
+--------------
+
+You need to know:
+- How to create and use fragments.
+- How to navigate between fragments, and use safeArgs to pass data between fragments.
+- How to use architecture components including ViewModel, ViewModelProvider.Factory, LiveData, and LiveData transformations.
+- How to use coroutines for long-running tasks.
+
+
+Getting Started
+---------------
+
+1. Download and run the app.
+
+License
+-------
+
+Copyright 2019 Google, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements. See the NOTICE file distributed with this work for
+additional information regarding copyright ownership. The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
\ No newline at end of file
diff --git a/MarsRealEstateNetwork/app/.gitignore b/MarsRealEstateNetwork/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/MarsRealEstateNetwork/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/MarsRealEstateNetwork/app/build.gradle b/MarsRealEstateNetwork/app/build.gradle
new file mode 100644
index 0000000..d06d288
--- /dev/null
+++ b/MarsRealEstateNetwork/app/build.gradle
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+apply plugin: "androidx.navigation.safeargs"
+
+android {
+ compileSdkVersion 28
+ dataBinding {
+ enabled = true
+ }
+ defaultConfig {
+ applicationId "com.example.android.marsrealestate"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ androidExtensions {
+ experimental = true
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ // Kotlin
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$version_kotlin"
+
+ // Constraint Layout
+ implementation "androidx.constraintlayout:constraintlayout:$version_constraint_layout"
+
+ // ViewModel and LiveData
+ implementation "androidx.lifecycle:lifecycle-extensions:$version_lifecycle_extensions"
+
+ // Navigation
+ implementation "android.arch.navigation:navigation-fragment-ktx:$version_navigation"
+ implementation "android.arch.navigation:navigation-ui-ktx:$version_navigation"
+
+ // Core with Ktx
+ implementation "androidx.core:core-ktx:$version_core"
+
+ // Moshi
+ implementation "com.squareup.moshi:moshi:$version_moshi"
+ implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
+
+ // Retrofit with Moshi Converter
+ implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
+ implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
+
+ // Coroutines
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines"
+
+ // Retrofit Coroutines Support
+ implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"
+}
diff --git a/MarsRealEstateNetwork/app/proguard-rules.pro b/MarsRealEstateNetwork/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/MarsRealEstateNetwork/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/MarsRealEstateNetwork/app/src/main/AndroidManifest.xml b/MarsRealEstateNetwork/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0de9e05
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/AndroidManifest.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateNetwork/app/src/main/ic_launcher-web.png b/MarsRealEstateNetwork/app/src/main/ic_launcher-web.png
new file mode 100644
index 0000000..87e04ff
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/ic_launcher-web.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/BindingAdapters.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/BindingAdapters.kt
new file mode 100644
index 0000000..8f6ac76
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/BindingAdapters.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate
+
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/MainActivity.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/MainActivity.kt
new file mode 100644
index 0000000..e1eb3ff
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/MainActivity.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+class MainActivity : AppCompatActivity() {
+
+ /**
+ * Our MainActivity is only responsible for setting the content view that contains the
+ * Navigation Host.
+ */
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ }
+}
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/detail/DetailFragment.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/detail/DetailFragment.kt
new file mode 100644
index 0000000..f4a02c9
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/detail/DetailFragment.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.marsrealestate.detail
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProviders
+import com.example.android.marsrealestate.databinding.FragmentDetailBinding
+
+/**
+ * This [Fragment] will show the detailed information about a selected piece of Mars real estate.
+ */
+class DetailFragment : Fragment() {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ @Suppress("UNUSED_VARIABLE")
+ val application = requireNotNull(activity).application
+ val binding = FragmentDetailBinding.inflate(inflater)
+ binding.setLifecycleOwner(this)
+ return binding.root
+ }
+}
\ No newline at end of file
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModel.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModel.kt
new file mode 100644
index 0000000..915d91e
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModel.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.marsrealestate.detail
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.ViewModel
+import com.example.android.marsrealestate.detail.DetailFragment
+import com.example.android.marsrealestate.network.MarsProperty
+
+/**
+ * The [ViewModel] that is associated with the [DetailFragment].
+ */
+class DetailViewModel(@Suppress("UNUSED_PARAMETER")marsProperty: MarsProperty, app: Application) : AndroidViewModel(app) {
+}
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModelFactory.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModelFactory.kt
new file mode 100644
index 0000000..2a74937
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/detail/DetailViewModelFactory.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.marsrealestate.detail
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.marsrealestate.network.MarsProperty
+
+/**
+ * Simple ViewModel factory that provides the MarsProperty and context to the ViewModel.
+ */
+class DetailViewModelFactory(
+ private val marsProperty: MarsProperty,
+ private val application: Application) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(DetailViewModel::class.java)) {
+ return DetailViewModel(marsProperty, application) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/network/MarsApiService.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/network/MarsApiService.kt
new file mode 100644
index 0000000..f010ccf
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/network/MarsApiService.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.network
+
+import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+import retrofit2.http.GET
+import kotlinx.coroutines.Deferred
+
+private const val BASE_URL = " https://android-kotlin-fun-mars-server.appspot.com/"
+
+/**
+ * Build the Moshi object that Retrofit will be using, making sure to add the Kotlin adapter for
+ * full Kotlin compatibility.
+ */
+private val moshi = Moshi.Builder()
+ .add(KotlinJsonAdapterFactory())
+ .build()
+
+/**
+ * Use the Retrofit builder to build a retrofit object using a Moshi converter with our Moshi
+ * object.
+ */
+private val retrofit = Retrofit.Builder()
+ .addConverterFactory(MoshiConverterFactory.create(moshi))
+ .addCallAdapterFactory(CoroutineCallAdapterFactory())
+ .baseUrl(BASE_URL)
+ .build()
+
+/**
+ * A public interface that exposes the [getProperties] method
+ */
+interface MarsApiService {
+ /**
+ * Returns a Coroutine [Deferred] [List] of [MarsProperty] which can be fetched with await() if
+ * in a Coroutine scope.
+ * The @GET annotation indicates that the "realestate" endpoint will be requested with the GET
+ * HTTP method
+ */
+ @GET("realestate")
+ fun getProperties():
+ // The Coroutine Call Adapter allows us to return a Deferred, a Job with a result
+ Deferred>
+}
+
+/**
+ * A public Api object that exposes the lazy-initialized Retrofit service
+ */
+object MarsApi {
+ val retrofitService : MarsApiService by lazy { retrofit.create(MarsApiService::class.java) }
+}
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/network/MarsProperty.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/network/MarsProperty.kt
new file mode 100644
index 0000000..b686056
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/network/MarsProperty.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.network
+
+import com.squareup.moshi.Json
+
+/**
+ * This data class defines a Mars property which includes an ID, the image URL, the type (sale
+ * or rental) and the price (monthly if it's a rental).
+ * The property names of this data class are used by Moshi to match the names of values in JSON.
+ */
+data class MarsProperty(
+ val id: String,
+ // used to map img_src from the JSON to imgSrcUrl in our class
+ @Json(name = "img_src") val imgSrcUrl: String,
+ val type: String,
+ val price: Double)
\ No newline at end of file
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/overview/OverviewFragment.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/overview/OverviewFragment.kt
new file mode 100644
index 0000000..15ae91e
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/overview/OverviewFragment.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.overview
+
+import android.os.Bundle
+import android.view.*
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProviders
+import com.example.android.marsrealestate.R
+import com.example.android.marsrealestate.databinding.FragmentOverviewBinding
+
+/**
+ * This fragment shows the the status of the Mars real-estate web services transaction.
+ */
+class OverviewFragment : Fragment() {
+
+ /**
+ * Lazily initialize our [OverviewViewModel].
+ */
+ private val viewModel: OverviewViewModel by lazy {
+ ViewModelProviders.of(this).get(OverviewViewModel::class.java)
+ }
+
+ /**
+ * Inflates the layout with Data Binding, sets its lifecycle owner to the OverviewFragment
+ * to enable Data Binding to observe LiveData, and sets up the RecyclerView with an adapter.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val binding = FragmentOverviewBinding.inflate(inflater)
+
+ // Allows Data Binding to Observe LiveData with the lifecycle of this Fragment
+ binding.setLifecycleOwner(this)
+
+ // Giving the binding access to the OverviewViewModel
+ binding.viewModel = viewModel
+
+ setHasOptionsMenu(true)
+ return binding.root
+ }
+
+ /**
+ * Inflates the overflow menu that contains filtering options.
+ */
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ inflater.inflate(R.menu.overflow_menu, menu)
+ super.onCreateOptionsMenu(menu, inflater)
+ }
+}
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/overview/OverviewViewModel.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/overview/OverviewViewModel.kt
new file mode 100644
index 0000000..3bdcc19
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/overview/OverviewViewModel.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.overview
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.marsrealestate.network.MarsApi
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+/**
+ * The [ViewModel] that is attached to the [OverviewFragment].
+ */
+class OverviewViewModel : ViewModel() {
+
+ // The internal MutableLiveData String that stores the most recent response
+ private val _response = MutableLiveData()
+
+ // The external immutable LiveData for the response String
+ val response: LiveData
+ get() = _response
+
+ // Create a Coroutine scope using a job to be able to cancel when needed
+ private var viewModelJob = Job()
+
+ // the Coroutine runs using the Main (UI) dispatcher
+ private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main )
+
+ /**
+ * Call getMarsRealEstateProperties() on init so we can display status immediately.
+ */
+ init {
+ getMarsRealEstateProperties()
+ }
+
+ /**
+ * Sets the value of the response LiveData to the Mars API status or the successful number of
+ * Mars properties retrieved.
+ */
+ private fun getMarsRealEstateProperties() {
+ coroutineScope.launch {
+ // Get the Deferred object for our Retrofit request
+ var getPropertiesDeferred = MarsApi.retrofitService.getProperties()
+ try {
+ // Await the completion of our Retrofit request
+ var listResult = getPropertiesDeferred.await()
+ _response.value = "Success: ${listResult.size} Mars properties retrieved"
+ } catch (e: Exception) {
+ _response.value = "Failure: ${e.message}"
+ }
+ }
+ }
+
+ /**
+ * When the [ViewModel] is finished, we cancel our coroutine [viewModelJob], which tells the
+ * Retrofit service to stop.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+}
diff --git a/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/overview/PhotoGridAdapter.kt b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/overview/PhotoGridAdapter.kt
new file mode 100644
index 0000000..0a53a20
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/java/com/example/android/marsrealestate/overview/PhotoGridAdapter.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.example.android.marsrealestate.overview
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/drawable/ic_broken_image.xml b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_broken_image.xml
new file mode 100644
index 0000000..d7284d3
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_broken_image.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/drawable/ic_connection_error.xml b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_connection_error.xml
new file mode 100644
index 0000000..70a71a6
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_connection_error.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/drawable/ic_for_sale_outline.xml b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_for_sale_outline.xml
new file mode 100644
index 0000000..dc2a807
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_for_sale_outline.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/drawable/ic_launcher_background.xml b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..ed74491
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/drawable/ic_launcher_foreground.xml b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..d91e222
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/drawable/loading_animation.xml b/MarsRealEstateNetwork/app/src/main/res/drawable/loading_animation.xml
new file mode 100644
index 0000000..4b4721c
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/drawable/loading_animation.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/drawable/loading_img.xml b/MarsRealEstateNetwork/app/src/main/res/drawable/loading_img.xml
new file mode 100644
index 0000000..c656871
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/drawable/loading_img.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/layout/activity_main.xml b/MarsRealEstateNetwork/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..0435a9b
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/layout/fragment_detail.xml b/MarsRealEstateNetwork/app/src/main/res/layout/fragment_detail.xml
new file mode 100644
index 0000000..002f892
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/layout/fragment_detail.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateNetwork/app/src/main/res/layout/fragment_overview.xml b/MarsRealEstateNetwork/app/src/main/res/layout/fragment_overview.xml
new file mode 100644
index 0000000..0eebf5f
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/layout/fragment_overview.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateNetwork/app/src/main/res/layout/grid_view_item.xml b/MarsRealEstateNetwork/app/src/main/res/layout/grid_view_item.xml
new file mode 100644
index 0000000..b6a23a1
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/layout/grid_view_item.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/menu/overflow_menu.xml b/MarsRealEstateNetwork/app/src/main/res/menu/overflow_menu.xml
new file mode 100755
index 0000000..90714fd
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/menu/overflow_menu.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/MarsRealEstateNetwork/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/MarsRealEstateNetwork/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-hdpi/ic_launcher.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..0b24188
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4a8354b
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-mdpi/ic_launcher.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..2f8a7d3
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..f39fc86
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..9a4a0c4
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..ea902dd
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..a17e8cf
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..815c41b
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..30fd705
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/MarsRealEstateNetwork/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4b947a8
Binary files /dev/null and b/MarsRealEstateNetwork/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/MarsRealEstateNetwork/app/src/main/res/navigation/nav_graph.xml b/MarsRealEstateNetwork/app/src/main/res/navigation/nav_graph.xml
new file mode 100644
index 0000000..0f4c7c6
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/values/colors.xml b/MarsRealEstateNetwork/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..871190d
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/values/colors.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/values/strings.xml b/MarsRealEstateNetwork/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a46a3d6
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/values/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+ Real Estate on Mars
+ Show all
+ Rent
+ Buy
+ Rent
+ Sale
+ For %s
+ $%,.0f/month
+ $%,.0f
+
diff --git a/MarsRealEstateNetwork/app/src/main/res/values/styles.xml b/MarsRealEstateNetwork/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..57b1542
--- /dev/null
+++ b/MarsRealEstateNetwork/app/src/main/res/values/styles.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
diff --git a/MarsRealEstateNetwork/build.gradle b/MarsRealEstateNetwork/build.gradle
new file mode 100644
index 0000000..a5f6ed2
--- /dev/null
+++ b/MarsRealEstateNetwork/build.gradle
@@ -0,0 +1,42 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext {
+ // Versions for all the dependencies we plan to use. It's particularly useful for kotlin and
+ // navigation where the versions of the plugin needs to be the same as the version of the
+ // library defined in the app Gradle file
+ version_android_gradle_plugin = "3.3.2"
+ version_core = "1.0.1"
+ version_constraint_layout = "1.1.3"
+ version_glide = "4.8.0"
+ version_kotlin = "1.3.21"
+ version_kotlin_coroutines = "1.1.0"
+ version_lifecycle_extensions = "2.0.0"
+ version_moshi = "1.8.0"
+ version_navigation = "1.0.0"
+ version_retrofit = "2.5.0"
+ version_retrofit_coroutines_adapter = "0.9.2"
+ version_recyclerview = "1.0.0"
+ }
+
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$version_android_gradle_plugin"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$version_kotlin"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$version_navigation"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/MarsRealEstateNetwork/gradle.properties b/MarsRealEstateNetwork/gradle.properties
new file mode 100644
index 0000000..51dc505
--- /dev/null
+++ b/MarsRealEstateNetwork/gradle.properties
@@ -0,0 +1,36 @@
+#
+# Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/MarsRealEstateNetwork/gradle/wrapper/gradle-wrapper.jar b/MarsRealEstateNetwork/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/MarsRealEstateNetwork/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/MarsRealEstateNetwork/gradle/wrapper/gradle-wrapper.properties b/MarsRealEstateNetwork/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4278fe2
--- /dev/null
+++ b/MarsRealEstateNetwork/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,23 @@
+#
+# Copyright 2019, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#Tue Dec 18 16:00:56 PST 2018
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+android.databinding.enableV2=true
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
diff --git a/MarsRealEstateNetwork/gradlew b/MarsRealEstateNetwork/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/MarsRealEstateNetwork/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/MarsRealEstateNetwork/gradlew.bat b/MarsRealEstateNetwork/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/MarsRealEstateNetwork/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/MarsRealEstateNetwork/settings.gradle b/MarsRealEstateNetwork/settings.gradle
new file mode 100644
index 0000000..245db96
--- /dev/null
+++ b/MarsRealEstateNetwork/settings.gradle
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+include ':app'