Skip to content
This repository was archived by the owner on Aug 22, 2024. It is now read-only.

Commit 803efbf

Browse files
authored
Merge pull request #105 from amardeshbd/102-learning-resource-list
102 learning resource list using Firebase firestore Fixes #102
2 parents 2d2199e + 9591af1 commit 803efbf

26 files changed

+696
-28
lines changed

.idea/assetWizardSettings.xml

+3-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle

+7
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ dependencies {
4747
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
4848

4949
// Android Support libs and Google Android libs
50+
implementation "androidx.appcompat:appcompat:$rootProject.supportAppCompatVersion"
5051
implementation "androidx.legacy:legacy-support-v13:$rootProject.supportLibraryVersion"
5152
implementation "com.google.android.material:material:$rootProject.supportLibraryVersion"
5253
implementation "androidx.cardview:cardview:$rootProject.supportLibraryVersion"
@@ -64,8 +65,11 @@ dependencies {
6465
// ========================================================
6566
// 3rd party libraries
6667
// ========================================================
68+
69+
// Leak Canary
6770
debugImplementation "com.squareup.leakcanary:leakcanary-android:$rootProject.leakcanaryLibraryVersion"
6871

72+
// Timber
6973
implementation "com.jakewharton.timber:timber:$rootProject.timberLibraryVersion"
7074

7175
// Crashlytics
@@ -75,6 +79,9 @@ dependencies {
7579
// Firebase Core
7680
// https://firebase.google.com/docs/android/setup
7781
implementation "com.google.firebase:firebase-core:$rootProject.firebaseVersion"
82+
// Firestore cloud realtime database
83+
// https://firebase.google.com/docs/firestore/quickstart?authuser=0
84+
implementation "com.google.firebase:firebase-firestore:$rootProject.firestoreVersion"
7885

7986
// Dagger
8087
// https://google.github.io/dagger/

app/src/main/AndroidManifest.xml

+21-17
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,41 @@
1212
android:supportsRtl="true"
1313
android:theme="@style/AppTheme"
1414
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
15-
<activity android:name="com.hossainkhan.android.demo.ui.browse.LayoutBrowseActivity">
15+
<activity android:name=".ui.browse.LayoutBrowseActivity">
1616
<intent-filter>
1717
<action android:name="android.intent.action.MAIN" />
1818

1919
<category android:name="android.intent.category.LAUNCHER" />
2020
</intent-filter>
2121
</activity>
2222
<activity
23-
android:name="com.hossainkhan.android.demo.ui.layoutpreview.LayoutPreviewBaseActivity"
24-
android:parentActivityName="com.hossainkhan.android.demo.ui.browse.LayoutBrowseActivity" />
23+
android:name=".ui.layoutpreview.LayoutPreviewBaseActivity"
24+
android:parentActivityName=".ui.browse.LayoutBrowseActivity" />
2525
<activity
26-
android:name="com.hossainkhan.android.demo.ui.layoutpreview.LayoutVisibilityGoneActivity"
27-
android:parentActivityName="com.hossainkhan.android.demo.ui.browse.LayoutBrowseActivity" />
26+
android:name=".ui.layoutpreview.LayoutVisibilityGoneActivity"
27+
android:parentActivityName=".ui.browse.LayoutBrowseActivity" />
2828
<activity
29-
android:name="com.hossainkhan.android.demo.ui.layoutpreview.LayoutChainStyleActivity"
30-
android:parentActivityName="com.hossainkhan.android.demo.ui.browse.LayoutBrowseActivity" />
29+
android:name=".ui.layoutpreview.LayoutChainStyleActivity"
30+
android:parentActivityName=".ui.browse.LayoutBrowseActivity" />
3131
<activity
32-
android:name="com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineBarrierActivity"
33-
android:parentActivityName="com.hossainkhan.android.demo.ui.browse.LayoutBrowseActivity" />
32+
android:name=".ui.layoutpreview.LayoutGuidelineBarrierActivity"
33+
android:parentActivityName=".ui.browse.LayoutBrowseActivity" />
3434
<activity
35-
android:name="com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineGroupActivity"
36-
android:parentActivityName="com.hossainkhan.android.demo.ui.browse.LayoutBrowseActivity" />
35+
android:name=".ui.layoutpreview.LayoutGuidelineGroupActivity"
36+
android:parentActivityName=".ui.browse.LayoutBrowseActivity" />
3737
<activity
38-
android:name="com.hossainkhan.android.demo.ui.layoutpreview.LayoutDimensionMinMaxActivity"
39-
android:parentActivityName="com.hossainkhan.android.demo.ui.browse.LayoutBrowseActivity" />
38+
android:name=".ui.layoutpreview.LayoutDimensionMinMaxActivity"
39+
android:parentActivityName=".ui.browse.LayoutBrowseActivity" />
4040
<activity
41-
android:name="com.hossainkhan.android.demo.ui.functionaldemo.MovieDetailsPreviewActivity"
42-
android:parentActivityName="com.hossainkhan.android.demo.ui.browse.LayoutBrowseActivity" />
41+
android:name=".ui.functionaldemo.MovieDetailsPreviewActivity"
42+
android:parentActivityName=".ui.browse.LayoutBrowseActivity" />
4343
<activity
44-
android:name="com.hossainkhan.android.demo.ui.functionaldemo.TedTalkPlaybackActivity"
45-
android:parentActivityName="com.hossainkhan.android.demo.ui.browse.LayoutBrowseActivity" />
44+
android:name=".ui.functionaldemo.TedTalkPlaybackActivity"
45+
android:parentActivityName=".ui.browse.LayoutBrowseActivity" />
46+
<activity
47+
android:label="@string/additional_resource_title"
48+
android:name=".ui.resource.LearningResourceActivity"
49+
android:parentActivityName=".ui.browse.LayoutBrowseActivity" />
4650
</application>
4751

4852
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2019 Hossain Khan
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.hossainkhan.android.demo.data
18+
19+
import com.google.firebase.Timestamp
20+
import java.text.SimpleDateFormat
21+
import java.util.Date
22+
import java.util.Locale
23+
24+
25+
object FirestoreDateFormatter {
26+
fun date(timestamp: Timestamp): String {
27+
val sfd = SimpleDateFormat("EEEE, MMMM d", Locale.CANADA)
28+
return sfd.format(timestamp.toDate())
29+
}
30+
31+
fun date(date: Date): String {
32+
val sfd = SimpleDateFormat("MMMM d, yyyy", Locale.CANADA)
33+
return sfd.format(date)
34+
}
35+
}

app/src/main/java/com/hossainkhan/android/demo/data/LayoutDataStore.kt

+13-2
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,16 @@ class LayoutDataStore @Inject constructor(
197197
layoutResourceId = R.layout.demo_ted_talk_playback,
198198
thumbnailResourceId = R.drawable.ic_ted_talks_logo,
199199
title = "Demo: TED Talk Preview Screen",
200-
description = "A demo screen containing TED talks video playback screen with different controls.")
200+
description = "A demo screen containing TED talks video playback screen with different controls."),
201+
202+
/*
203+
* Additional resources to learn more about ConstraintLayout
204+
*/
205+
LayoutInformation(
206+
layoutResourceId = R.layout.activity_learning_resource,
207+
thumbnailResourceId = R.drawable.ic_book_128dp,
208+
title = "Additional Resources: Tech Talks & Blogs",
209+
description = "External resources on constraint layout. Tech talks on YouTube or blogs.")
201210

202211

203212
/*
@@ -223,7 +232,7 @@ class LayoutDataStore @Inject constructor(
223232
* Returns Github URL for layout resource file for this project.
224233
*
225234
* @param layoutResourceId The layout resource ID to generate the URL for.
226-
* @return The URL to load the layout blob file.
235+
* @return The URL to load the layout blob file. For example: https://github.com/amardeshbd/android-constraint-layout-cheatsheet/blob/master/app/src/main/res/layout/preview_dimension_ratio.xml
227236
*/
228237
fun getLayoutUrl(@LayoutRes layoutResourceId: Int): String {
229238
// Contains package name and layout name
@@ -234,6 +243,8 @@ class LayoutDataStore @Inject constructor(
234243
throw IllegalStateException("Only layout resource is allowed.")
235244
}
236245

246+
// Returns fully qualified URL that can be viewed online. For example:
247+
// https://github.com/amardeshbd/android-constraint-layout-cheatsheet/blob/master/app/src/main/res/layout/preview_dimension_ratio.xml
237248
return AppConfig.GITHUB_BASE_URL.plus("/blob/master/app/src/main/res/") +
238249
resourceName.split(':')
239250
.last().plus(".xml")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) 2019 Hossain Khan
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.hossainkhan.android.demo.data
18+
19+
import com.google.firebase.Timestamp
20+
import java.util.*
21+
22+
23+
/**
24+
* External resource info.
25+
*
26+
* Example:
27+
* - author: Nicolas Roard & John Hoford
28+
* - event: Google I/O'19
29+
* - publish_date: 9 May 2019
30+
* - summary: Learn the capabilities of ConstraintLayout
31+
* - title: What's New in ConstraintLayout
32+
* - type: techtalk
33+
* - url: https://www.youtube.com/watch?v=29gLA90m6Gk
34+
*/
35+
data class ResourceInfo(
36+
var author: String = "",
37+
var summary: String = "",
38+
var title: String = "",
39+
var event: String = "",
40+
var url: String = "",
41+
var publish_date: Timestamp? = null,
42+
/**
43+
* Possible values
44+
* "blog", "techtalk"
45+
*/
46+
var type: String = "techtalk"
47+
) {
48+
val formattedDate: String
49+
get() {
50+
val date = publish_date?.toDate() ?: Date()
51+
return FirestoreDateFormatter.date(date)
52+
}
53+
}

app/src/main/java/com/hossainkhan/android/demo/di/ActivityBindingModule.kt

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineBarrierActiv
2424
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineGroupActivity
2525
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutPreviewBaseActivity
2626
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutVisibilityGoneActivity
27+
import com.hossainkhan.android.demo.ui.resource.LearningResourceActivity
2728
import dagger.Module
2829
import dagger.android.ContributesAndroidInjector
2930

@@ -83,4 +84,8 @@ abstract class ActivityBindingModule {
8384
@ActivityScope
8485
@ContributesAndroidInjector
8586
abstract fun layoutTedTalkPlaybackPreviewActivity(): TedTalkPlaybackActivity
87+
88+
@ActivityScope
89+
@ContributesAndroidInjector
90+
abstract fun learningResourceActivity(): LearningResourceActivity
8691
}

app/src/main/java/com/hossainkhan/android/demo/di/DataStoreModule.kt

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.hossainkhan.android.demo.di
1919
import android.content.Context
2020
import android.content.SharedPreferences
2121
import android.content.res.Resources
22+
import com.google.firebase.firestore.FirebaseFirestore
2223
import dagger.Module
2324
import dagger.Provides
2425

@@ -33,4 +34,9 @@ class DataStoreModule {
3334
internal fun provideAndroidResoures(context: Context): Resources {
3435
return context.resources
3536
}
37+
38+
@Provides
39+
internal fun provideFirestore(context: Context): FirebaseFirestore {
40+
return FirebaseFirestore.getInstance()
41+
}
3642
}

app/src/main/java/com/hossainkhan/android/demo/di/ViewModelModule.kt

+8-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.lifecycle.ViewModel
2020
import androidx.lifecycle.ViewModelProvider
2121
import com.hossainkhan.android.demo.ui.browse.LayoutBrowseViewModel
2222
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutInfoViewModel
23+
import com.hossainkhan.android.demo.ui.resource.LearningResourceViewModel
2324
import com.hossainkhan.android.demo.viewmodel.ViewModelProviderFactory
2425
import dagger.Binds
2526
import dagger.Module
@@ -36,12 +37,17 @@ abstract class ViewModelModule {
3637
@Binds
3738
@IntoMap
3839
@ViewModelKey(LayoutBrowseViewModel::class)
39-
abstract fun bindLayoutBrowserViewModel(layoutBrowseViewModel: LayoutBrowseViewModel): ViewModel
40+
abstract fun bindLayoutBrowserViewModel(viewModel: LayoutBrowseViewModel): ViewModel
4041

4142
@Binds
4243
@IntoMap
4344
@ViewModelKey(LayoutInfoViewModel::class)
44-
abstract fun bindLayoutInfoViewModel(layoutInfoViewModel: LayoutInfoViewModel): ViewModel
45+
abstract fun bindLayoutInfoViewModel(viewModel: LayoutInfoViewModel): ViewModel
46+
47+
@Binds
48+
@IntoMap
49+
@ViewModelKey(LearningResourceViewModel::class)
50+
abstract fun bindResourceViewModel(viewModel: LearningResourceViewModel): ViewModel
4551

4652
@Binds
4753
abstract fun bindViewModelFactory(factory: ViewModelProviderFactory): ViewModelProvider.Factory

app/src/main/java/com/hossainkhan/android/demo/ui/browse/DefaultLayoutBrowseNavigator.kt

+7
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,11 @@ class DefaultLayoutBrowseNavigator @Inject constructor(private val context: Cont
3535
startIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
3636
context.startActivity(startIntent)
3737
}
38+
39+
override fun <T> loadActivity(clazz: Class<T>) {
40+
val startIntent = Intent(context, clazz)
41+
// FIX IT: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
42+
startIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
43+
context.startActivity(startIntent)
44+
}
3845
}

app/src/main/java/com/hossainkhan/android/demo/ui/browse/LayoutBrowseActivity.kt

+29
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
package com.hossainkhan.android.demo.ui.browse
1818

1919
import android.os.Bundle
20+
import android.view.Menu
21+
import android.view.MenuItem
2022
import androidx.appcompat.app.AppCompatActivity
23+
import androidx.core.app.NavUtils
2124
import androidx.lifecycle.ViewModelProviders
2225
import androidx.recyclerview.widget.GridLayoutManager
2326
import androidx.recyclerview.widget.RecyclerView
@@ -69,4 +72,30 @@ class LayoutBrowseActivity : AppCompatActivity() {
6972
adapter = viewAdapter
7073
}
7174
}
75+
76+
//
77+
// Setup menu item on the action bar.
78+
//
79+
override fun onCreateOptionsMenu(menu: Menu): Boolean {
80+
val inflater = menuInflater
81+
inflater.inflate(R.menu.menu_layout_browse, menu)
82+
return true
83+
}
84+
85+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
86+
return when (item.itemId) {
87+
R.id.show_external_resource -> {
88+
viewModel.onExternalResourceSelected()
89+
true
90+
}
91+
android.R.id.home -> {
92+
// Respond to the action bar's Up/Home button
93+
// https://developer.android.com/training/implementing-navigation/ancestral
94+
NavUtils.navigateUpFromSameTask(this)
95+
return true
96+
}
97+
else -> super.onOptionsItemSelected(item)
98+
}
99+
}
100+
72101
}

