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

Commit 95db8b7

Browse files
authored
Merge pull request #15 from amardeshbd/feature/12_barebone_layout_screen
[ADDED] [#12] Better info snackbar and menu icon to reshow the snackbar.
2 parents 3a3b1a9 + 7684d7e commit 95db8b7

File tree

8 files changed

+209
-16
lines changed

8 files changed

+209
-16
lines changed

app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ dependencies {
7979
// ----------------------------------------------------------------
8080
// Android Unit and Instrumentation test
8181
// ----------------------------------------------------------------
82+
// Required -- JUnit 4 framework
8283
testImplementation "junit:junit:$rootProject.junitVersion"
84+
// Mocking framework for unit tests written in Java
85+
testImplementation "org.mockito:mockito-core:$rootProject.mockitoVersion"
8386

8487
androidTestImplementation("com.android.support.test.espresso:espresso-core:$rootProject.espressoVersion", {
8588
exclude group: 'com.android.support', module: 'support-annotations'

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.hossainkhan.android.demo.data
1919
import android.content.res.Resources
2020
import android.support.annotation.LayoutRes
2121
import com.hossainkhan.android.demo.R
22+
import com.hossainkhan.android.demo.base.AppConfig
2223
import javax.inject.Inject
2324
import javax.inject.Singleton
2425

@@ -45,8 +46,21 @@ class LayoutDataStore @Inject constructor(
4546

4647
/**
4748
* Returns Github URL for layout resource file for this project.
49+
*
50+
* @param
51+
* @return The URL to load the layout blob file.
4852
*/
4953
fun getLayoutUrl(@LayoutRes layoutResourceId: Int): String {
50-
return resources.getResourceName(layoutResourceId)
54+
// Containes package name and layout name
55+
// com.hossainkhan.android.demo:layout/activity_positioning_top_left
56+
val resourceName = resources.getResourceName(layoutResourceId)
57+
58+
if(!resourceName.contains("layout")) {
59+
throw IllegalStateException("Only layout resource is allowed.")
60+
}
61+
62+
return AppConfig.GITHUB_BASE_URL.plus("/blob/master/app/src/main/res/") +
63+
resourceName.split(':')
64+
.last().plus(".xml")
5165
}
5266
}

app/src/main/java/com/hossainkhan/android/demo/layoutpositioning/LayoutPositioningDemoActivity.kt

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import android.content.Intent
2121
import android.os.Bundle
2222
import android.support.annotation.LayoutRes
2323
import android.support.v7.app.AppCompatActivity
24+
import android.view.Menu
25+
import android.view.MenuItem
2426
import com.andrognito.flashbar.Flashbar
27+
import com.hossainkhan.android.demo.R
2528
import com.hossainkhan.android.demo.data.AppDataStore
2629
import com.hossainkhan.android.demo.data.LayoutInformation
2730
import dagger.android.AndroidInjection
@@ -54,13 +57,16 @@ class LayoutPositioningDemoActivity : AppCompatActivity() {
5457
@Inject
5558
lateinit var appDataStore: AppDataStore
5659

60+
lateinit var layoutInformation: LayoutInformation
61+
var flashbar: Flashbar? = null
62+
5763
override fun onCreate(savedInstanceState: Bundle?) {
5864
AndroidInjection.inject(this)
5965
super.onCreate(savedInstanceState)
6066

6167
val layoutResourceId = intent.getIntExtra(BUNDLE_KEY_LAYOUT_RESID, -1)
6268
val resourceName = appDataStore.layoutStore.getLayoutUrl(layoutResourceId)
63-
val layoutInformation = appDataStore.layoutStore.layoutsInfos[layoutResourceId]!!
69+
layoutInformation = appDataStore.layoutStore.layoutsInfos[layoutResourceId]!!
6470
Timber.d("Loading layout: %s, info: %s", resourceName, layoutInformation)
6571

6672
setContentView(layoutResourceId)
@@ -70,18 +76,58 @@ class LayoutPositioningDemoActivity : AppCompatActivity() {
7076
showLayoutInfo(layoutInformation)
7177
}
7278

79+
/**
80+
* Loads layout information and previews in a snackbar.
81+
*/
7382
private fun showLayoutInfo(layoutInformation: LayoutInformation) {
74-
Flashbar.Builder(this)
75-
.gravity(Flashbar.Gravity.BOTTOM)
76-
.title(layoutInformation.title.toString())
77-
.message(layoutInformation.description.toString())
78-
.primaryActionText("OK")
79-
.primaryActionTapListener(object : Flashbar.OnActionTapListener {
80-
override fun onActionTapped(bar: Flashbar) {
81-
bar.dismiss()
82-
}
83-
})
84-
.build()
85-
.show()
83+
if (flashbar == null) {
84+
flashbar = Flashbar.Builder(this)
85+
.gravity(Flashbar.Gravity.BOTTOM)
86+
.title(layoutInformation.title.toString())
87+
.message(layoutInformation.description.toString())
88+
.backgroundColorRes(R.color.colorPrimaryDark)
89+
.positiveActionText(R.string.btn_cta_preview_layout_xml)
90+
.negativeActionText(R.string.btn_cta_okay)
91+
.positiveActionTextColorRes(R.color.colorAccent)
92+
.negativeActionTextColorRes(R.color.colorAccent)
93+
.positiveActionTapListener(object : Flashbar.OnActionTapListener {
94+
override fun onActionTapped(bar: Flashbar) {
95+
Timber.d("Loading the XML for ")
96+
bar.dismiss()
97+
}
98+
})
99+
.negativeActionTapListener(object : Flashbar.OnActionTapListener {
100+
override fun onActionTapped(bar: Flashbar) {
101+
Timber.d("Closing dialog.")
102+
bar.dismiss()
103+
}
104+
})
105+
.build()
106+
}
107+
108+
if (flashbar?.isShowing() == false) {
109+
flashbar?.show()
110+
}
86111
}
112+
113+
114+
//
115+
// Setup menu item on the action bar.
116+
//
117+
override fun onCreateOptionsMenu(menu: Menu): Boolean {
118+
val inflater = menuInflater
119+
inflater.inflate(R.menu.menu_layout_positioning, menu)
120+
return true
121+
}
122+
123+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
124+
return when (item.itemId) {
125+
R.id.show_layout_info_menu_item -> {
126+
showLayoutInfo(layoutInformation)
127+
true
128+
}
129+
else -> super.onOptionsItemSelected(item)
130+
}
131+
}
132+
87133
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!--
2+
~ Copyright (c) 2018 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+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
android:width="24dp"
19+
android:height="24dp"
20+
android:viewportWidth="24.0"
21+
android:viewportHeight="24.0">
22+
<path
23+
android:fillColor="@color/md_grey_300"
24+
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" />
25+
</vector>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (c) 2018 Hossain Khan
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<menu xmlns:android="http://schemas.android.com/apk/res/android"
19+
xmlns:app="http://schemas.android.com/apk/res-auto"
20+
xmlns:tools="http://schemas.android.com/tools">
21+
22+
<!-- Allows to use custom font -->
23+
<item
24+
android:id="@+id/show_layout_info_menu_item"
25+
android:title="@string/btn_cta_information"
26+
android:icon="@drawable/ic_info_outline_black_24dp"
27+
android:visible="true"
28+
android:iconTint="@color/md_amber_300"
29+
app:showAsAction="always"
30+
tools:targetApi="o" />
31+
</menu>

app/src/main/res/values/colors.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
<color name="colorPrimaryDark">#303F9F</color>
55
<color name="colorAccent">#FF4081</color>
66

7-
<color name="grid_tint">#77aaaaaa</color>
7+
<color name="grid_tint">#44aaaaaa</color>
88
</resources>
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
<resources>
2-
<string name="app_name">ConstraintLayout-CheatSheet</string>
2+
<string name="app_name">ConstraintLayout Demo</string>
3+
<string name="btn_cta_okay">OK</string>
4+
<string name="btn_cta_information">Info</string>
5+
<string name="btn_cta_preview_layout_xml">Preview XML</string>
36
</resources>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2018 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 android.content.res.Resources
20+
import org.junit.Assert.assertEquals
21+
import org.junit.Assert.assertFalse
22+
import org.junit.Assert.assertNull
23+
import org.junit.Before
24+
import org.junit.Test
25+
import org.mockito.Mockito
26+
import org.mockito.Mockito.anyInt
27+
import org.mockito.Mockito.`when`
28+
29+
class LayoutDataStoreTest {
30+
31+
private lateinit var sut: LayoutDataStore
32+
private val resources: Resources = Mockito.mock(Resources::class.java)
33+
34+
@Before
35+
fun setUp() {
36+
sut = LayoutDataStore(resources)
37+
}
38+
39+
@Test
40+
fun getLayoutsInfos_hasLayoutData() {
41+
assertFalse("Should contain at least 1 item",
42+
sut.layoutsInfos.isEmpty())
43+
}
44+
45+
/**
46+
* Tests conversion from layout file to URL to preview on the web.
47+
*
48+
* Input:
49+
* com.hossainkhan.android.demo:layout/activity_positioning_top_left
50+
*
51+
* Output:
52+
* https://github.com/amardeshbd/android-constraint-layout-cheatsheet/blob/master/app/src/main/res/layout/activity_positioning_top_left.xml
53+
*/
54+
@Test
55+
fun getLayoutUrl_givenValidLayout_providesUrlToLayoutResource() {
56+
`when`(resources.getResourceName(anyInt()))
57+
.thenReturn("com.hossainkhan.android.demo:layout/activity_positioning_top_left")
58+
59+
60+
assertEquals("https://github.com/amardeshbd/android-constraint-layout-cheatsheet/blob/master/app/src/main/res/layout/activity_positioning_top_left.xml",
61+
sut.getLayoutUrl(1))
62+
}
63+
64+
@Test(expected = IllegalStateException::class)
65+
fun getLayoutUrl_givenInvalidLayout_providesNull() {
66+
`when`(resources.getResourceName(anyInt()))
67+
.thenReturn("some-random-resource")
68+
69+
sut.getLayoutUrl(1)
70+
}
71+
}

0 commit comments

Comments
 (0)