├── .idea
├── .name
├── compiler.xml
├── gradle.xml
└── misc.xml
├── README.md
├── app
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── rafsan
│ │ └── dynamicui_fromjson
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── rafsan
│ │ │ └── dynamicui_fromjson
│ │ │ ├── GenerateFormActivity.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── data
│ │ │ └── CollectData.kt
│ │ │ ├── model
│ │ │ ├── FormComponent.kt
│ │ │ ├── FormComponentItem.kt
│ │ │ ├── FormViewComponent.kt
│ │ │ ├── Value.kt
│ │ │ └── WidgetItems.kt
│ │ │ └── utils
│ │ │ ├── Utils.kt
│ │ │ └── dialog
│ │ │ ├── DialogListener.kt
│ │ │ └── ShowDialog.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── edit_text_background.xml
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_generate_form.xml
│ │ ├── activity_main.xml
│ │ ├── dialog_layout.xml
│ │ ├── form_buttons_layout.xml
│ │ └── form_spinner_row.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── raw
│ │ └── sample_json.json
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── rafsan
│ └── dynamicui_fromjson
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── data_collect.png
├── form_preview.png
├── formbuilder_web.png
└── json_view.png
└── settings.gradle
/.idea/.name:
--------------------------------------------------------------------------------
1 | DynamicUI-FromJson
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dynamic UI from JSON
2 |
3 | ## Functionality
4 | The app's functionality includes:
5 | 1. The app generates Dynamic UI from JSON. The app support json, generated from https://formbuilder.online/
6 |
7 | 
8 |
9 | 2. If the json is valid, the app will generate a preview of the form.
10 |
11 | 3. Supported widgets: `HEADER, TEXTVIEW, TEXTAREA, SELECT/SPINNER, RADIO-GROUP, PARAGRAPH, DATE, CHECKBOX-GROUP, NUMBER-FIELD, SWITCH-FIELD`.
12 |
13 | 4. User can submit or reset the form. After submit the data collected will be displayed using `JSONArray` properties.
14 |
15 | ## Building
16 |
17 | You can open the project in Android studio and press run.
18 | Android Studio version used to build the project: Arctic fox 2020.3.1
19 |
20 | Gradle plugin used in the project will require `Java 11.0` to run.
21 |
22 | you can set the gradle jdk in `Preferences->Build Tools->Gradle->Gradle JDK`
23 |
24 | ## Screenshots
25 |
26 | Json for the Form | Form generation | Data collect after submit
27 | :------------------------:|:------------------------:|:------------------------
28 |  |  | 
29 |
30 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdk 31
8 |
9 | defaultConfig {
10 | applicationId "com.rafsan.dynamicui_fromjson"
11 | minSdk 21
12 | targetSdk 31
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | buildFeatures {
27 | viewBinding = true
28 | }
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 | kotlinOptions {
35 | jvmTarget = '1.8'
36 | }
37 | }
38 |
39 | dependencies {
40 |
41 | implementation 'androidx.core:core-ktx:1.7.0'
42 | implementation 'androidx.appcompat:appcompat:1.4.0'
43 | implementation 'com.google.android.material:material:1.4.0'
44 | implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
45 | implementation 'com.google.code.gson:gson:2.8.9'
46 |
47 | testImplementation 'junit:junit:4.+'
48 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
49 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
50 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/rafsan/dynamicui_fromjson/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.rafsan.dynamicui_fromjson", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/GenerateFormActivity.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.DatePickerDialog
5 | import android.graphics.Color
6 | import android.graphics.Typeface
7 | import android.os.Build
8 | import android.os.Bundle
9 | import android.text.*
10 | import android.text.InputFilter.LengthFilter
11 | import android.text.style.ForegroundColorSpan
12 | import android.text.style.RelativeSizeSpan
13 | import android.util.Log
14 | import android.util.TypedValue
15 | import android.view.*
16 | import android.widget.*
17 | import androidx.appcompat.app.AppCompatActivity
18 | import androidx.appcompat.widget.AppCompatCheckBox
19 | import androidx.appcompat.widget.AppCompatRadioButton
20 | import androidx.appcompat.widget.LinearLayoutCompat
21 | import androidx.appcompat.widget.SwitchCompat
22 | import androidx.core.app.NavUtils
23 | import androidx.core.content.ContextCompat
24 | import com.google.gson.Gson
25 | import com.google.gson.JsonArray
26 | import com.google.gson.JsonObject
27 | import com.rafsan.dynamicui_fromjson.data.CollectData.Companion.getDataFromCheckBoxGroup
28 | import com.rafsan.dynamicui_fromjson.data.CollectData.Companion.getDataFromDateTextView
29 | import com.rafsan.dynamicui_fromjson.data.CollectData.Companion.getDataFromEditText
30 | import com.rafsan.dynamicui_fromjson.data.CollectData.Companion.getDataFromRadioGroup
31 | import com.rafsan.dynamicui_fromjson.data.CollectData.Companion.getDataFromSpinner
32 | import com.rafsan.dynamicui_fromjson.data.CollectData.Companion.printProperties
33 | import com.rafsan.dynamicui_fromjson.databinding.ActivityGenerateFormBinding
34 | import com.rafsan.dynamicui_fromjson.databinding.FormButtonsLayoutBinding
35 | import com.rafsan.dynamicui_fromjson.model.*
36 | import com.rafsan.dynamicui_fromjson.utils.Utils
37 | import com.rafsan.dynamicui_fromjson.utils.Utils.Companion.getCurrentDate
38 | import com.rafsan.dynamicui_fromjson.utils.Utils.Companion.getCustomColorStateList
39 | import com.rafsan.dynamicui_fromjson.utils.Utils.Companion.getDateFromString
40 | import com.rafsan.dynamicui_fromjson.utils.Utils.Companion.getDateStringToShow
41 | import com.rafsan.dynamicui_fromjson.utils.Utils.Companion.method
42 | import com.rafsan.dynamicui_fromjson.utils.Utils.Companion.setMerginToviews
43 | import com.rafsan.dynamicui_fromjson.utils.Utils.Companion.setSwitchColor
44 | import com.rafsan.dynamicui_fromjson.utils.dialog.ShowDialog
45 | import java.util.*
46 |
47 | class GenerateFormActivity : AppCompatActivity() {
48 |
49 | lateinit var binding: ActivityGenerateFormBinding
50 | var formViewCollection: ArrayList = arrayListOf()
51 |
52 | var submitRootJsonObj: JsonObject? = null
53 |
54 | companion object {
55 | var submitPropertyArrayJson: JsonArray? = null
56 | }
57 |
58 | var formComponent: FormComponent? = null
59 | val textColor = Color.parseColor("#000000")
60 |
61 | override fun onCreate(savedInstanceState: Bundle?) {
62 | super.onCreate(savedInstanceState)
63 | binding = ActivityGenerateFormBinding.inflate(layoutInflater)
64 | setContentView(binding.root)
65 |
66 | val json = intent.getStringExtra("value")
67 | submitRootJsonObj = JsonObject()
68 | submitPropertyArrayJson = JsonArray()
69 | json?.let {
70 | populateForm(it)
71 | }
72 | }
73 |
74 | private fun populateForm(json: String) {
75 | formComponent = Gson().fromJson(json, FormComponent::class.java)
76 | var viewId = 1
77 | binding.miniAppFormContainer.visibility = View.VISIBLE
78 |
79 | //TODO:- GENERATE FORM LAYOUT
80 | formComponent?.let {
81 | it.forEach { component ->
82 | when (component.type) {
83 | WidgetItems.HEADER.label -> binding.miniAppFormContainer.addView(
84 | createHeaderView(
85 | component
86 | )
87 | )
88 | WidgetItems.TEXT.label -> createEditableTextWithLabel(component, viewId++)
89 | WidgetItems.TEXTAREA.label -> createEditableTextWithLabel(component, viewId++)
90 | WidgetItems.SELECT.label -> createSpinner(component, viewId++)
91 | WidgetItems.RADIO_GROUP.label -> createRadioGroup(component, viewId++)
92 | WidgetItems.PARAGRAPH.label -> createParagraph(component)
93 | WidgetItems.DATE.label -> createDatePicker(component)
94 | WidgetItems.CHECKBOX_GROUP.label -> createCheckBoxGroup(component, viewId++)
95 | WidgetItems.NUMBER.label -> createNumberEditText(component)
96 | }
97 | }
98 | }
99 | addSubmitButtonLayout()
100 | }
101 |
102 | private fun createHeaderView(componentItem: FormComponentItem): TextView {
103 | val txtHeader = TextView(this)
104 | when (componentItem.subtype) {
105 | "h1" -> txtHeader.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20f)
106 | "h2" -> txtHeader.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18f)
107 | "h3" -> txtHeader.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
108 | }
109 | componentItem.label?.let {
110 | txtHeader.text = Utils.fromHtml(it)
111 | }
112 |
113 | txtHeader.layoutParams = LinearLayoutCompat.LayoutParams(
114 | ViewGroup.LayoutParams.MATCH_PARENT,
115 | ViewGroup.LayoutParams.WRAP_CONTENT
116 | )
117 | txtHeader.setTextColor(textColor)
118 | txtHeader.setPadding(0, 15, 0, 15)
119 | txtHeader.gravity = Gravity.CENTER
120 | return txtHeader
121 | }
122 |
123 | private fun createEditableTextWithLabel(component: FormComponentItem, viewId: Int) {
124 | isLabelNull(component)
125 | val editText = EditText(this)
126 | var rows = 1
127 |
128 | setMerginToviews(
129 | editText, 20,
130 | LinearLayoutCompat.LayoutParams.MATCH_PARENT,
131 | LinearLayout.LayoutParams.WRAP_CONTENT
132 | )
133 | if (component.type.equals(WidgetItems.TEXTAREA.label)) editText.gravity = Gravity.NO_GRAVITY
134 |
135 | editText.setPadding(20, 30, 20, 30)
136 | editText.setBackgroundResource(R.drawable.edit_text_background)
137 | editText.id = viewId
138 | isValueNull(component, editText)
139 | isSubTypeNull(component, editText)
140 | isPlaceHolderNull(component, editText)
141 | component.maxlength?.let {
142 | editText.filters = arrayOf(LengthFilter(it.toInt()))
143 | }
144 |
145 | component.rows?.let {
146 | rows = it.toInt()
147 | val finalRow = rows
148 | editText.setOnKeyListener { v, keyCode, event ->
149 | (v as EditText).lineCount > finalRow
150 | }
151 | editText.addTextChangedListener(object : TextWatcher {
152 | override fun beforeTextChanged(
153 | s: CharSequence, start: Int, count: Int, after: Int
154 | ) {
155 | }
156 |
157 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
158 | override fun afterTextChanged(s: Editable) {
159 | if (editText.lineCount > finalRow) {
160 | editText.setText(method(editText.text.toString()))
161 | editText.setSelection(editText.text.toString().length)
162 | }
163 | }
164 | })
165 | }
166 | editText.setLines(rows)
167 | binding.miniAppFormContainer.addView(editText)
168 | formViewCollection.add(FormViewComponent(editText, component))
169 | Log.i("EditTextInputType", editText.inputType.toString() + "")
170 | }
171 |
172 | private fun createSpinner(component: FormComponentItem, viewId: Int) {
173 | var selectedIndex = 0
174 | createLabelForViews(component)
175 | val layoutParams: LinearLayout.LayoutParams = LinearLayout.LayoutParams(
176 | LinearLayout.LayoutParams.MATCH_PARENT,
177 | LinearLayout.LayoutParams.WRAP_CONTENT
178 | )
179 | layoutParams.setMargins(40, 30, 40, 40)
180 | val spinner = Spinner(this)
181 | spinner.id = viewId
182 | spinner.setBackgroundColor(Color.WHITE)
183 | spinner.setBackgroundResource(R.drawable.edit_text_background)
184 | spinner.layoutParams = layoutParams
185 | //Spinner data source
186 | val spinnerDatasource = mutableListOf()
187 | component.values?.let {
188 | for (j in it.indices) {
189 | val value = it[j]
190 | spinnerDatasource.add(value.label)
191 | value.selected?.let { selected ->
192 | if (selected)
193 | selectedIndex = j
194 | }
195 | }
196 |
197 | val spinnerAdapter: ArrayAdapter<*> = ArrayAdapter(
198 | applicationContext,
199 | R.layout.form_spinner_row, spinnerDatasource
200 | )
201 | spinner.adapter = spinnerAdapter
202 | spinner.setSelection(selectedIndex)
203 | }
204 | binding.miniAppFormContainer.addView(spinner)
205 | formViewCollection.add(FormViewComponent(spinner, component))
206 | }
207 |
208 | @SuppressLint("RestrictedApi")
209 | private fun createRadioGroup(component: FormComponentItem, viewId: Int) {
210 | createLabelForViews(component)
211 | var selectedItem = 0
212 | var isRadioButtonSelected = false
213 |
214 | val radioGroup = RadioGroup(this)
215 | radioGroup.id = viewId
216 | Log.i("RadioGroupId", radioGroup.id.toString() + " " + radioGroup.tag)
217 | radioGroup.orientation = LinearLayout.VERTICAL
218 | setMerginToviews(radioGroup)
219 |
220 | component.values?.let {
221 | for (i in it.indices) {
222 | val value = it[i]
223 | val radioButton = AppCompatRadioButton(this)
224 | radioButton.text = value.label
225 | radioButton.supportButtonTintList = getCustomColorStateList(this)
226 |
227 | value.selected?.let { selected ->
228 | if (selected) {
229 | selectedItem = i
230 | isRadioButtonSelected = true
231 | }
232 | }
233 | radioGroup.addView(radioButton)
234 | }
235 | if (component.toggle != null && component.toggle) {
236 | val radioGroupContainer = RelativeLayout(this)
237 | var valueModels: MutableList = mutableListOf()
238 | component.values.let { options ->
239 | valueModels = options as MutableList
240 | }
241 | val valueModel = Value("Other", null, null)
242 | valueModels.add(valueModel)
243 | val radioButton = AppCompatRadioButton(this)
244 | radioButton.text = valueModel.label
245 | radioButton.supportButtonTintList = getCustomColorStateList(this)
246 | radioGroup.addView(radioButton)
247 | radioGroupContainer.addView(radioGroup)
248 | val otherText = EditText(this)
249 | otherText.setBackgroundResource(R.drawable.edit_text_background)
250 | otherText.isEnabled = false
251 | val otherTextParam = RelativeLayout.LayoutParams(
252 | RelativeLayout.LayoutParams.MATCH_PARENT,
253 | RelativeLayout.LayoutParams.WRAP_CONTENT
254 | )
255 | otherTextParam.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
256 | otherTextParam.addRule(RelativeLayout.RIGHT_OF, radioGroup.id)
257 | //otherTextParam.setMargins(10, 0, 40, 0);
258 | otherText.layoutParams = otherTextParam
259 | otherText.setPadding(10, 8, 10, 8)
260 | otherText.maxLines = 1
261 | radioGroupContainer.addView(otherText)
262 | otherText.addTextChangedListener(object : TextWatcher {
263 | override fun beforeTextChanged(
264 | s: CharSequence, start: Int, count: Int, after: Int
265 | ) {
266 | }
267 |
268 | override fun onTextChanged(
269 | s: CharSequence, start: Int, before: Int, count: Int
270 | ) {
271 | }
272 |
273 | override fun afterTextChanged(s: Editable) {
274 | if (otherText.text.toString() != "") {
275 | valueModel.value = otherText.text.toString()
276 | }
277 | }
278 | })
279 | radioButton.setOnCheckedChangeListener { buttonView, isChecked ->
280 | otherText.isEnabled = isChecked
281 | }
282 | binding.miniAppFormContainer.addView(radioGroupContainer)
283 | formViewCollection.add(FormViewComponent(radioGroup, component))
284 | } else {
285 | binding.miniAppFormContainer.addView(radioGroup)
286 | formViewCollection.add(FormViewComponent(radioGroup, component))
287 | }
288 | if (isRadioButtonSelected) (radioGroup.getChildAt(selectedItem) as RadioButton).isChecked =
289 | true
290 | }
291 | }
292 |
293 | @SuppressLint("SetTextI18n")
294 | private fun createParagraph(component: FormComponentItem) {
295 | val textView = TextView(this)
296 |
297 | val paragraphText: String =
298 | component.label?.substring(0, component.label.length - 2) ?: ""
299 |
300 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
301 | if (component.subtype.equals("blockquote")) {
302 | textView.text =
303 | "\"" + Html.fromHtml(paragraphText, Html.FROM_HTML_MODE_LEGACY) + "\""
304 | textView.setTypeface(null, Typeface.ITALIC)
305 | } else textView.text = Html.fromHtml(paragraphText, Html.FROM_HTML_MODE_LEGACY)
306 | } else {
307 | if (component.subtype.equals("blockquote")) {
308 | textView.text = "\"" + Html.fromHtml(paragraphText + "\"")
309 | textView.setTypeface(null, Typeface.ITALIC)
310 | } else textView.text = Html.fromHtml(paragraphText)
311 | }
312 |
313 | textView.setTextColor(textColor)
314 | setMerginToviews(textView)
315 | textView.setPadding(0, 10, 0, 10)
316 | binding.miniAppFormContainer.addView(textView)
317 | }
318 |
319 | private fun createDatePicker(component: FormComponentItem) {
320 | val relativeLayout = RelativeLayout(this)
321 | relativeLayout.setPadding(5, 10, 5, 10)
322 | val layoutParams = LinearLayout.LayoutParams(
323 | RelativeLayout.LayoutParams.MATCH_PARENT,
324 | RelativeLayout.LayoutParams.WRAP_CONTENT
325 | )
326 | layoutParams.setMargins(40, 40, 40, 40)
327 | relativeLayout.layoutParams = layoutParams
328 |
329 | component.label?.let { labelString ->
330 | val textView = TextView(this)
331 | textView.setTextColor(Color.BLACK)
332 | textView.setTypeface(null, Typeface.BOLD)
333 | component.required?.let { required ->
334 | if (required) {
335 | textView.text = labelStringForRequiredField(labelString)
336 | } else {
337 | textView.text = createStringForViewLabel(false, labelString)
338 | }
339 | }
340 | val layoutParams1 = RelativeLayout.LayoutParams(
341 | RelativeLayout.LayoutParams.WRAP_CONTENT,
342 | RelativeLayout.LayoutParams.WRAP_CONTENT
343 | )
344 | layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
345 | layoutParams1.addRule(RelativeLayout.CENTER_VERTICAL)
346 | relativeLayout.addView(textView)
347 | }
348 |
349 | val txtDate = TextView(this)
350 | val layoutParams1 = RelativeLayout.LayoutParams(
351 | RelativeLayout.LayoutParams.WRAP_CONTENT,
352 | RelativeLayout.LayoutParams.WRAP_CONTENT
353 | )
354 | layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
355 | layoutParams1.addRule(RelativeLayout.CENTER_VERTICAL)
356 | txtDate.layoutParams = layoutParams1
357 | relativeLayout.addView(txtDate)
358 |
359 | txtDate.text = getDateStringToShow(getCurrentDate(), "MMMM d, yyyy")
360 |
361 | relativeLayout.setOnClickListener {
362 | val calendar: Calendar = GregorianCalendar()
363 | val dateString = getDateFromString(txtDate.text.toString(), "MMMM d, yyyy")
364 | dateString?.let {
365 | calendar.time = it
366 | val datePickerDialog = DatePickerDialog(
367 | this@GenerateFormActivity,
368 | { view, year, month, dayOfMonth ->
369 | val selectedDate = Calendar.getInstance()
370 | selectedDate[year, month] = dayOfMonth
371 | txtDate.text = getDateStringToShow(selectedDate.time, "MMMM d, yyyy")
372 | }, calendar[Calendar.YEAR], calendar[Calendar.MONTH],
373 | calendar[Calendar.DAY_OF_MONTH]
374 | )
375 | datePickerDialog.show()
376 | }
377 | }
378 | binding.miniAppFormContainer.addView(relativeLayout)
379 | formViewCollection.add(FormViewComponent(txtDate, component))
380 | }
381 |
382 | private fun createCheckBoxGroup(component: FormComponentItem, viewId: Int) {
383 | isLabelNull(component)
384 | if (component.toggle != null && component.toggle) {
385 | createToggleCheckBoxGroup(component, viewId)
386 | } else createCheckBoxGroupUtil(component, viewId)
387 | }
388 |
389 | private fun createNumberEditText(component: FormComponentItem) {
390 | var minValue = 0
391 | var maxValue = 0L
392 | var step = 1
393 | component.min?.let {
394 | minValue = it
395 | }
396 | component.max?.let {
397 | maxValue = it
398 | }
399 | component.step?.let {
400 | step = it
401 | }
402 | val finalStep = step
403 | val finalMinValue = minValue
404 | val finalMaxValue = maxValue
405 |
406 | isLabelNull(component)
407 | val numberViewContainer = LinearLayout(this)
408 | val layoutParams = LinearLayout.LayoutParams(
409 | LinearLayout.LayoutParams.MATCH_PARENT,
410 | LinearLayout.LayoutParams.WRAP_CONTENT
411 | )
412 | numberViewContainer.setPadding(40, 10, 10, 10)
413 | numberViewContainer.layoutParams = layoutParams
414 |
415 | val editTextParam = LinearLayout.LayoutParams(
416 | 0,
417 | LinearLayout.LayoutParams.WRAP_CONTENT
418 | )
419 | editTextParam.weight = 5f
420 | editTextParam.setMargins(0, 0, 20, 0)
421 | val editText = EditText(this)
422 | editText.layoutParams = editTextParam
423 | editText.setPadding(10, 10, 10, 10)
424 | editText.inputType = InputType.TYPE_CLASS_NUMBER
425 | editText.setBackgroundResource(R.drawable.edit_text_background)
426 | component.placeholder?.let {
427 | editText.hint = it
428 | }
429 |
430 | editText.addTextChangedListener(object : TextWatcher {
431 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
432 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
433 | if (s.length > 1) {
434 | if (finalMaxValue != 0L) {
435 | if (s.toString().toLong() > finalMaxValue) {
436 | editText.setText(finalMaxValue.toString())
437 | }
438 | }
439 | if (finalMinValue != 0) {
440 | if (s.toString().toLong() < finalMinValue) {
441 | editText.setText(finalMinValue.toString())
442 | }
443 | }
444 | }
445 | }
446 |
447 | override fun afterTextChanged(s: Editable) {
448 | }
449 | })
450 |
451 | isValueNull(component, editText)
452 | editText.setText(minValue.toString())
453 | isSubTypeNull(component, editText)
454 | isPlaceHolderNull(component, editText)
455 |
456 | component.maxlength?.let {
457 | editText.filters = arrayOf(LengthFilter(it.toInt()))
458 | }
459 |
460 | val textParams = LinearLayout.LayoutParams(
461 | 0,
462 | LinearLayout.LayoutParams.MATCH_PARENT
463 | )
464 | textParams.weight = 1f
465 | textParams.setMargins(10, 0, 5, 0)
466 | val negativeButton = TextView(this)
467 | negativeButton.isAllCaps = false
468 | negativeButton.text = "-"
469 | negativeButton.gravity = Gravity.CENTER
470 | negativeButton.setTextColor(textColor)
471 | negativeButton.layoutParams = textParams
472 | negativeButton.setBackgroundColor(ContextCompat.getColor(this, R.color.teal_500))
473 |
474 | val positiveButton = TextView(this)
475 | positiveButton.isAllCaps = false
476 | positiveButton.text = "+"
477 | positiveButton.gravity = Gravity.CENTER
478 | positiveButton.setTextColor(textColor)
479 | positiveButton.layoutParams = textParams
480 | positiveButton.setBackgroundColor(ContextCompat.getColor(this, R.color.teal_500))
481 |
482 | //ClickListener for negative button
483 | negativeButton.setOnClickListener { v: View? ->
484 | var editTextNumber = 0
485 | if (editText.text.toString() != "") {
486 | editTextNumber = editText.text.toString().toInt()
487 | }
488 | Log.i("NumberFiledValue", editTextNumber.toString())
489 | if (finalStep == 0) {
490 | if (editTextNumber == finalMinValue) {
491 | editText.setText(finalMaxValue.toString())
492 | } else {
493 | editTextNumber--
494 | editText.setText(editTextNumber.toString())
495 | }
496 | } else {
497 | if (editTextNumber == finalMinValue || editTextNumber - finalStep < finalMinValue) {
498 | editText.setText(finalMaxValue.toString())
499 | } else {
500 | editTextNumber -= finalStep
501 | editText.setText(editTextNumber.toString())
502 | }
503 | }
504 | }
505 |
506 | //ClickListener for positive button
507 | positiveButton.setOnClickListener { v: View? ->
508 | var editTextNumber = 0L
509 | if (editText.text.toString() != "") {
510 | editTextNumber = editText.text.toString().toLong()
511 | }
512 | Log.i("NumberFieldValue", editTextNumber.toString())
513 | if (finalStep == 0) {
514 | if (editTextNumber == finalMaxValue) {
515 | editText.setText(finalMinValue.toString())
516 | } else {
517 | editTextNumber++
518 | editText.setText(editTextNumber.toString())
519 | }
520 | } else {
521 | if (editTextNumber == finalMaxValue || editTextNumber + finalStep > finalMaxValue) {
522 | editText.setText(finalMinValue.toString())
523 | } else {
524 | editTextNumber += finalStep
525 | editText.setText(editTextNumber.toString())
526 | }
527 | }
528 | }
529 |
530 | numberViewContainer.addView(editText)
531 | numberViewContainer.addView(negativeButton)
532 | numberViewContainer.addView(positiveButton)
533 | binding.miniAppFormContainer.addView(numberViewContainer)
534 | formViewCollection.add(FormViewComponent(editText, component))
535 | }
536 |
537 | @SuppressLint("RestrictedApi")
538 | private fun createCheckBoxGroupUtil(component: FormComponentItem, id: Int) {
539 | val checkBoxContainer = LinearLayout(this)
540 | checkBoxContainer.id = id
541 | checkBoxContainer.orientation = LinearLayout.VERTICAL
542 | val layoutParams = LinearLayout.LayoutParams(
543 | LinearLayout.LayoutParams.WRAP_CONTENT,
544 | LinearLayout.LayoutParams.WRAP_CONTENT
545 | )
546 | checkBoxContainer.layoutParams = layoutParams
547 | component.values?.let {
548 | for (i in it.indices) {
549 | val valueModel = it[i]
550 | val checkBox = AppCompatCheckBox(this)
551 | checkBox.text = valueModel.label
552 | val layoutParams1 = LinearLayout.LayoutParams(
553 | LinearLayout.LayoutParams.MATCH_PARENT,
554 | LinearLayout.LayoutParams.WRAP_CONTENT
555 | )
556 | layoutParams1.setMargins(70, 20, 20, 0)
557 | checkBox.layoutParams = layoutParams1
558 | valueModel.selected?.let { selected ->
559 | if (selected) {
560 | checkBox.isChecked = true
561 | }
562 | }
563 | checkBox.supportButtonTintList = getCustomColorStateList(this)
564 | checkBoxContainer.addView(checkBox)
565 | }
566 | if (component.toggle != null && component.toggle) {
567 | val rootContainer = RelativeLayout(this)
568 | var valueModels: MutableList = mutableListOf()
569 | component.values.let { options ->
570 | valueModels = options as MutableList
571 | }
572 | val valueModel = Value("Other", null, null)
573 | valueModels.add(valueModel)
574 | val checkBox = AppCompatCheckBox(this)
575 | checkBox.text = valueModel.label
576 | setMerginToviews(
577 | checkBox, 20,
578 | LinearLayout.LayoutParams.MATCH_PARENT,
579 | LinearLayout.LayoutParams.WRAP_CONTENT
580 | )
581 | valueModel.selected?.let { selected ->
582 | if (selected) {
583 | checkBox.isChecked = true
584 | }
585 | }
586 |
587 | checkBox.supportButtonTintList = getCustomColorStateList(this)
588 | checkBoxContainer.addView(checkBox)
589 | rootContainer.addView(checkBoxContainer)
590 | val otherText = EditText(this)
591 | otherText.setBackgroundResource(R.drawable.edit_text_background)
592 | otherText.isEnabled = false
593 | val otherTextParam = RelativeLayout.LayoutParams(
594 | RelativeLayout.LayoutParams.MATCH_PARENT,
595 | RelativeLayout.LayoutParams.WRAP_CONTENT
596 | )
597 | otherTextParam.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
598 | otherTextParam.addRule(RelativeLayout.RIGHT_OF, checkBoxContainer.id)
599 | otherTextParam.setMargins(10, 0, 40, 0)
600 | otherText.layoutParams = otherTextParam
601 | otherText.setPadding(10, 8, 10, 8)
602 | otherText.maxLines = 1
603 | rootContainer.addView(otherText)
604 | otherText.addTextChangedListener(object : TextWatcher {
605 | override fun beforeTextChanged(
606 | s: CharSequence, start: Int, count: Int, after: Int
607 | ) {
608 | }
609 |
610 | override fun onTextChanged(
611 | s: CharSequence, start: Int, before: Int, count: Int
612 | ) {
613 | }
614 |
615 | override fun afterTextChanged(s: Editable) {
616 | if (otherText.text.toString() != "") {
617 | valueModel.value = otherText.text.toString()
618 | }
619 | }
620 | })
621 | checkBox.setOnCheckedChangeListener { buttonView, isChecked ->
622 | otherText.isEnabled = isChecked
623 | }
624 | binding.miniAppFormContainer.addView(rootContainer)
625 | formViewCollection.add(FormViewComponent(checkBoxContainer, component))
626 | } else {
627 | binding.miniAppFormContainer.addView(checkBoxContainer)
628 | formViewCollection.add(FormViewComponent(checkBoxContainer, component))
629 | }
630 | }
631 | }
632 |
633 | /**
634 | * Checkbox group with Switch
635 | */
636 | private fun createToggleCheckBoxGroup(viewComponentModel: FormComponentItem, id: Int) {
637 | val switchContainer = LinearLayout(this)
638 | switchContainer.id = id
639 | switchContainer.orientation = LinearLayout.VERTICAL
640 | val layoutParams = LinearLayout.LayoutParams(
641 | LinearLayout.LayoutParams.WRAP_CONTENT,
642 | LinearLayout.LayoutParams.WRAP_CONTENT
643 | )
644 | switchContainer.layoutParams = layoutParams
645 | viewComponentModel.values?.let {
646 | for (i in it.indices) {
647 | val valueModel = it[i]
648 | val mySwitch = SwitchCompat(this)
649 | mySwitch.text = valueModel.label
650 | val layoutParams1 = LinearLayout.LayoutParams(
651 | LinearLayout.LayoutParams.MATCH_PARENT,
652 | LinearLayout.LayoutParams.WRAP_CONTENT
653 | )
654 | layoutParams1.setMargins(80, 20, 20, 0)
655 | mySwitch.layoutParams = layoutParams1
656 | valueModel.selected?.let { selected ->
657 | if (selected) {
658 | mySwitch.isChecked = true
659 | }
660 | }
661 | setSwitchColor(mySwitch, this)
662 | switchContainer.addView(mySwitch)
663 | }
664 | if (viewComponentModel.toggle != null && viewComponentModel.toggle) {
665 | val rootContainer = RelativeLayout(this)
666 | val valueModels: MutableList = mutableListOf()
667 | val valueModel = Value("Other", null, null)
668 | valueModels.add(valueModel)
669 | val mySwitch = SwitchCompat(this)
670 | mySwitch.text = valueModel.label
671 | val layoutParams1 = LinearLayout.LayoutParams(
672 | LinearLayout.LayoutParams.MATCH_PARENT,
673 | LinearLayout.LayoutParams.WRAP_CONTENT
674 | )
675 | layoutParams1.setMargins(80, 20, 20, 0)
676 | mySwitch.layoutParams = layoutParams1
677 | valueModel.selected?.let { selected ->
678 | if (selected) {
679 | mySwitch.isChecked = true
680 | }
681 | }
682 | setSwitchColor(mySwitch, this)
683 | switchContainer.addView(mySwitch)
684 | rootContainer.addView(switchContainer)
685 | val otherText = EditText(this)
686 | otherText.setBackgroundResource(R.drawable.edit_text_background)
687 | otherText.isEnabled = false
688 | val otherTextParam = RelativeLayout.LayoutParams(
689 | RelativeLayout.LayoutParams.MATCH_PARENT,
690 | RelativeLayout.LayoutParams.WRAP_CONTENT
691 | )
692 | otherTextParam.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
693 | otherTextParam.addRule(RelativeLayout.RIGHT_OF, switchContainer.id)
694 | otherTextParam.setMargins(10, 0, 40, 0)
695 | otherText.layoutParams = otherTextParam
696 | otherText.setPadding(10, 8, 10, 8)
697 | otherText.maxLines = 1
698 | rootContainer.addView(otherText)
699 | otherText.addTextChangedListener(object : TextWatcher {
700 | override fun beforeTextChanged(
701 | s: CharSequence, start: Int, count: Int, after: Int
702 | ) {
703 | }
704 |
705 | override fun onTextChanged(
706 | s: CharSequence, start: Int, before: Int, count: Int
707 | ) {
708 | }
709 |
710 | override fun afterTextChanged(s: Editable) {
711 | if (otherText.text.toString() != "") {
712 | valueModel.value = otherText.text.toString()
713 | }
714 | }
715 | })
716 | mySwitch.setOnCheckedChangeListener { buttonView, isChecked ->
717 | otherText.isEnabled = isChecked
718 | }
719 | binding.miniAppFormContainer.addView(rootContainer)
720 | formViewCollection.add(FormViewComponent(switchContainer, viewComponentModel))
721 | } else {
722 | binding.miniAppFormContainer.addView(switchContainer)
723 | formViewCollection.add(FormViewComponent(switchContainer, viewComponentModel))
724 | }
725 | }
726 | }
727 |
728 | private fun isLabelNull(viewComponentModel: FormComponentItem) {
729 | if (viewComponentModel.label != null) createLabelForViews(viewComponentModel)
730 | }
731 |
732 | private fun isSubTypeNull(viewComponentModel: FormComponentItem, editText: EditText) {
733 | if (viewComponentModel.subtype != null) {
734 | setInputTypeForEditText(editText, viewComponentModel)
735 | }
736 | }
737 |
738 | private fun isPlaceHolderNull(
739 | viewComponentModel: FormComponentItem,
740 | editText: EditText
741 | ) {
742 | if (viewComponentModel.placeholder != null) editText.hint = viewComponentModel.placeholder
743 | }
744 |
745 | private fun isValueNull(viewComponentModel: FormComponentItem, view: TextView) {
746 | viewComponentModel.value?.let {
747 | view.text = it
748 | }
749 | }
750 |
751 | private fun createLabelForViews(viewComponentModel: FormComponentItem) {
752 | val label = TextView(this)
753 | label.setTextColor(Color.BLACK)
754 | label.setTypeface(null, Typeface.BOLD)
755 | setMerginToviews(
756 | label,
757 | 40,
758 | LinearLayoutCompat.LayoutParams.WRAP_CONTENT,
759 | LinearLayoutCompat.LayoutParams.WRAP_CONTENT
760 | )
761 | viewComponentModel.label?.let { labelText ->
762 | viewComponentModel.required?.let {
763 | if (it) {
764 | label.text = createStringForViewLabel(it, labelText)
765 | } else {
766 | label.text = createStringForViewLabel(false, labelText)
767 | }
768 | }
769 | binding.miniAppFormContainer.addView(label)
770 | }
771 | }
772 |
773 | /**
774 | * EditText Input type selection
775 | */
776 | private fun setInputTypeForEditText(
777 | editText: EditText,
778 | viewComponentModel: FormComponentItem
779 | ) {
780 | when (viewComponentModel.subtype) {
781 | "password" -> editText.inputType = InputType.TYPE_TEXT_VARIATION_PASSWORD
782 | "email" -> editText.inputType = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
783 | "tel" -> editText.inputType = InputType.TYPE_CLASS_PHONE
784 | "dateTimeLocal" -> editText.inputType = InputType.TYPE_CLASS_DATETIME
785 | else -> editText.inputType = InputType.TYPE_CLASS_TEXT
786 | }
787 | }
788 |
789 | private fun createStringForViewLabel(
790 | required: Boolean,
791 | label: String
792 | ): SpannableStringBuilder {
793 | val labelStr = Utils.fromHtml(label)
794 | return if (required) {
795 | labelStringForRequiredField(labelStr)
796 | } else {
797 | val username = SpannableString(labelStr)
798 | val description = SpannableStringBuilder()
799 | username.setSpan(
800 | RelativeSizeSpan(1.1f), 0, username.length,
801 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
802 | )
803 | username.setSpan(
804 | ForegroundColorSpan(textColor), 0, username.length,
805 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
806 | )
807 | description.append(username)
808 | description
809 | }
810 | }
811 |
812 | private fun labelStringForRequiredField(label: String): SpannableStringBuilder {
813 | val username = SpannableString(label)
814 | val description = SpannableStringBuilder()
815 | username.setSpan(
816 | RelativeSizeSpan(1.1f), 0, username.length,
817 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
818 | )
819 | username.setSpan(
820 | ForegroundColorSpan(textColor), 0, username.length,
821 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
822 | )
823 | description.append(username)
824 | val commentSpannable = SpannableString(" *")
825 | commentSpannable.setSpan(
826 | ForegroundColorSpan(Color.RED), 0,
827 | commentSpannable.length,
828 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
829 | )
830 | commentSpannable.setSpan(
831 | RelativeSizeSpan(1.0f), 0,
832 | commentSpannable.length,
833 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
834 | )
835 | description.append(commentSpannable)
836 | return description
837 | }
838 |
839 | private fun addSubmitButtonLayout() {
840 | val layoutInflater = LayoutInflater.from(this)
841 | val buttonViewBinding: FormButtonsLayoutBinding =
842 | FormButtonsLayoutBinding.inflate(layoutInflater)
843 | binding.miniAppFormContainer.addView(buttonViewBinding.root)
844 | buttonViewBinding.btnReset.setOnClickListener {
845 | startActivity(intent)
846 | finish()
847 | }
848 |
849 | buttonViewBinding.btnSubmit.setOnClickListener {
850 | for (formViewComponent in formViewCollection) {
851 | val view: View = formViewComponent.createdView
852 | val viewComponentModel: FormComponentItem =
853 | formViewComponent.getViewComponentModel()
854 | when (viewComponentModel.type) {
855 | WidgetItems.TEXT.label ->
856 | if (!getDataFromEditText(view, viewComponentModel)) {
857 | viewComponentModel.label?.let { labelStr ->
858 | showRequiredDialog(labelStr)
859 | }
860 | }
861 | WidgetItems.TEXTAREA.label ->
862 | if (!getDataFromEditText(view, viewComponentModel)) {
863 | viewComponentModel.label?.let { labelStr ->
864 | showRequiredDialog(labelStr)
865 | }
866 | }
867 | WidgetItems.SELECT.label ->
868 | if (!getDataFromSpinner(view, viewComponentModel)) {
869 | viewComponentModel.label?.let { labelStr ->
870 | showRequiredDialog(labelStr)
871 | }
872 | }
873 | WidgetItems.RADIO_GROUP.label ->
874 | if (!getDataFromRadioGroup(view, viewComponentModel)) {
875 | viewComponentModel.label?.let { labelStr ->
876 | showRequiredDialog(labelStr)
877 | }
878 | }
879 | WidgetItems.DATE.label -> getDataFromDateTextView(view, viewComponentModel)
880 | WidgetItems.CHECKBOX_GROUP.label ->
881 | if (!getDataFromCheckBoxGroup(view, viewComponentModel)) {
882 | viewComponentModel.label?.let { labelStr ->
883 | showRequiredDialog(labelStr)
884 | }
885 | }
886 | WidgetItems.NUMBER.label -> {
887 | if (!getDataFromEditText(view, viewComponentModel)) {
888 | viewComponentModel.label?.let { labelStr ->
889 | showRequiredDialog(labelStr)
890 | }
891 | }
892 | }
893 | }
894 | }
895 |
896 | submitRootJsonObj?.add("properties", submitPropertyArrayJson)
897 | Log.i("JsonArray", submitRootJsonObj.toString())
898 | submitPropertyArrayJson?.let {
899 | val collectedData = printProperties(it)
900 | ShowDialog.customDialog(this, "Collected Data", collectedData, null)
901 | submitPropertyArrayJson = JsonArray()
902 | }
903 | }
904 | }
905 |
906 | private fun showRequiredDialog(labelStr: String) {
907 | ShowDialog.customDialog(
908 | this,
909 | "Required",
910 | labelStr, null
911 | )
912 | }
913 |
914 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
915 | // Respond to the action bar's Up/Home button
916 | if (item.itemId == R.id.home) {
917 | NavUtils.navigateUpFromSameTask(this)
918 | return true
919 | }
920 | return super.onOptionsItemSelected(item)
921 | }
922 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.google.android.material.snackbar.Snackbar
7 | import com.google.gson.Gson
8 | import com.rafsan.dynamicui_fromjson.databinding.ActivityMainBinding
9 |
10 | class MainActivity : AppCompatActivity() {
11 |
12 | lateinit var binding: ActivityMainBinding
13 |
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | binding = ActivityMainBinding.inflate(layoutInflater)
17 | setContentView(binding.root)
18 | val json = readRawJson()
19 |
20 | binding.buttonView.setOnClickListener {
21 | //Generate form
22 | if (json.isNotEmpty()) {
23 | val intent = Intent(this, GenerateFormActivity::class.java)
24 | intent.putExtra("value", json)
25 | startActivity(intent)
26 | }
27 | }
28 | }
29 |
30 | fun readRawJson(): String {
31 | val text = resources.openRawResource(R.raw.sample_json)
32 | .bufferedReader().use { it.readText() }
33 | val check = checkValidity(text)
34 | if (check) {
35 | binding.etContent.setText(text)
36 | } else {
37 | Snackbar.make(binding.content, "Invalid JSON", Snackbar.LENGTH_SHORT).show()
38 | return ""
39 | }
40 | return text
41 | }
42 |
43 | fun checkValidity(string: String): Boolean {
44 | val gson = Gson()
45 | try {
46 | gson.fromJson(string, Object::class.java)
47 | } catch (e: Exception) {
48 | return false
49 | }
50 | return true
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/data/CollectData.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson.data
2 |
3 | import android.util.Log
4 | import android.view.View
5 | import android.widget.*
6 | import androidx.appcompat.widget.SwitchCompat
7 | import com.google.gson.JsonArray
8 | import com.google.gson.JsonObject
9 | import com.rafsan.dynamicui_fromjson.GenerateFormActivity.Companion.submitPropertyArrayJson
10 | import com.rafsan.dynamicui_fromjson.model.FormComponentItem
11 | import com.rafsan.dynamicui_fromjson.model.Value
12 | import com.rafsan.dynamicui_fromjson.utils.Utils.Companion.isValidEmailAddress
13 | import com.rafsan.dynamicui_fromjson.utils.Utils.Companion.isValidTelephoneNumber
14 | import java.util.*
15 |
16 | class CollectData {
17 | companion object {
18 | /**
19 | * Get selected Date from date TextView.
20 | *
21 | * @param view
22 | * @param FormComponentItem
23 | */
24 | fun getDataFromDateTextView(
25 | view: View,
26 | viewComponentModel: FormComponentItem
27 | ) {
28 | val dateView = view as TextView
29 | val json = populateSubmitPropertyJson(viewComponentModel, null)
30 | json.addProperty("value", dateView.text.toString())
31 | submitPropertyArrayJson?.add(json)
32 | }
33 |
34 | /**
35 | * @param view
36 | * @param FormComponentItem
37 | * @return
38 | */
39 | fun getDataFromCheckBoxGroup(
40 | view: View,
41 | viewComponentModel: FormComponentItem
42 | ): Boolean {
43 | return if (viewComponentModel.toggle != null && viewComponentModel.toggle) {
44 | getDataFromSwitchContainer(view, viewComponentModel)
45 | } else getDataFromCheckBoxContainer(view, viewComponentModel)
46 | }
47 |
48 | /**
49 | * @param view
50 | * @param FormComponentItem
51 | * @return
52 | */
53 | fun getDataFromCheckBoxContainer(
54 | view: View,
55 | viewComponentModel: FormComponentItem
56 | ): Boolean {
57 | val checkBoxContainer = view as LinearLayout
58 | val submitJsonValues = JsonArray()
59 | var valueModel: Value?
60 | var isChecked = false
61 | return if (viewComponentModel.required != null && viewComponentModel.required) {
62 | for (i in 0 until checkBoxContainer.childCount) {
63 | val submitJsonValue = JsonObject()
64 | val checkBox = checkBoxContainer.getChildAt(i) as CheckBox
65 | valueModel = viewComponentModel.values?.get(i)
66 | if (checkBox.isChecked) {
67 | submitJsonValue.addProperty("label", checkBox.text.toString())
68 | //submitJsonValue.addProperty("value", valueModel.getValue());
69 | if (valueModel != null) {
70 | if (valueModel.label == "Other") {
71 | submitJsonValue.addProperty("value", valueModel.value)
72 | } else submitJsonValue.addProperty("value", checkBox.text.toString())
73 | }
74 | submitJsonValues.add(submitJsonValue)
75 | isChecked = true
76 | }
77 | }
78 | val json = populateSubmitPropertyJson(viewComponentModel, null)
79 | json.add("value", submitJsonValues)
80 | submitPropertyArrayJson?.add(json)
81 | return isChecked
82 | } else {
83 | for (i in 0 until checkBoxContainer.childCount) {
84 | val submitJsonValue = JsonObject()
85 | val checkBox = checkBoxContainer.getChildAt(i) as CheckBox
86 | valueModel = viewComponentModel.values?.get(i)
87 | if (checkBox.isChecked) {
88 | submitJsonValue.addProperty("label", checkBox.text.toString())
89 | //submitJsonValue.addProperty("value", valueModel.getValue());
90 | if (valueModel != null) {
91 | if (valueModel.label.equals("Other")) {
92 | submitJsonValue.addProperty("value", valueModel.value)
93 | } else submitJsonValue.addProperty("value", checkBox.text.toString())
94 | }
95 | submitJsonValues.add(submitJsonValue)
96 | }
97 | }
98 | val json = populateSubmitPropertyJson(viewComponentModel, null)
99 | json.add("value", submitJsonValues)
100 | submitPropertyArrayJson?.add(json)
101 | true
102 | }
103 | }
104 |
105 | /**
106 | * @param view
107 | * @param FormComponentItem
108 | * @return
109 | */
110 | fun getDataFromSwitchContainer(
111 | view: View,
112 | viewComponentModel: FormComponentItem
113 | ): Boolean {
114 | val checkBoxContainer = view as LinearLayout
115 | val submitJsonValues = JsonArray()
116 | var valueModel: Value?
117 | var isChecked = false
118 | return if (viewComponentModel.required != null && viewComponentModel.required) {
119 | for (i in 0 until (viewComponentModel.values?.size ?: 0)) {
120 | val submitJsonValue = JsonObject()
121 | val aSwitch = checkBoxContainer.getChildAt(i) as SwitchCompat
122 | valueModel = viewComponentModel.values?.get(i)
123 | if (aSwitch.isChecked) {
124 | submitJsonValue.addProperty("label", aSwitch.text.toString())
125 | //submitJsonValue.addProperty("value", valueModel.getValue());
126 | if (valueModel != null) {
127 | if (valueModel.label == "Other") {
128 | submitJsonValue.addProperty("value", valueModel.value)
129 | } else submitJsonValue.addProperty("value", aSwitch.text.toString())
130 | }
131 | submitJsonValues.add(submitJsonValue)
132 | isChecked = true
133 | }
134 | }
135 | val json = populateSubmitPropertyJson(viewComponentModel, null)
136 | json.add("value", submitJsonValues)
137 | submitPropertyArrayJson?.add(json)
138 | return isChecked
139 | } else {
140 | for (i in 0 until (viewComponentModel.values?.size ?: 0)) {
141 | val submitJsonValue = JsonObject()
142 | val aSwitch = checkBoxContainer.getChildAt(i) as SwitchCompat
143 | valueModel = viewComponentModel.values?.get(i)
144 | if (aSwitch.isChecked) {
145 | submitJsonValue.addProperty("label", aSwitch.text.toString())
146 | //submitJsonValue.addProperty("value", valueModel.getValue());
147 | if (valueModel != null) {
148 | if (valueModel.label == "Other") {
149 | submitJsonValue.addProperty("value", valueModel.value)
150 | } else submitJsonValue.addProperty("value", aSwitch.text.toString())
151 | }
152 | submitJsonValues.add(submitJsonValue)
153 | }
154 | }
155 | val json = populateSubmitPropertyJson(viewComponentModel, null)
156 | json.add("value", submitJsonValues)
157 | submitPropertyArrayJson?.add(json)
158 | true
159 | }
160 | }
161 |
162 | /**
163 | * @param view
164 | * @param FormComponentItem
165 | * @return Boolean value to check required field fill up or not
166 | */
167 | fun getDataFromEditText(
168 | view: View,
169 | viewComponentModel: FormComponentItem
170 | ): Boolean {
171 | val editText = view as EditText
172 | if (editText.text.toString() != "") {
173 | viewComponentModel.required?.let {
174 | //Not null
175 | if (it) {
176 | if (editText.text.toString() == "") {
177 | return false
178 | } else {
179 | viewComponentModel.subtype?.let { subType ->
180 | if (subType == "tel") {
181 | if (!isValidTelephoneNumber(editText.text.toString())) {
182 | return false
183 | }
184 | } else if (subType == "email") {
185 | if (!isValidEmailAddress(editText.text.toString())) {
186 | return false
187 | }
188 | }
189 | }
190 | }
191 | }
192 | }
193 | val json = populateSubmitPropertyJson(viewComponentModel, null)
194 | json.addProperty("value", editText.text.toString())
195 | submitPropertyArrayJson?.add(json)
196 | }
197 | return true
198 | }
199 |
200 | /**
201 | * @param view
202 | * @param FormComponentItem
203 | * @return
204 | */
205 | fun getDataFromRadioGroup(
206 | view: View,
207 | viewComponentModel: FormComponentItem
208 | ): Boolean {
209 | val radioGroup = view as RadioGroup
210 | val valuesModels: ArrayList = viewComponentModel.values as ArrayList
211 | val valueModel: Value
212 | val radioButton: RadioButton?
213 | val radioButtonIndex: Int
214 | return if (viewComponentModel.required != null && viewComponentModel.required) {
215 | if (radioGroup.checkedRadioButtonId != -1) {
216 | radioButton =
217 | radioGroup.findViewById(radioGroup.checkedRadioButtonId) as RadioButton
218 | radioButtonIndex = radioGroup.indexOfChild(radioButton)
219 | valueModel = valuesModels[radioButtonIndex]
220 | val json = populateSubmitPropertyJson(viewComponentModel, valueModel)
221 | submitPropertyArrayJson?.add(json)
222 | Log.i("selectedRadioButton", valueModel.label)
223 | true
224 | } else {
225 | false
226 | }
227 | } else {
228 | if (radioGroup.checkedRadioButtonId != -1) {
229 | radioButton =
230 | radioGroup.findViewById(radioGroup.checkedRadioButtonId) as? RadioButton
231 | radioButton?.let {
232 | radioButtonIndex = radioGroup.indexOfChild(it)
233 | valueModel = valuesModels[radioButtonIndex]
234 | val json = populateSubmitPropertyJson(viewComponentModel, valueModel)
235 | submitPropertyArrayJson?.add(json)
236 | Log.i("selectedRadioButton", valueModel.label)
237 | }
238 | }
239 | true
240 | }
241 | }
242 |
243 | /**
244 | * @param view
245 | * @param FormComponentItem
246 | * @return
247 | */
248 | fun getDataFromSpinner(
249 | view: View,
250 | viewComponentModel: FormComponentItem
251 | ): Boolean {
252 | val spinner = view as Spinner
253 |
254 | if (viewComponentModel.required != null && viewComponentModel.required) {
255 | return if (spinner.selectedItem != null) {
256 | val json = populateSubmitPropertyJson(viewComponentModel, null)
257 | val selectedValues = populateSpinnerOptions(viewComponentModel, spinner)
258 | json.add("value", selectedValues)
259 | submitPropertyArrayJson?.add(json)
260 | Log.i("SpinnerSelectedItem", spinner.selectedItem.toString())
261 | true
262 | } else {
263 | false
264 | }
265 | } else {
266 | if (spinner.selectedItem != null) {
267 | val json = populateSubmitPropertyJson(viewComponentModel, null)
268 | val selectedValues = populateSpinnerOptions(viewComponentModel, spinner)
269 | json.add("value", selectedValues)
270 | submitPropertyArrayJson?.add(json)
271 | Log.i("SpinnerSelectedItem", spinner.selectedItem.toString())
272 | return true
273 | }
274 | }
275 |
276 | return true
277 | }
278 |
279 | fun populateSpinnerOptions(componentItem: FormComponentItem, spinner: Spinner): JsonArray {
280 | val selectedValue = JsonObject()
281 | val selectedValues = JsonArray()
282 | selectedValue.addProperty("label", spinner.selectedItem.toString())
283 | if (spinner.selectedItem.toString() == "Other") {
284 | selectedValue.addProperty(
285 | "value",
286 | componentItem.values
287 | ?.get(spinner.selectedItemPosition)?.value
288 | )
289 | } else {
290 | selectedValue.addProperty(
291 | "value",
292 | spinner.selectedItem.toString()
293 | )
294 | }
295 | selectedValues.add(selectedValue)
296 | return selectedValues
297 | }
298 |
299 | fun populateSubmitPropertyJson(
300 | componentItem: FormComponentItem,
301 | valueModel: Value?
302 | ): JsonObject {
303 | val submitPropertiesValueObj = JsonObject()
304 | submitPropertiesValueObj.addProperty("label", componentItem.label)
305 | valueModel?.let {
306 | if (it.label == "Other") {
307 | submitPropertiesValueObj.addProperty("value", it.value)
308 | } else submitPropertiesValueObj.addProperty("value", it.label)
309 | }
310 | submitPropertiesValueObj.addProperty("type", componentItem.type)
311 | submitPropertiesValueObj.addProperty("subtype", componentItem.subtype)
312 | return submitPropertiesValueObj
313 | }
314 |
315 | fun printProperties(array: JsonArray): String {
316 | val builder: StringBuilder = StringBuilder()
317 | for (i in 0 until array.size()) {
318 | val propObj = array[i].asJsonObject
319 | val label = propObj.get("label")
320 | val value = propObj.get("value")
321 | builder.append(label).append(" : ").append(value)
322 | builder.append("\n")
323 | }
324 | return builder.toString()
325 | }
326 | }
327 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/model/FormComponent.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson.model
2 |
3 | class FormComponent : ArrayList()
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/model/FormComponentItem.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson.model
2 |
3 | data class FormComponentItem(
4 | val access: Boolean,
5 | val className: String,
6 | val `inline`: Boolean,
7 | val label: String?,
8 | val max: Long?,
9 | val min: Int?,
10 | val step: Int?,
11 | val multiple: Boolean?,
12 | val name: String?,
13 | val value: String?,
14 | val maxlength: String?,
15 | val rows: String?,
16 | val other: Boolean?,
17 | val placeholder: String?,
18 | val required: Boolean?,
19 | val style: String?,
20 | val subtype: String?,
21 | val toggle: Boolean?,
22 | val type: String?,
23 | val values: List?
24 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/model/FormViewComponent.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson.model
2 |
3 | import android.view.View
4 |
5 | class FormViewComponent(
6 | val createdView: View,
7 | viewComponentModel: FormComponentItem
8 | ) {
9 | private val viewComponentModel: FormComponentItem = viewComponentModel
10 | fun getViewComponentModel(): FormComponentItem {
11 | return viewComponentModel
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/model/Value.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson.model
2 |
3 | data class Value(
4 | val label: String,
5 | val selected: Boolean?,
6 | var value: String?
7 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/model/WidgetItems.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson.model
2 |
3 | enum class WidgetItems(val label: String) {
4 | HEADER("header"),
5 | TEXT("text"),
6 | TEXTAREA("textarea"),
7 | SELECT("select"),
8 | RADIO_GROUP("radio-group"),
9 | PARAGRAPH("paragraph"),
10 | DATE("date"),
11 | CHECKBOX_GROUP("checkbox-group"),
12 | NUMBER("number")
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson.utils
2 |
3 | import android.content.Context
4 | import android.content.res.ColorStateList
5 | import android.graphics.Color
6 | import android.os.Build
7 | import android.text.Html
8 | import android.util.Log
9 | import android.view.View
10 | import android.widget.LinearLayout
11 | import androidx.appcompat.widget.SwitchCompat
12 | import androidx.core.content.ContextCompat
13 | import androidx.core.graphics.drawable.DrawableCompat
14 | import com.rafsan.dynamicui_fromjson.R
15 | import java.text.ParseException
16 | import java.text.SimpleDateFormat
17 | import java.util.*
18 | import java.util.regex.Pattern
19 |
20 | class Utils {
21 | companion object {
22 | fun fromHtml(str: String): String {
23 | return if (Build.VERSION.SDK_INT >= 24) Html.fromHtml(str, Html.FROM_HTML_MODE_LEGACY)
24 | .toString() else Html.fromHtml(str).toString()
25 | }
26 |
27 | fun setMerginToviews(view: View, topMergin: Int, width: Int, height: Int) {
28 | val layoutParams = LinearLayout.LayoutParams(width, height)
29 | layoutParams.setMargins(40, topMergin, 40, 0)
30 | view.layoutParams = layoutParams
31 | }
32 |
33 | fun setMerginToviews(view: View) {
34 | val layoutParams = LinearLayout.LayoutParams(
35 | LinearLayout.LayoutParams.WRAP_CONTENT,
36 | LinearLayout.LayoutParams.WRAP_CONTENT
37 | )
38 | layoutParams.setMargins(40, 20, 40, 0)
39 | view.layoutParams = layoutParams
40 | }
41 |
42 | fun method(str: String?): String? {
43 | var str = str
44 | if (str != null && str.isNotEmpty()) {
45 | str = str.substring(0, str.length - 1)
46 | }
47 | return str
48 | }
49 |
50 | fun getCustomColorStateList(context: Context): ColorStateList {
51 | return ColorStateList(
52 | arrayOf(
53 | intArrayOf(android.R.attr.state_checked),
54 | intArrayOf(-android.R.attr.state_checked)
55 | ),
56 | intArrayOf(
57 | ContextCompat.getColor(context, R.color.grey),//disabled
58 | ContextCompat.getColor(context, R.color.teal_500) //enabled
59 | )
60 | )
61 | }
62 |
63 | fun getCurrentDate(): Date {
64 | val calendar = Calendar.getInstance()
65 | return calendar.time
66 | }
67 |
68 | fun getDateStringToShow(date: Date, format: String): String {
69 | try {
70 | val simpleDateFormat = SimpleDateFormat(format, Locale.ENGLISH);
71 | return simpleDateFormat.format(date)
72 | } catch (e: Exception) {
73 | Log.d("Error", e.toString())
74 | return ""
75 | }
76 | }
77 |
78 | fun getDateFromString(date: String, format: String): Date? {
79 | Log.i("StringDate", date)
80 | val simpleDateFormat = SimpleDateFormat(format, Locale.ENGLISH)
81 | try {
82 | val formatedDate = simpleDateFormat.parse(date)
83 | Log.i("ParsedDate", formatedDate.toString())
84 | return formatedDate
85 | } catch (e: ParseException) {
86 | e.printStackTrace()
87 | }
88 | return null
89 | }
90 |
91 | fun setSwitchColor(v: SwitchCompat, context: Context) {
92 | // thumb color of your choice
93 | val thumbColor = ContextCompat.getColor(context, R.color.teal_500)
94 |
95 | // trackColor is the thumbColor with 30% transparency (77)
96 | val trackColor = Color.argb(
97 | 77, Color.red(thumbColor),
98 | Color.green(thumbColor),
99 | Color.blue(thumbColor)
100 | )
101 |
102 | // setting the thumb color
103 | DrawableCompat.setTintList(
104 | v.thumbDrawable, ColorStateList(
105 | arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()), intArrayOf(
106 | thumbColor,
107 | Color.WHITE
108 | )
109 | )
110 | )
111 |
112 | // setting the track color
113 | DrawableCompat.setTintList(
114 | v.trackDrawable, ColorStateList(
115 | arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()), intArrayOf(
116 | trackColor,
117 | Color.parseColor("#4D000000") // full black with 30% transparency (4D)
118 | )
119 | )
120 | )
121 | }
122 |
123 | fun isValidEmailAddress(emailAddress: String): Boolean {
124 | val emailPattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
125 | val p = Pattern.compile(emailPattern)
126 | val m = p.matcher(emailAddress)
127 | return m.matches()
128 | }
129 |
130 | fun isValidTelephoneNumber(telephoneNumber: String): Boolean {
131 | return telephoneNumber.length >= 10
132 | }
133 | }
134 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/utils/dialog/DialogListener.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson.utils.dialog
2 |
3 | interface DialogListener {
4 | fun OnSuccess(obj: Any?)
5 | fun OnError(obj: Any?)
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rafsan/dynamicui_fromjson/utils/dialog/ShowDialog.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson.utils.dialog
2 |
3 | import android.app.Activity
4 | import android.app.Dialog
5 | import android.content.Context
6 | import android.view.Gravity
7 | import android.view.LayoutInflater
8 | import android.view.Window
9 | import android.view.WindowManager
10 | import com.rafsan.dynamicui_fromjson.databinding.DialogLayoutBinding
11 |
12 | class ShowDialog {
13 | companion object {
14 | fun customDialog(
15 | context: Context,
16 | title: String,
17 | message: String,
18 | listener: DialogListener?
19 | ) {
20 | val dialog = Dialog(context)
21 | dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
22 | val inflater =
23 | context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
24 | val binding: DialogLayoutBinding = DialogLayoutBinding.inflate(inflater)
25 | binding.modalTitle.text = title
26 | binding.modalMsg.text = message
27 | binding.modalConfirm.setOnClickListener {
28 | dialog.dismiss()
29 | listener?.OnSuccess("")
30 | }
31 | (context as Activity).window.setSoftInputMode(
32 | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE or
33 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
34 | )
35 | dialog.setContentView(binding.root)
36 | val window = dialog.window
37 | window!!.setLayout(
38 | WindowManager.LayoutParams.MATCH_PARENT,
39 | WindowManager.LayoutParams.WRAP_CONTENT
40 | )
41 | window.setGravity(Gravity.CENTER)
42 | dialog.show()
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/edit_text_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_generate_form.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
21 |
22 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
19 |
20 |
24 |
25 |
32 |
33 |
41 |
42 |
45 |
46 |
57 |
58 |
69 |
70 |
71 |
76 |
77 |
85 |
86 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/form_buttons_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/form_spinner_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/raw/sample_json.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "header",
4 | "subtype": "h1",
5 | "label": "General Information form",
6 | "access": false
7 | },
8 | {
9 | "type": "paragraph",
10 | "subtype": "p",
11 | "label": "Please fill-up the form with correct information...",
12 | "access": false
13 | },
14 | {
15 | "type": "text",
16 | "required": false,
17 | "label": "Enter your name",
18 | "placeholder": "full name",
19 | "className": "form-control",
20 | "name": "text-1640082066779-0",
21 | "access": false,
22 | "subtype": "text",
23 | "maxlength": 60
24 | },
25 | {
26 | "type": "number",
27 | "required": false,
28 | "label": "Age",
29 | "className": "form-control",
30 | "name": "number-1640082094782-0",
31 | "access": false,
32 | "min": 10,
33 | "max": 100,
34 | "step": 1
35 | },
36 | {
37 | "type": "radio-group",
38 | "required": false,
39 | "label": "Gender",
40 | "inline": false,
41 | "name": "radio-group-1640082112533-0",
42 | "access": false,
43 | "other": false,
44 | "values": [
45 | {
46 | "label": "Male",
47 | "value": "option-1",
48 | "selected": false
49 | },
50 | {
51 | "label": "Female",
52 | "value": "option-2",
53 | "selected": false
54 | }
55 | ]
56 | },
57 | {
58 | "type": "select",
59 | "required": false,
60 | "label": "Country of origin",
61 | "className": "form-control",
62 | "name": "select-1640082150449-0",
63 | "access": false,
64 | "multiple": false,
65 | "values": [
66 | {
67 | "label": "Australia",
68 | "value": "option-1",
69 | "selected": true
70 | },
71 | {
72 | "label": "Bangladesh",
73 | "value": "option-2",
74 | "selected": false
75 | },
76 | {
77 | "label": "India",
78 | "value": "option-3",
79 | "selected": false
80 | },
81 | {
82 | "label": "United Kingdom",
83 | "value": "",
84 | "selected": false
85 | },
86 | {
87 | "label": "United State",
88 | "value": "",
89 | "selected": false
90 | },
91 | {
92 | "label": "Germany",
93 | "value": "",
94 | "selected": false
95 | }
96 | ]
97 | },
98 | {
99 | "type": "textarea",
100 | "required": false,
101 | "label": "Enter your address",
102 | "className": "form-control",
103 | "name": "textarea-1640082215264-0",
104 | "access": false,
105 | "subtype": "textarea"
106 | },
107 | {
108 | "type": "checkbox-group",
109 | "required": false,
110 | "label": "Choose Marital Status",
111 | "toggle": false,
112 | "inline": false,
113 | "name": "checkbox-group-1640082315632-0",
114 | "access": false,
115 | "other": false,
116 | "values": [
117 | {
118 | "label": "Married",
119 | "value": "option-1",
120 | "selected": true
121 | },
122 | {
123 | "label": "Unmarried",
124 | "value": "",
125 | "selected": false
126 | }
127 | ]
128 | },
129 | {
130 | "type": "date",
131 | "required": false,
132 | "label": "Date of submission",
133 | "className": "form-control",
134 | "name": "date-1640082355015-0",
135 | "access": false
136 | },
137 | {
138 | "type": "button",
139 | "label": "Submit form",
140 | "subtype": "button",
141 | "className": "btn-default btn",
142 | "name": "button-1640082377047-0",
143 | "access": false,
144 | "style": "default"
145 | },
146 | {
147 | "type": "number",
148 | "required": false,
149 | "label": "Phone Number",
150 | "placeholder": "Enter your phone number",
151 | "className": "form-control",
152 | "name": "number-1640159009641-0",
153 | "access": false,
154 | "min": 100,
155 | "max": 999999999999,
156 | "step": 1
157 | },
158 | {
159 | "type": "date",
160 | "required": false,
161 | "label": "Date of Birth",
162 | "className": "form-control",
163 | "name": "date-1640160922288-0",
164 | "access": false
165 | },
166 | {
167 | "type": "text",
168 | "required": false,
169 | "label": "Educational Institution",
170 | "placeholder": "Enter your school name",
171 | "className": "form-control",
172 | "name": "text-1640160946355-0",
173 | "access": false,
174 | "subtype": "text",
175 | "maxlength": 200
176 | },
177 | {
178 | "type": "paragraph",
179 | "subtype": "blockquote",
180 | "label": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
",
181 | "access": false
182 | },
183 | {
184 | "type": "textarea",
185 | "required": false,
186 | "label": "Add your hobbies",
187 | "className": "form-control",
188 | "name": "textarea-1640160857581-0",
189 | "access": false,
190 | "subtype": "textarea",
191 | "rows": 8
192 | },
193 | {
194 | "type": "checkbox-group",
195 | "required": false,
196 | "label": "Your Preferences",
197 | "toggle": true,
198 | "inline": false,
199 | "name": "checkbox-group-1640161076154-0",
200 | "access": false,
201 | "other": true,
202 | "values": [
203 | {
204 | "label": "Array",
205 | "value": "option-1",
206 | "selected": true
207 | },
208 | {
209 | "label": "LinkedList",
210 | "value": "2",
211 | "selected": false
212 | },
213 | {
214 | "label": "Tree",
215 | "value": "3",
216 | "selected": false
217 | },
218 | {
219 | "label": "Map",
220 | "value": "4",
221 | "selected": false
222 | },
223 | {
224 | "label": "Graph",
225 | "value": "5",
226 | "selected": false
227 | },
228 | {
229 | "label": "Tree",
230 | "value": "6",
231 | "selected": false
232 | },
233 | {
234 | "label": "ArrayList",
235 | "value": "7",
236 | "selected": false
237 | },
238 | {
239 | "label": "Hashing",
240 | "value": "8",
241 | "selected": false
242 | }
243 | ]
244 | },
245 | {
246 | "type": "radio-group",
247 | "required": false,
248 | "label": "Your choice of department",
249 | "inline": false,
250 | "name": "radio-group-1640160977822-0",
251 | "access": false,
252 | "other": false,
253 | "values": [
254 | {
255 | "label": "Engineering",
256 | "value": "option-1",
257 | "selected": false
258 | },
259 | {
260 | "label": "Marketting",
261 | "value": "option-2",
262 | "selected": false
263 | },
264 | {
265 | "label": "Management",
266 | "value": "option-3",
267 | "selected": false
268 | },
269 | {
270 | "label": "Finance",
271 | "value": "ew",
272 | "selected": false
273 | },
274 | {
275 | "label": "Technology",
276 | "value": "aa",
277 | "selected": false
278 | },
279 | {
280 | "label": "Arts & Literature",
281 | "value": "aw",
282 | "selected": false
283 | },
284 | {
285 | "label": "Business Management",
286 | "value": "rr",
287 | "selected": false
288 | },
289 | {
290 | "label": "Law",
291 | "value": "qq",
292 | "selected": false
293 | }
294 | ]
295 | }
296 | ]
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #0CAE9E
9 | #FF000000
10 | #FFFFFFFF
11 | #808080
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Form Builder
3 | Reset
4 | Submit
5 | Ok
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/test/java/com/rafsan/dynamicui_fromjson/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.rafsan.dynamicui_fromjson
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:7.0.3"
9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0"
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | task clean(type: Delete) {
17 | delete rootProject.buildDir
18 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Dec 02 01:26:16 BDT 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/images/data_collect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/images/data_collect.png
--------------------------------------------------------------------------------
/images/form_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/images/form_preview.png
--------------------------------------------------------------------------------
/images/formbuilder_web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/images/formbuilder_web.png
--------------------------------------------------------------------------------
/images/json_view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafsanahmad/Dynamic-UI-From-JSON/cc144f6a2c45fd0385016848c7e1c1b8980d8429/images/json_view.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | repositories {
4 | google()
5 | mavenCentral()
6 | jcenter() // Warning: this repository is going to shut down soon
7 | }
8 | }
9 | rootProject.name = "DynamicUI-FromJson"
10 | include ':app'
11 |
--------------------------------------------------------------------------------