app/src/main/java/com/hossainkhan/android/demo/ui/browse/LayoutBrowseNavigator.kt

+1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ import androidx.annotation.LayoutRes
2424
interface LayoutBrowseNavigator {
2525
fun loadLayoutPreview(@LayoutRes layoutResId: Int)
2626
fun <T> loadLayoutPreview(clazz: Class<T>, @LayoutRes layoutResId: Int)
27+
fun <T> loadActivity(clazz: Class<T>)
2728
}

app/src/main/java/com/hossainkhan/android/demo/ui/browse/LayoutBrowseViewModel.kt

+8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.hossainkhan.android.demo.ui.layoutpreview.LayoutDimensionMinMaxActivi
2929
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineBarrierActivity
3030
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineGroupActivity
3131
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutVisibilityGoneActivity
32+
import com.hossainkhan.android.demo.ui.resource.LearningResourceActivity
3233
import timber.log.Timber
3334
import javax.inject.Inject
3435

@@ -78,11 +79,18 @@ class LayoutBrowseViewModel @Inject constructor(
7879
R.layout.demo_ted_talk_playback -> {
7980
browseNavigator.loadLayoutPreview(TedTalkPlaybackActivity::class.java, layoutResId)
8081
}
82+
R.layout.activity_learning_resource -> {
83+
browseNavigator.loadActivity(LearningResourceActivity::class.java)
84+
}
8185
else -> {
8286
// By default it loads the preview activity with the layout requested.
8387
browseNavigator.loadLayoutPreview(layoutResId)
8488
}
8589
}
8690

8791
}
92+
93+
fun onExternalResourceSelected() {
94+
browseNavigator.loadActivity(LearningResourceActivity::class.java)
95+
}
8896
}

0 commit comments

Comments
 (0)