• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

opensrp / opensrp-client-native-form / #63

21 Dec 2023 07:28AM UTC coverage: 66.274% (+0.09%) from 66.188%
#63

Pull #665

github

web-flow
Merge b24ded2a8 into f8c80cf2b
Pull Request #665: Removing unnecessary multi-threading

8440 of 12735 relevant lines covered (66.27%)

0.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.06
android-json-form-wizard/src/main/java/com/vijay/jsonwizard/activities/JsonFormActivity.java
1
package com.vijay.jsonwizard.activities;
2

3
import static android.view.inputmethod.InputMethodManager.HIDE_NOT_ALWAYS;
4
import static com.vijay.jsonwizard.utils.FormUtils.getCheckboxValueJsonArray;
5
import static com.vijay.jsonwizard.utils.FormUtils.getCurrentCheckboxValues;
6

7
import android.Manifest;
8
import android.annotation.SuppressLint;
9
import android.app.AlertDialog;
10
import android.content.BroadcastReceiver;
11
import android.content.Context;
12
import android.content.DialogInterface;
13
import android.content.Intent;
14
import android.content.IntentFilter;
15
import android.graphics.Color;
16
import android.graphics.drawable.ColorDrawable;
17
import android.graphics.drawable.Drawable;
18
import android.graphics.drawable.GradientDrawable;
19
import android.os.AsyncTask;
20
import android.os.Bundle;
21
import android.text.Html;
22
import android.text.Spanned;
23
import android.text.TextUtils;
24
import android.view.View;
25
import android.view.ViewGroup;
26
import android.view.inputmethod.InputMethodManager;
27
import android.widget.Button;
28
import android.widget.CheckBox;
29
import android.widget.EditText;
30
import android.widget.ImageView;
31
import android.widget.LinearLayout;
32
import android.widget.RadioButton;
33
import android.widget.RadioGroup;
34
import android.widget.RelativeLayout;
35
import android.widget.Spinner;
36
import android.widget.TextView;
37
import android.widget.Toast;
38

39
import androidx.annotation.NonNull;
40
import androidx.appcompat.widget.AppCompatRadioButton;
41
import androidx.core.app.ActivityCompat;
42
import androidx.core.util.Pair;
43
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
44

45
import com.google.gson.Gson;
46
import com.google.gson.reflect.TypeToken;
47
import com.rengwuxian.materialedittext.MaterialEditText;
48
import com.shashank.sony.fancydialoglib.Animation;
49
import com.shashank.sony.fancydialoglib.FancyAlertDialog;
50
import com.shashank.sony.fancydialoglib.Icon;
51
import com.vijay.jsonwizard.R;
52
import com.vijay.jsonwizard.comparisons.Comparison;
53
import com.vijay.jsonwizard.comparisons.EqualToComparison;
54
import com.vijay.jsonwizard.comparisons.GreaterThanComparison;
55
import com.vijay.jsonwizard.comparisons.GreaterThanEqualToComparison;
56
import com.vijay.jsonwizard.comparisons.LessThanComparison;
57
import com.vijay.jsonwizard.comparisons.LessThanEqualToComparison;
58
import com.vijay.jsonwizard.comparisons.NotEqualToComparison;
59
import com.vijay.jsonwizard.comparisons.RegexComparison;
60
import com.vijay.jsonwizard.constants.JsonFormConstants;
61
import com.vijay.jsonwizard.customviews.GenericPopupDialog;
62
import com.vijay.jsonwizard.customviews.MaterialSpinner;
63
import com.vijay.jsonwizard.customviews.TextableView;
64
import com.vijay.jsonwizard.domain.Form;
65
import com.vijay.jsonwizard.event.RefreshExpansionPanelEvent;
66
import com.vijay.jsonwizard.interfaces.CommonListener;
67
import com.vijay.jsonwizard.interfaces.GenericDialogInterface;
68
import com.vijay.jsonwizard.interfaces.JsonApi;
69
import com.vijay.jsonwizard.interfaces.LifeCycleListener;
70
import com.vijay.jsonwizard.interfaces.OnActivityRequestPermissionResultListener;
71
import com.vijay.jsonwizard.interfaces.OnActivityResultListener;
72
import com.vijay.jsonwizard.rules.RuleConstant;
73
import com.vijay.jsonwizard.utils.AppExecutors;
74
import com.vijay.jsonwizard.utils.ExObjectResult;
75
import com.vijay.jsonwizard.utils.FormUtils;
76
import com.vijay.jsonwizard.utils.NativeFormsProperties;
77
import com.vijay.jsonwizard.utils.PermissionUtils;
78
import com.vijay.jsonwizard.utils.PropertyManager;
79
import com.vijay.jsonwizard.utils.Utils;
80
import com.vijay.jsonwizard.views.CustomTextView;
81
import com.vijay.jsonwizard.widgets.CountDownTimerFactory;
82
import com.vijay.jsonwizard.widgets.NumberSelectorFactory;
83

84
import org.apache.commons.lang3.StringUtils;
85
import org.greenrobot.eventbus.EventBus;
86
import org.greenrobot.eventbus.Subscribe;
87
import org.greenrobot.eventbus.ThreadMode;
88
import org.jeasy.rules.api.Facts;
89
import org.json.JSONArray;
90
import org.json.JSONException;
91
import org.json.JSONObject;
92
import org.yaml.snakeyaml.Yaml;
93

94
import java.io.BufferedReader;
95
import java.util.ArrayList;
96
import java.util.Arrays;
97
import java.util.Collection;
98
import java.util.HashMap;
99
import java.util.HashSet;
100
import java.util.Iterator;
101
import java.util.List;
102
import java.util.Map;
103
import java.util.Set;
104
import java.util.concurrent.ConcurrentHashMap;
105
import java.util.concurrent.ConcurrentSkipListSet;
106
import java.util.regex.Matcher;
107
import java.util.regex.Pattern;
108

109
import timber.log.Timber;
110

111
public class JsonFormActivity extends JsonFormBaseActivity implements JsonApi {
1✔
112

113
    private final FormUtils formUtils = new FormUtils();
1✔
114
    private final Map<String, JSONObject> formFields = new ConcurrentHashMap<>();
1✔
115
    private final Set<String> popupFormFields = new ConcurrentSkipListSet<>();
1✔
116
    private final Map<String, List<String>> ruleKeys = new HashMap<>();
1✔
117
    private final Map<String, String> formValuesCacheMap = new HashMap<>();
1✔
118
    private final Utils utils = new Utils();
1✔
119
    private final HashMap<String, String[]> addressMap = new HashMap<>();
1✔
120
    private final Map<String, Set<String>> calculationDependencyMap = new HashMap<>();
1✔
121
    private final Map<String, Set<String>> skipLogicDependencyMap = new HashMap<>();
1✔
122
    private final Map<String, Boolean> stepSkipLogicPresenceMap = new ConcurrentHashMap<>();
1✔
123
    private final AppExecutors appExecutors = new AppExecutors();
1✔
124
    private final BroadcastReceiver messageReceiver = new BroadcastReceiver() {
1✔
125
        @Override
126
        public void onReceive(Context context, Intent intent) {
127
            String messageType = intent.getStringExtra(JsonFormConstants.INTENT_KEY.MESSAGE_TYPE);
×
128
            if (JsonFormConstants.MESSAGE_TYPE.GLOBAL_VALUES.equals(messageType)) {
×
129
                Map<String, String> map =
×
130
                        (Map<String, String>) intent.getSerializableExtra(JsonFormConstants.INTENT_KEY.MESSAGE);
×
131
                globalValues.putAll(map);
×
132
                String stepName = intent.getStringExtra(JsonFormConstants.STEPNAME);
×
133
                if (StringUtils.isNotBlank(stepName))
×
134
                    performActionOnReceived(stepName);
×
135
            }
136
        }
×
137
    };
138
    private Map<String, View> formDataViews = new ConcurrentHashMap<>();
1✔
139
    private String functionRegex;
140
    private HashMap<String, Comparison> comparisons;
141
    private GenericDialogInterface genericDialogInterface;
142
    private JSONArray extraFieldsWithValues;
143
    private TextView selectedTextView = null;
1✔
144
    private boolean isNextStepRelevant;
145
    private String nextStep = "";
1✔
146

147

148
    public void performActionOnReceived(String stepName) {
149
        try {
150
            invokeRefreshLogic(null, false, null, null, stepName, false);
×
151
        } catch (Exception e) {
×
152
            Timber.e(e);
×
153
        }
×
154
    }
×
155

156
    @Override
157
    public synchronized JSONObject getStep(final String name) {
158
        synchronized (getmJSONObject()) {
1✔
159
            try {
160
                return getmJSONObject().getJSONObject(name);
1✔
161
            } catch (JSONException e) {
×
162
                Timber.e(e);
×
163
            }
164
        }
×
165
        return null;
×
166
    }
167

168
    @Override
169
    public void writeValue(String stepName, String key, String value, String openMrsEntityParent, String openMrsEntity,
170
                           String openMrsEntityId, boolean popup) throws JSONException {
171
        if (invokeRefreshLogic(stepName, null, key, value)) {
1✔
172
            if (!popup) {
1✔
173
                cacheFormMapValues(stepName, null, key, value);
1✔
174
            }
175
            widgetsWriteValue(stepName, key, value, openMrsEntityParent, openMrsEntity, openMrsEntityId, popup);
1✔
176
        }
177
    }
1✔
178

179
    @Override
180
    public void writeValue(String stepName, String parentKey, String childObjectKey, String childKey, String value,
181
                           String openMrsEntityParent, String openMrsEntity, String openMrsEntityId, boolean popup)
182
            throws JSONException {
183
        if (invokeRefreshLogic(stepName, parentKey, childKey, value)) {
1✔
184
            if (!popup) {
1✔
185
                cacheFormMapValues(stepName, parentKey, childKey, value);
1✔
186
            }
187
            checkBoxWriteValue(stepName, parentKey, childObjectKey, childKey, value, popup);
1✔
188

189
        }
190
    }
1✔
191

192
    @Override
193
    public void writeValue(String stepName, String key, String value, String openMrsEntityParent, String openMrsEntity,
194
                           String openMrsEntityId) throws JSONException {
195
        if (invokeRefreshLogic(stepName, null, key, value)) {
×
196
            cacheFormMapValues(stepName, null, key, value);
×
197
            widgetsWriteValue(stepName, key, value, openMrsEntityParent, openMrsEntity, openMrsEntityId, false);
×
198
        }
199
    }
×
200

201
    @Override
202
    public void writeValue(String stepName, String parentKey, String childObjectKey, String childKey, String value,
203
                           String openMrsEntityParent, String openMrsEntity, String openMrsEntityId) throws JSONException {
204
        if (invokeRefreshLogic(stepName, parentKey, childKey, value)) {
×
205
            cacheFormMapValues(stepName, parentKey, childKey, value);
×
206
            checkBoxWriteValue(stepName, parentKey, childObjectKey, childKey, value, false);
×
207

208
        }
209
    }
×
210

211
    @Override
212
    public void writeMetaDataValue(String metaDataKey, Map<String, String> values) throws JSONException {
213
        synchronized (getmJSONObject()) {
×
214
            if (mJSONObject.has(FormUtils.METADATA_PROPERTY) && !values.isEmpty() &&
×
215
                    (mJSONObject.getJSONObject(FormUtils.METADATA_PROPERTY).has(metaDataKey))) {
×
216
                JSONObject metaData = mJSONObject.getJSONObject(FormUtils.METADATA_PROPERTY).getJSONObject(metaDataKey);
×
217
                for (Map.Entry<String, String> entry : values.entrySet()) {
×
218
                    String key = entry.getKey();
×
219
                    String value = entry.getValue();
×
220
                    if (value == null) value = "";
×
221
                    metaData.put(key, value);
×
222
                }
×
223
            }
224

225
        }
×
226
    }
×
227

228
    @Override
229
    public String currentJsonState() {
230
        synchronized (getmJSONObject()) {
1✔
231
            return getmJSONObject().toString();
1✔
232
        }
233
    }
234

235
    @Override
236
    public String getCount() {
237
        synchronized (getmJSONObject()) {
1✔
238
            return getmJSONObject().optString("count");
1✔
239
        }
240
    }
241

242
    @Override
243
    public void onFormFinish() {
244
        try {
245
            if (propertyManager == null) {
1✔
246
                propertyManager = new PropertyManager(this);
×
247
            }
248
            FormUtils.updateEndProperties(propertyManager, mJSONObject);
1✔
249
        } catch (Exception e) {
×
250
            Timber.e(e);
×
251
        }
1✔
252
    }
1✔
253

254
    @Override
255
    public void clearSkipLogicViews() {
256
        skipLogicViews.clear();
1✔
257
    }
1✔
258

259
    @Override
260
    public void clearCalculationLogicViews() {
261
        calculationLogicViews.clear();
1✔
262
    }
1✔
263

264
    @Override
265
    public void clearConstrainedViews() {
266
        constrainedViews = new ConcurrentHashMap<>();
1✔
267
    }
1✔
268

269
    @Override
270
    public void clearFormDataViews() {
271
        formDataViews = new ConcurrentHashMap<>();
1✔
272
        clearSkipLogicViews();
1✔
273
        clearConstrainedViews();
1✔
274
        clearCalculationLogicViews();
1✔
275
    }
1✔
276

277
    @Override
278
    public void addSkipLogicView(View view) {
279
        skipLogicViews.put((String) view.getTag(R.id.address), view);
1✔
280
    }
1✔
281

282
    @Override
283
    public void addCalculationLogicView(View view) {
284
        calculationLogicViews.put((String) view.getTag(R.id.address), view);
1✔
285
    }
1✔
286

287
    @Override
288
    public void addConstrainedView(View view) {
289
        constrainedViews.put(getViewKey(view), view);
1✔
290
    }
1✔
291

292
    @Override
293
    public void refreshHiddenViews(boolean popup) {
294
        for (View curView : getFormDataViews()) {
1✔
295
            String addressString = (String) curView.getTag(R.id.address);
1✔
296
            if (StringUtils.isNotBlank(addressString)) {
1✔
297
                String[] address = addressString.split(":");
1✔
298
                try {
299
                    JSONObject viewData = getObjectUsingAddress(address, popup);
1✔
300
                    if (viewData.has(JsonFormConstants.HIDDEN) && viewData.getBoolean(JsonFormConstants.HIDDEN)) {
1✔
301
                        toggleViewVisibility(curView, false, popup);
×
302
                    }
303
                } catch (JSONException e) {
×
304
                    Timber.e(e);
×
305
                }
1✔
306
            }
307
        }
1✔
308
    }
1✔
309

310
    @Override
311
    public void refreshSkipLogic(String parentKey, String childKey, boolean popup, String stepName, boolean isForNextStep) {
312
        initComparisons();
1✔
313
        Set<String> viewsIds = skipLogicDependencyMap.get(stepName + "_" + parentKey);
1✔
314
        if (parentKey == null || childKey == null) {
1✔
315
            for (View curView : skipLogicViews.values()) {
1✔
316
                if (isForNextStep && isNextStepRelevant()) {
×
317
                    break;
×
318
                }
319
                addRelevance(curView, popup, isForNextStep);
×
320
            }
1✔
321
        } else if (viewsIds == null) {
1✔
322
            for (String curViewId : skipLogicViews.keySet()) {
1✔
323
                //skip any relevance by rules engine since the these components are not affected either way.
324
                // Run relevance for native relevance functions as these are first and not optimized currently
325
                if (isForNextStep && isNextStepRelevant()) {
×
326
                    break;
×
327
                }
328
                if (!skipLogicDependencyMap.containsKey(curViewId)) {
×
329
                    addRelevance(skipLogicViews.get(curViewId), popup, isForNextStep);
×
330
                }
331
            }
1✔
332
        } else {
333
            for (String viewId : viewsIds) {
×
334
                if (isForNextStep && isNextStepRelevant()) {
×
335
                    break;
×
336
                }
337
                addRelevance(skipLogicViews.get(viewId), popup, isForNextStep);
×
338
            }
×
339
        }
340
    }
1✔
341

342
    public Pair<String[], JSONObject> getCalculationAddressAndValue(View view) throws JSONException {
343
        String calculationTag = (String) view.getTag(R.id.calculation);
1✔
344
        String widgetKey = (String) view.getTag(R.id.key);
1✔
345
        String stepName = ((String) view.getTag(R.id.address)).split(":")[0];
1✔
346
        if (calculationTag != null && calculationTag.length() > 0) {
1✔
347
            JSONObject calculation = new JSONObject(calculationTag);
1✔
348
            Iterator<String> keys = calculation.keys();
1✔
349

350
            while (keys.hasNext()) {
1✔
351
                String curKey = keys.next();
1✔
352

353
                JSONObject curCalculation = calculation.getJSONObject(curKey);
1✔
354
                JSONObject valueSource = new JSONObject();
1✔
355
                if (calculation.has(JsonFormConstants.SRC)) {
1✔
356
                    valueSource = calculation.getJSONObject(JsonFormConstants.SRC);
×
357
                }
358
                String[] address = getAddressFromMap(widgetKey, stepName, JsonFormConstants.CALCULATION);
1✔
359
                if (address == null && curCalculation.has(JsonFormConstants.JSON_FORM_KEY.EX_RULES)) {
1✔
360
                    JSONObject exRulesObject = curCalculation.getJSONObject(JsonFormConstants.JSON_FORM_KEY.EX_RULES);
×
361
                    if (exRulesObject.has(RuleConstant.RULES_DYNAMIC)) {
×
362
                        address = getDynamicRulesEngineAddress(curKey, curCalculation, view, JsonFormConstants.CALCULATION);
×
363
                    } else {
364
                        address = getRulesEngineAddress(curKey, curCalculation, view, JsonFormConstants.CALCULATION);
×
365
                    }
366
                }
367
                return new Pair<>(address, valueSource);
1✔
368
            }
369
        }
370
        return null;
×
371
    }
372

373
    @Override
374
    public void refreshCalculationLogic(String parentKey, String childKey, boolean popup, String stepName, boolean isForNextStep) {
375
        Set<String> viewsIds = calculationDependencyMap.get(stepName + "_" + parentKey);
1✔
376
        if (parentKey == null || viewsIds == null)
1✔
377
            viewsIds = calculationLogicViews.keySet();
1✔
378
        for (String viewId : viewsIds) {
1✔
379
            try {
380
                View curView = calculationLogicViews.get(viewId);
×
381
                if (curView == null) {
×
382
                    Timber.w("calculationLogicViews Missing %s", viewId);
×
383
                    continue;
×
384
                }
385
                Pair<String[], JSONObject> addressAndValue = getCalculationAddressAndValue(curView);
×
386
                if (addressAndValue != null && addressAndValue.first != null) {
×
387
                    String[] address = addressAndValue.first;
×
388
                    JSONObject valueSource = addressAndValue.second;
×
389
                    Facts curValueMap;
390
                    if (valueSource.length() > 0) {
×
391
                        curValueMap = getValueFromAddress(address, popup, valueSource);
×
392
                    } else {
393
                        curValueMap = getValueFromAddress(address, popup);
×
394
                    }
395
                    //update ui
396
                    updateCalculation(curValueMap, curView, address, isForNextStep);
×
397

398
                }
399

400
            } catch (Exception e) {
×
401
                Timber.e(e, "%s refreshCalculationLogic()", this.getClass().getCanonicalName());
×
402

403
            }
×
404
        }
×
405

406

407
    }
1✔
408

409
    @Override
410
    public void initializeDependencyMaps() {
411
        populateDependencyMap(calculationLogicViews, calculationDependencyMap, true);
1✔
412
        populateDependencyMap(skipLogicViews, skipLogicDependencyMap, false);
1✔
413
    }
1✔
414

415
    @Override
416
    public void invokeRefreshLogic(String value, boolean popup, String parentKey, String childKey, String stepName, boolean isForNextStep) {
417
        refreshCalculationLogic(parentKey, childKey, popup, stepName, isForNextStep);
1✔
418
        refreshSkipLogic(parentKey, childKey, popup, stepName, isForNextStep);
1✔
419

420
        if (!isForNextStep) {
1✔
421
            refreshConstraints(parentKey, childKey, popup);
1✔
422
            refreshMediaLogic(parentKey, value, stepName);
1✔
423
        }
424
    }
1✔
425

426
    private void populateDependencyMap(Map<String, View> formViews, Map<String, Set<String>> dependencyMap, boolean calculation) {
427
        for (View view : formViews.values()) {
1✔
428
            try {
429
                boolean isPopup = false;
×
430
                if (view.getTag(R.id.extraPopup) != null) {
×
431
                    isPopup = (boolean) view.getTag(R.id.extraPopup);
×
432
                }
433
                Pair<String[], JSONObject> addressAndValue = calculation ? getCalculationAddressAndValue(view) :
×
434
                        getRelevanceAddress(view, isPopup);
×
435
                if (addressAndValue != null) {
×
436
                    String[] address = addressAndValue.first;
×
437
                    List<String> widgets = null;
×
438
                    if (address.length > 2) {
×
439
                        if (RuleConstant.RULES_DYNAMIC.equals(address[0])) {
×
440
                            widgets = getDynamicRules(address);
×
441
                        } else {
442
                            widgets = getRules(address[1], address[2], true);
×
443
                        }
444
                    } else if (address.length == 2) {
×
445
                        widgets = Arrays.asList(address[0] + "_" + address[1]);
×
446
                    }
447

448
                    if (widgets == null)
×
449
                        continue;
×
450
                    for (String widget : widgets) {
×
451
                        if (!widget.startsWith(RuleConstant.STEP)) {
×
452
                            continue;
×
453
                        }
454
                        String key = (String) view.getTag(R.id.address);
×
455
                        if (!dependencyMap.containsKey(widget)) {
×
456
                            Set<String> views = new HashSet<>();
×
457
                            views.add(key);
×
458
                            dependencyMap.put(widget, views);
×
459
                        } else {
×
460
                            dependencyMap.get(widget).add(key);
×
461
                        }
462
                    }
×
463
                }
464
            } catch (JSONException e) {
×
465
                Timber.e(e);
×
466
            }
×
467
        }
×
468

469
    }
1✔
470

471
    @Override
472
    public void addFormDataView(View view) {
473
        String address = String.valueOf(view.getTag(R.id.address));
1✔
474
        formDataViews.put(address, view);
1✔
475
    }
1✔
476

477
    @Override
478
    public Collection<View> getFormDataViews() {
479
        return formDataViews.values();
1✔
480
    }
481

482
    @Override
483
    public View getFormDataView(String address) {
484
        return formDataViews.get(address);
1✔
485
    }
486

487
    @Override
488
    public JSONObject getObjectUsingAddress(String[] address, boolean popup) throws JSONException {
489
        if (address != null && address.length > 1) {
1✔
490
            if (RuleConstant.RULES_DYNAMIC.equals(address[0])) {
1✔
491
                List<String> rulesList = getDynamicRules(address);
×
492
                return fillFieldsWithValues(rulesList, popup);
×
493
            } else if (RuleConstant.RULES_ENGINE.equals(address[0])) {
1✔
494
                String fieldKey = address[2];
×
495
                List<String> rulesList = getRules(address[1], fieldKey, false);
×
496
                if (rulesList != null) {
×
497
                    return fillFieldsWithValues(rulesList, popup);
×
498
                }
499
            } else {
×
500
                return getRelevanceReferencedObject(address[0], address[1]);
1✔
501
            }
502
        }
503

504
        return null;
×
505
    }
506

507
    private List<String> getDynamicRules(@NonNull String[] address) {
508
        List<String> keysList = new ArrayList<>();
×
509

510
        JSONArray jsonArray = null;
×
511
        if (address.length > 1 && StringUtils.isNotBlank(address[1])) {
×
512
            try {
513
                jsonArray = new JSONArray(address[1]);
×
514
                for (int i = 0; i < jsonArray.length(); i++) {
×
515
                    JSONObject jsonObject = jsonArray.optJSONObject(i);
×
516
                    if (!jsonObject.has(JsonFormConstants.KEY)) {
×
517
                        String condition = jsonObject.optString(RuleConstant.CONDITION);
×
518
                        if (StringUtils.isNotBlank(condition)) {
×
519
                            keysList.addAll(Utils.getConditionKeys(condition));
×
520
                        }
521

522
                        String action = jsonObject.optString(RuleConstant.ACTIONS);
×
523
                        if (StringUtils.isNotBlank(action) && !action.trim().startsWith(RuleConstant.IS_RELEVANT)) {
×
524
                            keysList.addAll(Utils.getConditionKeys(action));
×
525
                        }
526
                    }
527
                }
528
            } catch (JSONException e) {
×
529
                Timber.e(e);
×
530
            }
×
531
        }
532
        return keysList;
×
533
    }
534

535
    private JSONObject fillFieldsWithValues(List<String> rulesList, boolean popup) throws JSONException {
536
        JSONObject result = new JSONObject();
×
537
        JSONArray rulesArray = new JSONArray();
×
538
        for (int h = 1; h < getmJSONObject().getInt(JsonFormConstants.COUNT) + 1; h++) {
×
539
            JSONArray fields = fetchFields(getmJSONObject().optJSONObject(RuleConstant.STEP + h), popup);
×
540
            for (int i = 0; i < fields.length(); i++) {
×
541
                if (rulesList.contains(RuleConstant.STEP + h + "_" +
×
542
                        fields.getJSONObject(i).getString(JsonFormConstants.KEY))) {
×
543

544
                    JSONObject fieldObject = fields.getJSONObject(i);
×
545
                    fieldObject.put(RuleConstant.STEP, RuleConstant.STEP + h);
×
546
                    rulesArray.put(fieldObject);
×
547
                }
548
            }
549
        }
550
        result.put(RuleConstant.RESULT, rulesArray);
×
551
        return result;
×
552
    }
553

554
    @Override
555
    public JSONObject getObjectUsingAddress(String[] address, boolean popup, JSONObject valueSource) throws JSONException {
556
        if (valueSource != null && valueSource.has(JsonFormConstants.KEY) && valueSource.has(JsonFormConstants.STEPNAME) &&
1✔
557
                valueSource.has(JsonFormConstants.OPTION_KEY)) {
×
558

559
            String key = valueSource.getString(JsonFormConstants.KEY);
×
560
            String stepName = valueSource.getString(JsonFormConstants.STEPNAME);
×
561
            String optionKey = valueSource.getString(JsonFormConstants.OPTION_KEY);
×
562

563
            try {
564
                if (address != null && address.length > 1) {
×
565
                    if (RuleConstant.RULES_ENGINE.equals(address[0])) {
×
566
                        String fieldKey = address[2];
×
567

568
                        List<String> rulesList = getRules(address[1], fieldKey, false);
×
569
                        if (rulesList != null) {
×
570
                            JSONObject result = new JSONObject();
×
571
                            JSONArray rulesArray = new JSONArray();
×
572

573
                            JSONObject mainWidget = FormUtils.getFieldFromForm(mJSONObject, key);
×
574
                            if (mainWidget.has(JsonFormConstants.OPTIONS_FIELD_NAME)) {
×
575
                                JSONArray options = mainWidget.getJSONArray(JsonFormConstants.OPTIONS_FIELD_NAME);
×
576
                                for (int i = 0; i < options.length(); i++) {
×
577
                                    JSONObject option = options.getJSONObject(i);
×
578

579
                                    if (option != null && option.has(JsonFormConstants.KEY) &&
×
580
                                            optionKey.equals(option.getString(JsonFormConstants.KEY)) &&
×
581
                                            option.has(JsonFormConstants.CONTENT_FORM)) {
×
582
                                        String formName = option.getString(JsonFormConstants.CONTENT_FORM);
×
583
                                        String popupFormName = "";
×
584
                                        if (genericDialogInterface != null) {
×
585
                                            popupFormName = genericDialogInterface.getFormIdentity();
×
586
                                        }
587

588
                                        if (genericDialogInterface != null && formName.equals(popupFormName)) {
×
589
                                            JSONArray subFormField = genericDialogInterface.getPopUpFields();
×
590
                                            getFieldObject(stepName, rulesList, rulesArray, subFormField);
×
591
                                        } else if (option.has(JsonFormConstants.SECONDARY_VALUE)) {
×
592
                                            JSONArray secondaryValue =
×
593
                                                    option.getJSONArray(JsonFormConstants.SECONDARY_VALUE);
×
594
                                            getFieldObject(stepName, rulesList, rulesArray, secondaryValue);
×
595
                                        }
596
                                    }
597
                                }
598
                            }
599

600
                            result.put(RuleConstant.RESULT, rulesArray);
×
601
                            return result;
×
602
                        }
603
                    } else {
×
604
                        return getRelevanceReferencedObject(address[0], address[1]);
×
605
                    }
606
                }
607
            } catch (Exception e) {
×
608
                Timber.e(e, "%s getObjectUsingAddress()", this.getClass().getCanonicalName());
×
609
            }
×
610
        } else {
×
611
            getObjectUsingAddress(address, popup);
1✔
612
        }
613

614
        return null;
1✔
615
    }
616

617
    /**
618
     * This method checks if all views being watched for constraints enforce those constraints This library currently only
619
     * supports constraints on views that store the value in {@link MaterialEditText} (ie TreeViews, DatePickers, and
620
     * EditTexts), and {@link CheckBox}
621
     *
622
     * @param parentKey {@link String}
623
     * @param childKey  {@link String}
624
     * @param popup     {@link Boolean}
625
     */
626
    @Override
627
    public void refreshConstraints(String parentKey, String childKey, boolean popup) {
628
        initComparisons();
1✔
629

630
        // Priorities constraints on the view that has just been changed
631
        String changedViewKey = parentKey;
1✔
632
        if (changedViewKey != null && childKey != null) {
1✔
633
            changedViewKey = changedViewKey + ":" + childKey;
1✔
634
        }
635

636
        if (changedViewKey != null && (constrainedViews != null && constrainedViews.containsKey(changedViewKey))) {
1✔
637
            checkViewConstraints(constrainedViews.get(changedViewKey), popup);
×
638
        }
639

640
        for (View curView : constrainedViews.values()) {
1✔
641
            String viewKey = getViewKey(curView);
×
642
            if (changedViewKey == null || (!TextUtils.isEmpty(viewKey) && !viewKey.equals(changedViewKey))) {
×
643
                checkViewConstraints(curView, popup);
×
644
            }
645
        }
×
646

647
    }
1✔
648

649
    @Override
650
    public void addOnActivityResultListener(final Integer requestCode, OnActivityResultListener onActivityResultListener) {
651
        onActivityResultListeners.put(requestCode, onActivityResultListener);
1✔
652
    }
1✔
653

654
    @Override
655
    public void addOnActivityRequestPermissionResultListener(Integer requestCode,
656
                                                             OnActivityRequestPermissionResultListener onActivityRequestPermissionResultListener) {
657
        onActivityRequestPermissionResultListeners.put(requestCode, onActivityRequestPermissionResultListener);
1✔
658
    }
1✔
659

660
    @Override
661
    public void removeOnActivityRequestPermissionResultListener(Integer requestCode) {
662
        onActivityRequestPermissionResultListeners.remove(requestCode);
×
663
    }
×
664

665
    @Override
666
    public void resetFocus() {
667
        EditText defaultFocusView = findViewById(R.id.default_focus_view);
1✔
668
        defaultFocusView.requestFocus();
1✔
669
        InputMethodManager inputManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
1✔
670
        if (inputManager != null && getCurrentFocus() != null) {
1✔
671
            inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), HIDE_NOT_ALWAYS);
×
672
        }
673
    }
1✔
674

675
    @Override
676
    public JSONObject getmJSONObject() {
677
        return mJSONObject;
1✔
678
    }
679

680
    @Override
681
    public void setmJSONObject(JSONObject mJSONObject) {
682
        super.setmJSONObject(mJSONObject);
1✔
683
        initializeFormFieldsMap();
1✔
684
    }
1✔
685

686
    private void initializeFormFieldsMap() {
687
        appExecutors.diskIO().execute(new Runnable() {
1✔
688
            @Override
689
            public void run() {
690
                JSONObject formJsonObject = getmJSONObject();
1✔
691
                int count = formJsonObject.optInt(JsonFormConstants.COUNT);
1✔
692
                try {
693
                    for (int i = 1; i <= count; i++) {
1✔
694
                        String step = JsonFormConstants.STEP + i;
1✔
695
                        if (!formJsonObject.has(step)) {
1✔
696
                            Timber.w("Missing step %s", step);
1✔
697
                            continue;
1✔
698
                        }
699
                        JSONArray fields = fetchFields(formJsonObject.getJSONObject(step), false);
1✔
700
                        for (int j = 0; j < fields.length(); j++) {
1✔
701
                            JSONObject field = fields.getJSONObject(j);
1✔
702
                            formFields.put(step + "_" + field.getString(JsonFormConstants.KEY), field);
1✔
703
                        }
704
                    }
705
                } catch (JSONException e) {
×
706
                    Timber.e(e);
×
707
                }
1✔
708
            }
1✔
709
        });
710
    }
1✔
711

712
    @Override
713
    protected void initiateFormUpdate(JSONObject json) {
714
        if (getForm() != null && ((getForm().getHiddenFields() != null && !getForm().getHiddenFields().isEmpty()) || (getForm().getDisabledFields() != null && !getForm().getDisabledFields().isEmpty()))) {
1✔
715
            JSONArray fieldsJsonObject = FormUtils.getMultiStepFormFields(json);
1✔
716
            for (int k = 0; k < fieldsJsonObject.length(); k++) {
1✔
717
                Utils.handleFieldBehaviour(fieldsJsonObject.optJSONObject(k), getForm());
1✔
718
            }
719
        }
720
    }
1✔
721

722
    @Override
723
    public void updateGenericPopupSecondaryValues(JSONArray jsonArray, String stepName) {
724
        if (jsonArray == null || jsonArray.length() == 0) {
×
725
            for (String key : popupFormFields) {
×
726
                popupFormFields.remove(key);
×
727
            }
×
728
        }
729
        setExtraFieldsWithValues(jsonArray);
×
730
        if (jsonArray != null) {
×
731
            for (int j = 0; j < jsonArray.length(); j++) {
×
732
                try {
733
                    JSONObject field = jsonArray.getJSONObject(j);
×
734
                    String key = stepName + "_" + field.getString(JsonFormConstants.KEY);
×
735
                    formFields.put(key, field);
×
736
                    popupFormFields.add(key);
×
737
                } catch (JSONException e) {
×
738
                    Timber.e(e);
×
739
                }
×
740
            }
741
        }
742
    }
×
743

744
    @Override
745
    public void registerLifecycleListener(LifeCycleListener lifeCycleListener) {
746
        lifeCycleListeners.add(lifeCycleListener);
×
747
    }
×
748

749
    @Override
750
    public void unregisterLifecycleListener(LifeCycleListener lifeCycleListener) {
751
        lifeCycleListeners.remove(lifeCycleListener);
×
752
    }
×
753

754
    @Override
755
    public void setGenericPopup(GenericPopupDialog context) {
756
        genericDialogInterface = context;
×
757
    }
×
758

759
    @Override
760
    public String getConfirmCloseMessage() {
761
        return confirmCloseMessage;
1✔
762
    }
763

764
    @Override
765
    public void setConfirmCloseMessage(String confirmCloseMessage) {
766
        this.confirmCloseMessage = confirmCloseMessage;
1✔
767
    }
1✔
768

769
    @Override
770
    public String getConfirmCloseTitle() {
771
        return confirmCloseTitle;
1✔
772
    }
773

774
    @Override
775
    public void setConfirmCloseTitle(String confirmCloseTitle) {
776
        this.confirmCloseTitle = confirmCloseTitle;
1✔
777
    }
1✔
778

779
    @Override
780
    public void showPermissionDeniedDialog() {
781
        new AlertDialog.Builder(this)
×
782
                .setTitle(getString(R.string.permission_denied_title))
×
783
                .setMessage(getString(R.string.permission_messege))
×
784
                .setPositiveButton(getString(R.string.no), new DialogInterface.OnClickListener() {
×
785
                    @Override
786
                    public void onClick(DialogInterface dialog, int which) {
787
                        ActivityCompat.requestPermissions(JsonFormActivity.this, new String[]{
×
788
                                Manifest.permission.READ_PHONE_STATE}, PermissionUtils.PHONE_STATE_PERMISSION);
789
                    }
×
790
                })
791
                .setNegativeButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
×
792
                    @Override
793
                    public void onClick(DialogInterface dialog, int which) {
794
                        dialog.dismiss();
×
795
                    }
×
796
                })
797
                .show();
×
798
    }
×
799

800
    /**
801
     * if the display scroll bars attribute is set to true then the form shows scroll bars
802
     *
803
     * @return true/false {@link Boolean}
804
     */
805
    @Override
806
    public boolean displayScrollBars() {
807
        synchronized (getmJSONObject()) {
1✔
808
            return getmJSONObject().optBoolean(JsonFormConstants.DISPLAY_SCROLL_BARS, false);
1✔
809
        }
810
    }
811

812
    @Override
813
    public boolean skipBlankSteps() {
814
        synchronized (getmJSONObject()) {
1✔
815
            return getmJSONObject().optBoolean(JsonFormConstants.SKIP_BLANK_STEPS, false);
1✔
816
        }
817
    }
818

819
    @Override
820
    public Form form() {
821
        return getForm();
×
822
    }
823

824
    private String getViewKey(View view) {
825
        String key = (String) view.getTag(R.id.key);
1✔
826
        if (view.getTag(R.id.childKey) != null) {
1✔
827
            key = key + ":" + view.getTag(R.id.childKey);
1✔
828
        }
829

830
        return key;
1✔
831
    }
832

833
    private void addToAddressMap(String key, String stepName, String type, String[] address) {
834
        addressMap.put(stepName + "_" + key + "_" + type, address);
1✔
835
    }
1✔
836

837
    private String[] getAddressFromMap(String key, String stepName, String type) {
838
        return addressMap.get(stepName + "_" + key + "_" + type);
1✔
839
    }
840

841
    private String[] getAddress(View view, String curKey, JSONObject curRelevance, String type) {
842
        String[] address;
843
        if (curKey.contains(":")) {
1✔
844
            address = curKey.split(":");
1✔
845
            String[] viewAddress = view.getTag(R.id.address).toString().split(":");
1✔
846
            addToAddressMap(viewAddress[1], viewAddress[0], type, address);
1✔
847
        } else {
1✔
848
            try {
849
                if (curRelevance.getJSONObject(JsonFormConstants.JSON_FORM_KEY.EX_RULES).has(RuleConstant.RULES_DYNAMIC)) {
×
850
                    return getDynamicRulesEngineAddress(curKey, curRelevance, view, type);
×
851
                } else {
852
                    address = getRulesEngineAddress(curKey, curRelevance, view, type);
×
853
                }
854
            } catch (JSONException e) {
×
855
                Timber.e(e);
×
856
                return null;
×
857
            }
×
858
        }
859
        return address;
1✔
860
    }
861

862
    private String[] getRulesEngineAddress(String curKey, JSONObject curRelevance, View view, String type) {
863
        return getRulesEngineAddress(curKey, curRelevance, view, type, RuleConstant.RULES_FILE);
×
864
    }
865

866
    private String[] getDynamicRulesEngineAddress(String curKey, JSONObject curRelevance, View view, String type) {
867
        return getRulesEngineAddress(curKey, curRelevance, view, type, RuleConstant.RULES_DYNAMIC);
×
868
    }
869

870
    private String[] getRulesEngineAddress(String curKey, JSONObject curRelevance, View view, String type, String ruleType) {
871
        String[] address = new String[0];
×
872
        try {
873
            String currentKey = RuleConstant.RULES_DYNAMIC.equals(ruleType) ? ruleType : curKey;
×
874
            address = new String[]{currentKey,
×
875
                    curRelevance.getJSONObject(JsonFormConstants.JSON_FORM_KEY.EX_RULES).getString(ruleType),
×
876
                    view.getTag(R.id.address).toString().replace(':', '_')};
×
877

878
            String[] viewAddress = view.getTag(R.id.address).toString().split(":");
×
879
            addToAddressMap(viewAddress[0], viewAddress[1], type, address);
×
880
        } catch (JSONException e) {
×
881
            Timber.e(e, "%s getRulesEngineAddress()", this.getClass().getCanonicalName());
×
882
        }
×
883
        return address;
×
884
    }
885

886
    private JSONObject getRelevanceReferencedObject(String stepName, String key) {
887
        return formFields.get(stepName + "_" + key);
1✔
888
    }
889

890
    private void getFieldObject(String stepName, List<String> rulesList, JSONArray rulesArray, JSONArray fields)
891
            throws JSONException {
892
        if (fields.length() > 0) {
×
893
            for (int j = 0; j < fields.length(); j++) {
×
894
                JSONObject fieldObject = fields.getJSONObject(j);
×
895
                if (rulesList.contains(stepName + "_" + fieldObject.getString(JsonFormConstants.KEY)) &&
×
896
                        !JsonFormConstants.LABEL.equals(fieldObject.getString(JsonFormConstants.TYPE))) {
×
897
                    if (fieldObject.has(JsonFormConstants.VALUES)) {
×
898
                        String value;
899
                        if (JsonFormConstants.CHECK_BOX.equals(fieldObject.getString(JsonFormConstants.TYPE))) {
×
900
                            value = String.valueOf(fieldObject.getJSONArray(JsonFormConstants.VALUES));
×
901
                            if (Utils.enabledProperty(NativeFormsProperties.KEY.WIDGET_VALUE_TRANSLATED)) {
×
902
                                fieldObject.put(JsonFormConstants.VALUE, Utils.generateTranslatableValue(value, fieldObject));
×
903
                            } else {
904
                                fieldObject.put(JsonFormConstants.VALUE, value);
×
905
                            }
906
                        } else {
907
                            value = fieldObject.getJSONArray(JsonFormConstants.VALUES).getString(0);
×
908
                            fieldObject.put(JsonFormConstants.VALUE, value);
×
909
                        }
910
                    }
911

912
                    fieldObject.put(RuleConstant.STEP, stepName);
×
913
                    rulesArray.put(fieldObject);
×
914

915
                }
916
            }
917
        }
918
    }
×
919

920
    protected void widgetsWriteValue(String stepName, String key, String value, String openMrsEntityParent,
921
                                     String openMrsEntity, String openMrsEntityId, boolean popup) throws JSONException {
922

923
        JSONObject item = formFields.get(stepName + "_" + key);
1✔
924
        if (item != null) {
1✔
925
            String keyAtIndex = item.getString(JsonFormConstants.KEY);
1✔
926
            String itemType = item.has(JsonFormConstants.TYPE) ? item.getString(JsonFormConstants.TYPE) : "";
1✔
927
            boolean isSpecialWidget = isSpecialWidget(itemType);
1✔
928
            String cleanKey = isSpecialWidget ? cleanWidgetKey(key, itemType) : key;
1✔
929

930
            if (cleanKey.equals(keyAtIndex)) {
1✔
931
                if (item.has(JsonFormConstants.TEXT)) {
1✔
932
                    item.put(JsonFormConstants.TEXT, value);
×
933
                } else {
934
                    widgetWriteItemValue(value, item, itemType);
1✔
935
                }
936
                addOpenMrsAttributes(openMrsEntityParent, openMrsEntity, openMrsEntityId, item);
1✔
937
                invokeRefreshLogic(value, popup, cleanKey, null, stepName, false);
1✔
938
            }
939
        }
940
    }
1✔
941

942
    private void addOpenMrsAttributes(String openMrsEntityParent, String openMrsEntity, String openMrsEntityId,
943
                                      JSONObject item) throws JSONException {
944
        item.put(JsonFormConstants.OPENMRS_ENTITY_PARENT, openMrsEntityParent);
1✔
945
        item.put(JsonFormConstants.OPENMRS_ENTITY, openMrsEntity);
1✔
946
        item.put(JsonFormConstants.OPENMRS_ENTITY_ID, openMrsEntityId);
1✔
947
    }
1✔
948

949
    private void widgetWriteItemValue(String value, JSONObject item, String itemType) throws JSONException {
950
        if (!TextUtils.isEmpty(value)) {
1✔
951
            value = value.trim();
1✔
952
        }
953
        if (itemType.equals(JsonFormConstants.HIDDEN) && TextUtils.isEmpty(value)) {
1✔
954
            if (item.has(JsonFormConstants.VALUE) && !TextUtils.isEmpty(item.getString(JsonFormConstants.VALUE)))
×
955
                item.put(JsonFormConstants.VALUE, item.getString(JsonFormConstants.VALUE));
×
956
            else item.put(JsonFormConstants.VALUE, value);
×
957
        } else {
958
            if ((itemType.equals(JsonFormConstants.NATIVE_RADIO_BUTTON) || itemType.equals(JsonFormConstants.SPINNER) || itemType.equals(JsonFormConstants.CHECK_BOX)) && Utils.enabledProperty(NativeFormsProperties.KEY.WIDGET_VALUE_TRANSLATED)) {
1✔
959
                if (itemType.equals(JsonFormConstants.SPINNER)) {
×
960
                    if (item.has(JsonFormConstants.KEYS) && item.has(JsonFormConstants.VALUES)) {
×
961
                        item.put(JsonFormConstants.VALUE, value);
×
962
                    } else {
963
                        item.put(JsonFormConstants.VALUE, Utils.generateTranslatableValue(value, item));
×
964
                    }
965
                } else {
966
                    item.put(JsonFormConstants.VALUE, Utils.generateTranslatableValue(value, item));
×
967
                }
968
            } else {
969
                item.put(JsonFormConstants.VALUE, value);
1✔
970
            }
971
        }
972
    }
1✔
973

974
    /**
975
     * Generates a JSONObject Value for the value translatable fields
976
     *
977
     * @param value
978
     * @param item
979
     * @param itemType
980
     * @return
981
     */
982

983

984
    private boolean checkPopUpValidity(String[] curKey, boolean popup) throws JSONException {
985
        boolean validity = false;
×
986
        if (popup) {
×
987
            String parentKey = "";
×
988
            if (curKey.length == 2) {
×
989
                parentKey = curKey[1];
×
990
            } else if (curKey.length == 3) {
×
991
                parentKey = curKey[2].substring(curKey[2].indexOf("_") + 1);
×
992
            }
993

994
            if (extraFieldsWithValues != null && extraFieldsWithValues.length() > 0) {
×
995
                for (int i = 0; i < extraFieldsWithValues.length(); i++) {
×
996
                    JSONObject jsonObject = extraFieldsWithValues.getJSONObject(i);
×
997
                    if (jsonObject.has(JsonFormConstants.KEY) &&
×
998
                            jsonObject.getString(JsonFormConstants.KEY).equals(parentKey)) {
×
999
                        validity = true;
×
1000
                        break;
×
1001
                    }
1002
                }
1003
            }
1004
        }
1005
        return validity;
×
1006
    }
1007

1008
    protected boolean isSpecialWidget(String itemType) {
1009
        return isNumberSelector(itemType);
1✔
1010
    }
1011

1012
    protected String cleanWidgetKey(String itemKey, String itemType) {
1013
        String key = itemKey;
×
1014

1015
        if (isNumberSelector(itemType) && itemKey.endsWith(JsonFormConstants.SUFFIX.TEXT_VIEW) ||
×
1016
                itemKey.endsWith(JsonFormConstants.SUFFIX.SPINNER)) {
×
1017
            key = itemKey.endsWith(JsonFormConstants.SUFFIX.TEXT_VIEW) ?
×
1018
                    itemKey.substring(0, itemKey.indexOf(JsonFormConstants.SUFFIX.TEXT_VIEW)) :
×
1019
                    itemKey.substring(0, itemKey.indexOf(JsonFormConstants.SUFFIX.SPINNER));
×
1020
        }
1021

1022
        return key;
×
1023
    }
1024

1025
    private boolean isNumberSelector(String itemType) {
1026
        return itemType.equals(JsonFormConstants.NUMBER_SELECTOR);
1✔
1027
    }
1028

1029
    protected void checkBoxWriteValue(String stepName, String parentKey, String childObjectKey, String childKey,
1030
                                      String value, boolean popup) throws JSONException {
1031

1032
        synchronized (getmJSONObject()) {
1✔
1033
            JSONObject checkboxObject = formFields.get(stepName + "_" + parentKey);
1✔
1034
            JSONArray checkboxOptions = checkboxObject.getJSONArray(childObjectKey);
1✔
1035
            HashSet<String> currentValues = new HashSet<>();
1✔
1036
            //Get current values
1037
            if (checkboxObject.has(JsonFormConstants.VALUE)) {
1✔
1038
                formUtils.updateValueToJSONArray(checkboxObject, checkboxObject.optString(JsonFormConstants.VALUE, ""));
×
1039
            }
1040

1041
            if (checkboxObject != null && checkboxOptions != null) {
1✔
1042
                if (checkboxObject.has(JsonFormConstants.VALUE) && StringUtils.isNotEmpty(checkboxObject.getString(JsonFormConstants.VALUE))) {
1✔
1043
                    currentValues.addAll(getCurrentCheckboxValues(checkboxObject.getJSONArray(JsonFormConstants.VALUE)));
×
1044
                }
1045

1046
                for (int index = 0; index < checkboxOptions.length(); index++) {
1✔
1047
                    JSONObject option = checkboxOptions.getJSONObject(index);
1✔
1048
                    if (option.has(JsonFormConstants.KEY) &&
1✔
1049
                            childKey.equals(option.getString(JsonFormConstants.KEY))) {
1✔
1050
                        option.put(JsonFormConstants.VALUE, Boolean.parseBoolean(value));
1✔
1051
                        if (Boolean.parseBoolean(value)) {
1✔
1052
                            if (Utils.enabledProperty(NativeFormsProperties.KEY.WIDGET_VALUE_TRANSLATED)) {
1✔
1053
                                JSONObject object = Utils.generateTranslatableValue(childKey, option);
1✔
1054
                                currentValues.add(object.toString());
1✔
1055
                            } else {
1✔
1056
                                currentValues.add(childKey);
×
1057
                            }
1058
                        } else {
1059
                            if (Utils.enabledProperty(NativeFormsProperties.KEY.WIDGET_VALUE_TRANSLATED)) {
×
1060
                                JSONObject object = Utils.generateTranslatableValue(childKey, option);
×
1061
                                currentValues.remove(object.toString());
×
1062
                            } else {
×
1063
                                currentValues.remove(childKey);
×
1064
                            }
1065
                        }
1066
                    }
1067
                }
1068
                checkboxObject.put(JsonFormConstants.VALUE, getCheckboxValueJsonArray(currentValues));
1✔
1069
            }
1070
            invokeRefreshLogic(value, popup, parentKey, childKey, stepName, false);
1✔
1071
        }
1✔
1072
    }
1✔
1073

1074
    @Override
1075
    public void onBackPressed() {
1076
        AlertDialog dialog = new AlertDialog.Builder(this, R.style.AppThemeAlertDialog).setTitle(confirmCloseTitle)
×
1077
                .setMessage(confirmCloseMessage).setNegativeButton(R.string.yes, new DialogInterface.OnClickListener() {
×
1078
                    @Override
1079
                    public void onClick(DialogInterface dialog, int which) {
1080
                        JsonFormActivity.this.finish();
×
1081
                        CountDownTimerFactory.stopAlarm();
×
1082
                    }
×
1083
                }).setPositiveButton(R.string.no, new DialogInterface.OnClickListener() {
×
1084
                    @Override
1085
                    public void onClick(DialogInterface dialog, int which) {
1086
                        Timber.d("No button on dialog in %s", JsonFormActivity.class.getCanonicalName());
×
1087
                    }
×
1088
                }).create();
×
1089

1090
        dialog.show();
×
1091
    }
×
1092

1093
    @Override
1094
    public void onLowMemory() {
1095
        super.onLowMemory();
×
1096
        for (LifeCycleListener lifeCycleListener : lifeCycleListeners) {
×
1097
            lifeCycleListener.onLowMemory();
×
1098
        }
×
1099
    }
×
1100

1101
    @Override
1102
    protected void onPause() {
1103
        localBroadcastManager.unregisterReceiver(NumberSelectorFactory.getNumberSelectorFactory().getNumberSelectorsReceiver());
×
1104
        localBroadcastManager.unregisterReceiver(messageReceiver);
×
1105
        super.onPause();
×
1106
        for (LifeCycleListener lifeCycleListener : lifeCycleListeners) {
×
1107
            lifeCycleListener.onPause();
×
1108
        }
×
1109
        EventBus.getDefault().unregister(this);
×
1110
    }
×
1111

1112
    @Override
1113
    protected void onResume() {
1114
        super.onResume();
1✔
1115
        localBroadcastManager
1✔
1116
                .registerReceiver(messageReceiver, new IntentFilter(JsonFormConstants.INTENT_ACTION.JSON_FORM_ACTIVITY));
1✔
1117
        localBroadcastManager.registerReceiver(NumberSelectorFactory.getNumberSelectorFactory().getNumberSelectorsReceiver(),
1✔
1118
                new IntentFilter(JsonFormConstants.INTENT_ACTION.NUMBER_SELECTOR_FACTORY));
1119

1120
        for (LifeCycleListener lifeCycleListener : lifeCycleListeners) {
1✔
1121
            lifeCycleListener.onResume();
×
1122
        }
×
1123
        if (!getmJSONObject().has(JsonFormConstants.INVISIBLE_REQUIRED_FIELDS)) {
1✔
1124
            try {
1125
                getmJSONObject().put(JsonFormConstants.INVISIBLE_REQUIRED_FIELDS, invisibleRequiredFields);
1✔
1126
            } catch (JSONException e) {
×
1127
                Timber.e(e, "JsonFormActivity --> onResume");
×
1128
            }
1✔
1129
        }
1130
        EventBus.getDefault().register(this);
1✔
1131
    }
1✔
1132

1133

1134
    protected Pair<String[], JSONObject> getRelevanceAddress(View view, boolean popup) throws
1135
            JSONException {
1136
        if (view != null) {
1✔
1137
            String relevanceTag = (String) view.getTag(R.id.relevance);
1✔
1138
            String widgetKey = (String) view.getTag(R.id.key);
1✔
1139
            String stepName = ((String) view.getTag(R.id.address)).split(":")[0];
1✔
1140
            boolean widgetDisplay = (boolean) view.getTag(R.id.extraPopup);
1✔
1141
            if ((relevanceTag != null && relevanceTag.length() > 0) && (widgetDisplay == popup)) {
1✔
1142
                JSONObject relevance = new JSONObject(relevanceTag);
1✔
1143
                Iterator<String> keys = relevance.keys();
1✔
1144
                while (keys.hasNext()) {
1✔
1145
                    String curKey = keys.next();
1✔
1146
                    JSONObject curRelevance = relevance.has(curKey) ? relevance.getJSONObject(curKey) : null;
1✔
1147

1148
                    String[] address = getAddressFromMap(widgetKey, stepName, JsonFormConstants.RELEVANCE);
1✔
1149
                    if (address == null) {
1✔
1150
                        address = getAddress(view, curKey, curRelevance, JsonFormConstants.RELEVANCE);
1✔
1151
                    }
1152
                    return new Pair<>(address, curRelevance);
1✔
1153
                }
1154
            }
1155
        }
1156
        return null;
×
1157
    }
1158

1159

1160
    protected void addRelevance(View view, boolean popup, boolean isForNextStep) {
1161
        try {
1162
            Pair<String[], JSONObject> addressPair = getRelevanceAddress(view, popup);
×
1163
            boolean comparison = true;
×
1164
            if (addressPair != null) {
×
1165
                String[] address = addressPair.first;
×
1166
                JSONObject curRelevance = addressPair.second;
×
1167
                boolean isPopup = checkPopUpValidity(address, popup);
×
1168
                if (address.length > 1) {
×
1169

1170
                    Facts curValueMap = getValueFromAddress(address, isPopup);
×
1171
                    try {
1172
                        comparison = isRelevant(curValueMap, curRelevance);
×
1173
                    } catch (Exception e) {
×
1174
                        Timber.e(e, "JsonFormActivity --> addRelevance --> comparison");
×
1175
                    }
×
1176

1177
                }
1178

1179
                if (isForNextStep) {
×
1180
                    if (((address.length == 2 && address[0].equals(nextStep())) || (address.length == 3 && address[2].contains(nextStep()))) && comparison) {
×
1181
                        setNextStepRelevant(true);
×
1182
                    }
1183
                } else {
1184
                    if (Utils.isRunningOnUiThread()) {
×
1185
                        toggleViewVisibility(view, comparison, isPopup);
×
1186
                    }
1187
                }
1188
            }
1189

1190

1191
        } catch (Exception e) {
×
1192
            Timber.e(e);
×
1193
        }
×
1194
    }
×
1195

1196
    protected void toggleViewVisibility(View view, boolean visible, boolean popup) {
1197
        try {
1198
            JSONArray canvasViewIds = new JSONArray((String) view.getTag(R.id.canvas_ids));
1✔
1199
            String addressString = (String) view.getTag(R.id.address);
1✔
1200
            String[] address = addressString.split(":");
1✔
1201
            JSONObject object = getObjectUsingAddress(address, popup);
1✔
1202
            boolean enabled = visible;
1✔
1203
            if (object != null && object.has(JsonFormConstants.READ_ONLY) &&
1✔
1204
                    object.getBoolean(JsonFormConstants.READ_ONLY) && visible) {
×
1205
                enabled = false;
×
1206
            }
1207

1208
            view.setEnabled(enabled);
1✔
1209
            if (view instanceof MaterialEditText || view instanceof RelativeLayout || view instanceof LinearLayout) {
1✔
1210
                view.setFocusable(enabled);
1✔
1211
                if (view instanceof MaterialEditText) {
1✔
1212
                    view.setFocusableInTouchMode(enabled);
1✔
1213
                }
1214
            }
1215

1216
            updateCanvas(view, visible, canvasViewIds, addressString, object);
1✔
1217
            setReadOnlyAndFocus(view, visible, popup);
1✔
1218

1219
        } catch (JSONException e) {
×
1220
            Timber.e(e);
×
1221
        }
1✔
1222
    }
1✔
1223

1224
    private void setReadOnlyAndFocus(View view, boolean visible, boolean popup) {
1225
        try {
1226
            String addressString = (String) view.getTag(R.id.address);
1✔
1227
            String widgetType = (String) view.getTag(R.id.type);
1✔
1228
            String[] address = addressString.split(":");
1✔
1229
            JSONObject object = getObjectUsingAddress(address, popup);
1✔
1230

1231
            boolean enabled = visible;
1✔
1232
            if (object != null && object.has(JsonFormConstants.READ_ONLY) &&
1✔
1233
                    object.getBoolean(JsonFormConstants.READ_ONLY) && visible) {
×
1234
                enabled = false;
×
1235
            }
1236

1237
            view.setEnabled(enabled);
1✔
1238
            if (StringUtils.isNotBlank(widgetType) && JsonFormConstants.NATIVE_RADIO_BUTTON.equals(widgetType) && view instanceof RadioGroup) {
1✔
1239
                setReadOnlyRadioButtonOptions(view, enabled);
×
1240
            }
1241
            if (view instanceof MaterialEditText || view instanceof RelativeLayout || view instanceof LinearLayout) {
1✔
1242
                view.setFocusable(enabled);
1✔
1243
                if (view instanceof MaterialEditText) {
1✔
1244
                    view.setFocusableInTouchMode(enabled);
1✔
1245
                }
1246
            }
1247
        } catch (JSONException e) {
×
1248
            Timber.e(e, "JsonFormActivity --> setReadOnlyAndFocus");
×
1249
        }
1✔
1250
    }
1✔
1251

1252
    /**
1253
     * Gets the {@link AppCompatRadioButton} views on the whole {@link com.vijay.jsonwizard.widgets.NativeRadioButtonFactory} and updates the enabled status
1254
     *
1255
     * @param view    {@link View}
1256
     * @param enabled {@link Boolean}
1257
     */
1258
    private void setReadOnlyRadioButtonOptions(View view, boolean enabled) {
1259
        if (view != null) {
1✔
1260
            try {
1261
                int viewChildrenCount = ((RadioGroup) view).getChildCount();
1✔
1262
                for (int i = 0; i < viewChildrenCount; i++) {
1✔
1263
                    RelativeLayout radioGroupChildLayout = (RelativeLayout) ((RadioGroup) view).getChildAt(i);
1✔
1264
                    LinearLayout linearLayout = (LinearLayout) (radioGroupChildLayout).getChildAt(0);
1✔
1265
                    LinearLayout radioButtonMainLayout = (LinearLayout) (linearLayout).getChildAt(0);
1✔
1266
                    AppCompatRadioButton appCompatRadioButton = (AppCompatRadioButton) (radioButtonMainLayout).getChildAt(0);
1✔
1267
                    appCompatRadioButton.setEnabled(enabled);
1✔
1268
                }
1269
            } catch (ClassCastException e) {
1✔
1270
                Timber.e(e, " --> setReadOnlyRadioButtonOptions");
1✔
1271
            }
1✔
1272
        }
1273
    }
1✔
1274

1275
    private void checkViewConstraints(View curView, boolean popup) {
1276
        String constraintTag = (String) curView.getTag(R.id.constraints);
×
1277
        String widgetKey = (String) curView.getTag(R.id.key);
×
1278
        String stepName = ((String) curView.getTag(R.id.address)).split(":")[0];
×
1279
        if (constraintTag != null && constraintTag.length() > 0) {
×
1280
            try {
1281

1282
                String errorMessage = null;
×
1283
                String[] address = null;
×
1284

1285
                if (constraintTag.charAt(0) == '[') {
×
1286
                    String addressString = (String) curView.getTag(R.id.address);
×
1287
                    address = addressString.split(":");
×
1288

1289
                    JSONArray constraint = new JSONArray(constraintTag);
×
1290
                    for (int i = 0; i < constraint.length(); i++) {
×
1291
                        JSONObject curConstraint = constraint.getJSONObject(i);
×
1292
                        if (address.length == 2) {
×
1293
                            String value = String.valueOf(getValueFromAddress(address, popup).get(JsonFormConstants.VALUE));
×
1294
                            errorMessage = enforceConstraint(value, curView, curConstraint);
×
1295
                            if (errorMessage != null) break;
×
1296
                        }
1297
                    }
1298

1299
                } else {
×
1300
                    //Rules Engine
1301
                    JSONObject constraint = new JSONObject(constraintTag);
×
1302
                    Iterator<String> keys = constraint.keys();
×
1303
                    while (keys.hasNext()) {
×
1304
                        String curKey = keys.next();
×
1305
                        JSONObject curConstraint = constraint.getJSONObject(curKey);
×
1306

1307

1308
                        address = getAddressFromMap(widgetKey, stepName, JsonFormConstants.CONSTRAINTS);
×
1309
                        if (address == null) {
×
1310
                            address = getAddress(curView, curKey, curConstraint, JsonFormConstants.CONSTRAINTS);
×
1311
                        }
1312
                        Facts curValueMap = getValueFromAddress(address, popup);
×
1313
                        errorMessage = enforceConstraint(curValueMap, curConstraint);
×
1314
                        if (errorMessage != null) break;
×
1315
                    }
×
1316
                }
1317

1318
                updateUiByConstraints(curView, popup, errorMessage);
×
1319

1320
            } catch (Exception e) {
×
1321
                Timber.e(e, "JsonFormActivity --> checkViewConstraints");
×
1322
            }
×
1323
        }
1324
    }
×
1325

1326
    private void updateUiByConstraints(View curView, boolean popup, String errorMessage) throws
1327
            JSONException {
1328
        String[] address = ((String) curView.getTag(R.id.address)).split(":");
1✔
1329
        if (errorMessage != null) {
1✔
1330
            if (curView instanceof MaterialEditText) {
1✔
1331
                ((MaterialEditText) curView).setText(null);
1✔
1332
                ((MaterialEditText) curView).setError(errorMessage);
1✔
1333
            } else if (curView instanceof CheckBox) {
1✔
1334
                ((CheckBox) curView).setChecked(false);
×
1335
                Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show();
×
1336
                String checkBoxKey = (String) curView.getTag(R.id.childKey);
×
1337

1338
                JSONObject questionObject = getObjectUsingAddress(address, popup);
×
1339
                for (int i = 0;
×
1340
                     i < questionObject.getJSONArray(JsonFormConstants.OPTIONS_FIELD_NAME).length(); i++) {
×
1341
                    JSONObject curOption =
×
1342
                            questionObject.getJSONArray(JsonFormConstants.OPTIONS_FIELD_NAME).getJSONObject(i);
×
1343
                    if (curOption.getString(JsonFormConstants.KEY).equals(checkBoxKey)) {
×
1344
                        curOption.put(JsonFormConstants.VALUE, "false");
×
1345
                        break;
×
1346
                    }
1347
                }
1348
            } else if (curView.getTag(R.id.type).toString().equals(JsonFormConstants.NUMBER_SELECTOR) &&
1✔
1349
                    !TextUtils.isEmpty(errorMessage) &&
×
1350
                    (curView.getTag(R.id.previous) == null || !curView.getTag(R.id.previous).equals(errorMessage))) {
×
1351

1352
                if (!"false".equals(errorMessage)) {
×
1353
                    Intent localIntent = new Intent(JsonFormConstants.INTENT_ACTION.NUMBER_SELECTOR_FACTORY);
×
1354
                    localIntent.putExtra(JsonFormConstants.MAX_SELECTION_VALUE, Integer.valueOf(errorMessage));
×
1355
                    localIntent.putExtra(JsonFormConstants.JSON_OBJECT_KEY, curView.getTag(R.id.key).toString());
×
1356
                    localIntent.putExtra(JsonFormConstants.STEPNAME, address[0]);
×
1357
                    localIntent.putExtra(JsonFormConstants.IS_POPUP, popup);
×
1358
                    localBroadcastManager.sendBroadcast(localIntent);
×
1359
                    curView.setTag(R.id.previous, errorMessage); //Store value to avoid re-fires
×
1360
                }
×
1361

1362

1363
            } else if (curView instanceof RadioGroup &&
1✔
1364
                    curView.getTag(R.id.type).toString().equals(JsonFormConstants.NATIVE_RADIO_BUTTON) &&
1✔
1365
                    !TextUtils.isEmpty(errorMessage) &&
×
1366
                    (curView.getTag(R.id.previous) == null || !curView.getTag(R.id.previous).equals(errorMessage))) {
×
1367

1368
                JSONObject jsonObject = (JSONObject) curView.getTag(R.id.json_object);
×
1369
                JSONObject jsonObjectNew = new JSONObject(errorMessage);
×
1370
                Iterator<String> keys = jsonObjectNew.keys();
×
1371

1372
                while (keys.hasNext()) {
×
1373
                    String key = keys.next();
×
1374
                    jsonObject.put(key, jsonObjectNew.getString(key));
×
1375
                }
×
1376

1377
            } else if (curView instanceof LinearLayout) {
1✔
1378
                LinearLayout linearLayout = (LinearLayout) curView;
1✔
1379
                try {
1380
                    View viewRadioGroup = linearLayout.getChildAt(0);
1✔
1381
                    if (viewRadioGroup instanceof RadioGroup && curView.getTag(R.id.type).toString().equals(JsonFormConstants.NATIVE_RADIO_BUTTON) &&
1✔
1382
                            !TextUtils.isEmpty(errorMessage) &&
×
1383
                            (curView.getTag(R.id.previous) == null || !curView.getTag(R.id.previous).equals(errorMessage))) {
×
1384
                        JSONObject jsonObject = (JSONObject) curView.getTag(R.id.json_object);
×
1385
                        JSONObject jsonObjectNew = new JSONObject(errorMessage);
×
1386
                        Iterator<String> keys = jsonObjectNew.keys();
×
1387
                        while (keys.hasNext()) {
×
1388
                            String key = keys.next();
×
1389
                            jsonObject.put(key, jsonObjectNew.getString(key));
×
1390
                        }
×
1391
                    }
1392
                } catch (IndexOutOfBoundsException e) {
×
1393
                    Timber.e(e);
×
1394
                }
1✔
1395
            }
1396
        }
1397
    }
1✔
1398

1399
    private Facts getValueFromAddress(String[] address, boolean popup, JSONObject valueSource) throws
1400
            Exception {
1401
        JSONObject object = getObjectUsingAddress(address, popup, valueSource);
1✔
1402
        return getEntries(address, object);
1✔
1403
    }
1404

1405
    private Facts getValueFromAddress(String[] address, boolean popup) throws Exception {
1406
        JSONObject object = getObjectUsingAddress(address, popup);
×
1407
        return getEntries(address, object);
×
1408
    }
1409

1410
    private Facts getEntries(String[] address, JSONObject object) throws JSONException {
1411
        Facts result = new Facts();
1✔
1412
        if (object != null) {
1✔
1413
            //reset the rules check value
1414
            object.put(RuleConstant.IS_RULE_CHECK, false);
×
1415
            if (object.has(RuleConstant.RESULT)) {
×
1416
                JSONArray jsonArray = object.getJSONArray(RuleConstant.RESULT);
×
1417

1418
                for (int i = 0; i < jsonArray.length(); i++) {
×
1419
                    JSONObject formObject = jsonArray.getJSONObject(i);
×
1420

1421
                    formObject.put(RuleConstant.IS_RULE_CHECK, true);
×
1422
                    formObject.put(RuleConstant.STEP, formObject.getString(RuleConstant.STEP));
×
1423

1424
                    result.asMap().putAll(getValueFromAddressCore(formObject).asMap());
×
1425
                }
1426

1427
                result.put(RuleConstant.SELECTED_RULE, address[2]);
×
1428
            } else {
×
1429
                result = getValueFromAddressCore(object);
×
1430
            }
1431
        }
1432
        return result;
1✔
1433
    }
1434

1435
    protected Facts getValueFromAddressCore(JSONObject object) throws JSONException {
1436
        Facts result = new Facts();
1✔
1437

1438
        if (object != null && object.has(JsonFormConstants.TYPE)) {
1✔
1439
            switch (object.getString(JsonFormConstants.TYPE)) {
1✔
1440
                case JsonFormConstants.CHECK_BOX:
1441
                    result = formUtils.getCheckBoxResults(object);
1✔
1442
                    break;
1✔
1443
                case JsonFormConstants.NATIVE_RADIO_BUTTON:
1444
                case JsonFormConstants.EXTENDED_RADIO_BUTTON:
1445
                    boolean multiRelevance = object.optBoolean(JsonFormConstants.NATIVE_RADIO_BUTTON_MULTI_RELEVANCE, false);
1✔
1446
                    result = formUtils.getRadioButtonResults(multiRelevance, object);
1✔
1447
                    break;
1✔
1448
                default:
1449
                    result.put(getKey(object), getValue(object));
1✔
1450
                    break;
1451
            }
1452

1453
            if (object.has(RuleConstant.IS_RULE_CHECK) && object.getBoolean(RuleConstant.IS_RULE_CHECK) &&
1✔
1454
                    (object.getString(JsonFormConstants.TYPE).equals(JsonFormConstants.CHECK_BOX) ||
1✔
1455
                            (object.getString(JsonFormConstants.TYPE).equals(JsonFormConstants.NATIVE_RADIO_BUTTON) &&
1✔
1456
                                    object.optBoolean(JsonFormConstants.NATIVE_RADIO_BUTTON_MULTI_RELEVANCE, false)))) {
×
1457
                List<String> selectedValues = new ArrayList<>(result.asMap().keySet());
×
1458
                result = new Facts();
×
1459
                result.put(getKey(object), selectedValues);
×
1460
            }
1461
        }
1462
        return result;
1✔
1463
    }
1464

1465
    private void initComparisons() {
1466
        if (comparisons == null) {
1✔
1467
            functionRegex = "";
1✔
1468
            comparisons = new HashMap<>();
1✔
1469

1470
            LessThanComparison lessThanComparison = new LessThanComparison();
1✔
1471
            functionRegex += lessThanComparison.getFunctionName();
1✔
1472
            comparisons.put(lessThanComparison.getFunctionName(), lessThanComparison);
1✔
1473

1474
            LessThanEqualToComparison lessThanEqualToComparison = new LessThanEqualToComparison();
1✔
1475
            functionRegex += "|" + lessThanEqualToComparison.getFunctionName();
1✔
1476
            comparisons.put(lessThanEqualToComparison.getFunctionName(), lessThanEqualToComparison);
1✔
1477

1478
            EqualToComparison equalToComparison = new EqualToComparison();
1✔
1479
            functionRegex += "|" + equalToComparison.getFunctionName();
1✔
1480
            comparisons.put(equalToComparison.getFunctionName(), equalToComparison);
1✔
1481

1482
            NotEqualToComparison notEqualToComparer = new NotEqualToComparison();
1✔
1483
            functionRegex += "|" + notEqualToComparer.getFunctionName();
1✔
1484
            comparisons.put(notEqualToComparer.getFunctionName(), notEqualToComparer);
1✔
1485

1486
            GreaterThanComparison greaterThanComparison = new GreaterThanComparison();
1✔
1487
            functionRegex += "|" + greaterThanComparison.getFunctionName();
1✔
1488
            comparisons.put(greaterThanComparison.getFunctionName(), greaterThanComparison);
1✔
1489

1490
            GreaterThanEqualToComparison greaterThanEqualToComparison = new GreaterThanEqualToComparison();
1✔
1491
            functionRegex += "|" + greaterThanEqualToComparison.getFunctionName();
1✔
1492
            comparisons.put(greaterThanEqualToComparison.getFunctionName(), greaterThanEqualToComparison);
1✔
1493

1494
            RegexComparison regexComparison = new RegexComparison();
1✔
1495
            functionRegex += "|" + regexComparison.getFunctionName();
1✔
1496
            comparisons.put(regexComparison.getFunctionName(), regexComparison);
1✔
1497
        }
1498
    }
1✔
1499

1500
    private boolean doComparison(String value, JSONObject comparison) throws Exception {
1501
        String type = comparison.getString(JsonFormConstants.TYPE).toLowerCase();
×
1502
        String ex = comparison.getString(JsonFormConstants.EX);
×
1503

1504
        Pattern pattern = Pattern.compile("(" + functionRegex + ")\\((.*)\\)");
×
1505
        Matcher matcher = pattern.matcher(ex);
×
1506
        if (matcher.find()) {
×
1507
            String functionName = matcher.group(1);
×
1508
            String b = matcher.group(
×
1509
                    2);//functions arguments should be two, and should either be addresses or values (enclosed using "")
1510
            String[] args = getFunctionArgs(b, value);
×
1511
            return comparisons.get(functionName).compare(args[0], type, args[1]);
×
1512
        }
1513

1514
        return false;
×
1515
    }
1516

1517
    private String[] getFunctionArgs(String functionArgs, String value) {
1518
        String[] args = new String[2];
×
1519
        String[] splitArgs = functionArgs.split(",");
×
1520
        if (splitArgs.length == 2) {
×
1521
            Pattern valueRegex = Pattern.compile("\"(.*)\"");
×
1522
            for (int i = 0; i < splitArgs.length; i++) {
×
1523
                String curArg = splitArgs[i].trim();
×
1524

1525
                if (".".equals(curArg)) {
×
1526
                    args[i] = value;
×
1527
                } else {
1528
                    Matcher valueMatcher = valueRegex.matcher(curArg);
×
1529
                    if (valueMatcher.find()) {
×
1530
                        args[i] = valueMatcher.group(1);
×
1531
                    } else {
1532
                        try {
1533
                            args[i] = String.valueOf(
×
1534
                                    getValueFromAddress(curArg.split(":"), false).get(JsonFormConstants.VALUE));
×
1535
                        } catch (Exception e) {
×
1536
                            Timber.e(e, "JsonFormActivity --> getFunctionArgs");
×
1537
                        }
×
1538
                    }
1539
                }
1540
            }
1541
        }
1542

1543
        return args;
×
1544
    }
1545

1546
    protected void refreshMediaLogic(String key, String value, String stepName) {
1547
        if (StringUtils.isBlank(key))
1✔
1548
            return;
1✔
1549
        try {
1550
            JSONObject questionGroup = formFields.get(stepName + "_" + key);
1✔
1551
            if (questionGroup == null) {
1✔
1552
                Timber.d("refreshMediaLogic field %s is missing", key);
×
1553
            } else if ((questionGroup.has("key") && questionGroup.has("has_media_content")) &&
1✔
1554
                    (questionGroup.getString("key").equalsIgnoreCase(key)) &&
×
1555
                    (questionGroup.getBoolean("has_media_content"))) {
×
1556
                JSONArray medias = questionGroup.getJSONArray("media");
×
1557
                for (int j = 0; j < medias.length(); j++) {
×
1558
                    JSONObject media = medias.getJSONObject(j);
×
1559
                    mediaDialog(media, value);
×
1560
                }
1561
            }
1562
        } catch (Exception e) {
×
1563
            Timber.e(e, "JsonFormActivity --> refreshMediaLogic");
×
1564
        }
1✔
1565
    }
1✔
1566

1567
    public void mediaDialog(JSONObject media, String value) {
1568
        try {
1569
            if (media.getString("media_trigger_value").equalsIgnoreCase(value)) {
×
1570
                String mediatype = media.getString("media_type");
×
1571
                String medialink = media.getString("media_link");
×
1572
                String mediatext = media.getString("media_text");
×
1573

1574
                infoDialog(mediatype, medialink, mediatext);
×
1575
            }
1576
        } catch (Exception e) {
×
1577
            Timber.e(e, "JsonFormActivity --> mediaDialog");
×
1578
        }
×
1579
    }
×
1580

1581
    private void infoDialog(String mediatype, String medialink, String mediatext) {
1582
        final FancyAlertDialog.Builder builder = new FancyAlertDialog.Builder(this);
×
1583
        builder.setTitle("Info");
×
1584
        builder.setBackgroundColor(Color.parseColor("#208CC5"))
×
1585
                .setPositiveBtnBackground(Color.parseColor("#208CC5"))  //Don't pass R
×
1586
                // .color.colorvalue
1587
                .setPositiveBtnText("OK").setAnimation(Animation.SLIDE).isCancellable(true)
×
1588
                .setIcon(com.shashank.sony.fancydialoglib.R.drawable.ic_person_black_24dp, Icon.Visible);
×
1589
        builder.setMessage(mediatext);
×
1590
        if (mediatype.equalsIgnoreCase("image")) {
×
1591
            builder.setImagetoshow(medialink);
×
1592
        } else if (mediatype.equalsIgnoreCase("video")) {
×
1593
            builder.setVideopath(medialink);
×
1594
        }
1595
        builder.build();
×
1596
    }
×
1597

1598
    /**
1599
     * This method checks whether a constraint has been enforced and returns an error message if not The error message should
1600
     * be displayable to the user
1601
     *
1602
     * @param value      {@link String} The value to be checked
1603
     * @param view       {@link View} The value to be checked
1604
     * @param constraint {@link JSONObject} The constraint expression to use
1605
     * @return An error message if constraint has not been enforced or NULL if constraint enforced
1606
     * @throws Exception
1607
     */
1608
    private String enforceConstraint(String value, View view, JSONObject constraint) throws
1609
            Exception {
1610

1611
        String type = constraint.getString("type").toLowerCase();
×
1612
        String ex = constraint.getString(JsonFormConstants.EX);
×
1613
        String errorMessage = type.equals(JsonFormConstants.NUMBER_SELECTOR) ? constraint.optString(JsonFormConstants.ERR) :
×
1614
                constraint.getString(JsonFormConstants.ERR);
×
1615
        Pattern pattern = Pattern.compile("(" + functionRegex + ")\\((.*)\\)");
×
1616
        Matcher matcher = pattern.matcher(ex);
×
1617
        if (matcher.find()) {
×
1618
            String functionName = matcher.group(1);
×
1619
            String b = matcher.group(2);
×
1620
            String[] args = getFunctionArgs(b, value);
×
1621

1622
            boolean viewDoesNotHaveValue = TextUtils.isEmpty(value);
×
1623
            if (view instanceof CheckBox) {
×
1624
                viewDoesNotHaveValue = !((CheckBox) view).isChecked();
×
1625
            } else if (isNumberSelectorConstraint(view) || isDatePickerNativeRadio(view)) {
×
1626
                return args.length > 1 ? args[1] : "";//clever fix to pass back the max value for number selectors
×
1627

1628
            }
1629

1630
            if (checkViewValues(type, functionName, args, viewDoesNotHaveValue)) return null;
×
1631
        } else {
×
1632
            Timber.d("Matcher didn't work with function");
×
1633
        }
1634

1635
        return errorMessage;
×
1636
    }
1637

1638
    private boolean checkViewValues(String type, String functionName, String[] args,
1639
                                    boolean viewDoesNotHaveValue) {
1640
        return viewDoesNotHaveValue || TextUtils.isEmpty(args[0]) || TextUtils.isEmpty(args[1]) ||
×
1641
                comparisons.get(functionName).compare(args[0], type, args[1]);
×
1642
    }
1643

1644
    private String enforceConstraint(Facts curValueMap, JSONObject constraint) throws Exception {
1645
        return curValueMap.asMap().size() == 0 ? "0" : getRulesEngineFactory().getConstraint(curValueMap,
×
1646
                constraint.getJSONObject(JsonFormConstants.JSON_FORM_KEY.EX_RULES).getString(RuleConstant.RULES_FILE));
×
1647
    }
1648

1649
    private boolean isNumberSelectorConstraint(View view) {
1650
        return JsonFormConstants.NUMBER_SELECTOR.equals(view.getTag(R.id.type));
×
1651
    }
1652

1653
    private boolean isDatePickerNativeRadio(View view) {
1654
        return JsonFormConstants.NATIVE_RADIO_BUTTON.equals(view.getTag(R.id.type));
×
1655
    }
1656

1657
    protected JSONArray fetchFields(JSONObject parentJson, boolean popup) {
1658
        JSONArray fields = new JSONArray();
1✔
1659
        if (Utils.isEmptyJsonObject(parentJson)) {
1✔
1660
            return fields;
×
1661
        }
1662
        try {
1663
            if (parentJson.has(JsonFormConstants.SECTIONS) &&
1✔
1664
                    parentJson.get(JsonFormConstants.SECTIONS) instanceof JSONArray) {
×
1665
                JSONArray sections = parentJson.getJSONArray(JsonFormConstants.SECTIONS);
×
1666
                for (int i = 0; i < sections.length(); i++) {
×
1667
                    JSONObject sectionJson = sections.getJSONObject(i);
×
1668
                    fields = returnFormWithSectionFields(sectionJson, popup);
×
1669
                }
1670
            } else if (parentJson.has(JsonFormConstants.FIELDS) &&
1✔
1671
                    parentJson.get(JsonFormConstants.FIELDS) instanceof JSONArray) {
1✔
1672
                fields = returnWithFormFields(parentJson, popup);
1✔
1673
            }
1674
        } catch (JSONException e) {
×
1675
            Timber.e(e, "JsonFormActivity --> fetchFields");
×
1676
        }
1✔
1677

1678
        return fields;
1✔
1679
    }
1680

1681
    /**
1682
     * Get form fields from JSON forms that have sections in the form steps. The JSONObject {@link JSONObject} argument is
1683
     * the object after getting the section in the specified step name The popup {@link Boolean} argument is a boolean value
1684
     * to let the function know that the form is being executed on a popup and not the main android view.
1685
     * <p>
1686
     * This function returns a JSONArray {@link JSONArray} of the fields contained in the section for the given step
1687
     *
1688
     * @param sectionJson
1689
     * @param popup
1690
     * @return
1691
     * @throws JSONException
1692
     * @author dubdabasoduba
1693
     */
1694
    protected JSONArray returnFormWithSectionFields(JSONObject sectionJson, boolean popup) throws
1695
            JSONException {
1696
        JSONArray fields = new JSONArray();
×
1697
        if (sectionJson.has(JsonFormConstants.FIELDS)) {
×
1698
            if (popup) {
×
1699
                JSONArray jsonArray = sectionJson.getJSONArray(JsonFormConstants.FIELDS);
×
1700
                for (int k = 0; k < jsonArray.length(); k++) {
×
1701
                    JSONObject item = jsonArray.getJSONObject(k);
×
1702
                    if (genericDialogInterface != null &&
×
1703
                            item.getString(JsonFormConstants.KEY).equals(genericDialogInterface.getParentKey())) {
×
1704
                        fields = formUtils.concatArray(fields, specifyFields(item));
×
1705
                    }
1706
                }
1707
            } else {
×
1708
                fields = formUtils.concatArray(fields, sectionJson.getJSONArray(JsonFormConstants.FIELDS));
×
1709
            }
1710
        }
1711
        return fields;
×
1712
    }
1713

1714
    /**
1715
     * Get the form fields for the JSON forms that do not use the sections in the steps The JSONObject {@link JSONObject}
1716
     * argument is the object after getting the step name The popup {@link boolean} argument is a boolean value to let the
1717
     * function know that the form is being executed on a popup and not the main android view.
1718
     * <p>
1719
     * This function returns a JSONArray {@link JSONArray} of the fields contained in the step
1720
     *
1721
     * @param parentJson
1722
     * @param popup
1723
     * @return fields
1724
     * @throws JSONException
1725
     * @author dubdabasoduba
1726
     */
1727
    protected JSONArray returnWithFormFields(JSONObject parentJson, boolean popup) throws
1728
            JSONException {
1729
        JSONArray fields = new JSONArray();
1✔
1730
        if (popup) {
1✔
1731
            JSONArray jsonArray = parentJson.getJSONArray(JsonFormConstants.FIELDS);
×
1732
            for (int k = 0; k < jsonArray.length(); k++) {
×
1733
                JSONObject item = jsonArray.getJSONObject(k);
×
1734
                if (genericDialogInterface != null &&
×
1735
                        item.getString(JsonFormConstants.KEY).equals(genericDialogInterface.getParentKey())) {
×
1736
                    fields = specifyFields(item);
×
1737
                }
1738
            }
1739
        } else {
×
1740
            fields = parentJson.getJSONArray(JsonFormConstants.FIELDS);
1✔
1741
        }
1742

1743
        return fields;
1✔
1744
    }
1745

1746
    protected JSONArray specifyFields(JSONObject parentJson) {
1747
        JSONArray fields = new JSONArray();
×
1748
        if (genericDialogInterface != null && genericDialogInterface.getWidgetType() != null &&
×
1749
                genericDialogInterface.getWidgetType().equals(JsonFormConstants.EXPANSION_PANEL)) {
×
1750
            if (parentJson.has(JsonFormConstants.CONTENT_FORM)) {
×
1751
                fields = returnFields(parentJson);
×
1752
            }
1753
        } else {
1754
            if (parentJson.has(JsonFormConstants.HAS_EXTRA_REL)) {
×
1755
                String optionKey;
1756
                try {
1757
                    optionKey = (String) parentJson.get(JsonFormConstants.HAS_EXTRA_REL);
×
1758
                    JSONArray options = parentJson.getJSONArray(JsonFormConstants.OPTIONS_FIELD_NAME);
×
1759
                    if (options.length() > 0) {
×
1760
                        for (int j = 0; j < options.length(); j++) {
×
1761
                            JSONObject jsonObject = options.getJSONObject(j);
×
1762
                            String objectKey = (String) jsonObject.get(JsonFormConstants.KEY);
×
1763
                            if (objectKey.equals(optionKey) && jsonObject.has(JsonFormConstants.CONTENT_FORM)) {
×
1764
                                fields = returnFields(jsonObject);
×
1765
                            }
1766
                        }
1767
                    }
1768
                } catch (JSONException e) {
×
1769
                    Timber.e(e, "JsonFormActivity --> specifyFields");
×
1770
                }
×
1771
            }
1772
        }
1773
        return fields;
×
1774
    }
1775

1776
    private JSONArray returnFields(JSONObject jsonObject) {
1777
        JSONArray fields = new JSONArray();
×
1778
        try {
1779
            if (getExtraFieldsWithValues() != null) {
×
1780
                fields = getExtraFieldsWithValues();
×
1781
            } else {
1782
                String formLocation = jsonObject.has(JsonFormConstants.CONTENT_FORM_LOCATION) ? jsonObject.getString(JsonFormConstants.CONTENT_FORM_LOCATION) : "";
×
1783
                fields = getSubFormFields(jsonObject.get(JsonFormConstants.CONTENT_FORM).toString(), formLocation, fields);
×
1784
            }
1785
        } catch (JSONException e) {
×
1786
            Timber.e(e, "JsonFormActivity --> returnFields");
×
1787
        }
×
1788
        return fields;
×
1789
    }
1790

1791
    protected JSONArray getSubFormFields(String subFormName, String subFormLocation, JSONArray
1792
            fields) {
1793
        JSONArray fieldArray = new JSONArray();
1✔
1794
        JSONObject jsonObject = null;
1✔
1795
        try {
1796
            jsonObject = getSubForm(subFormName, subFormLocation, this, translateForm);
1✔
1797
        } catch (Exception e) {
×
1798
            Timber.e(e);
×
1799
        }
1✔
1800

1801
        if (jsonObject != null) {
1✔
1802
            try {
1803
                JSONArray jsonArray = jsonObject.getJSONArray(JsonFormConstants.CONTENT_FORM);
1✔
1804
                if (jsonArray != null && jsonArray.length() > 0) {
1✔
1805
                    fieldArray = formUtils.concatArray(fields, jsonArray);
1✔
1806
                }
1807
            } catch (JSONException e) {
×
1808
                Timber.e(e, "JsonFormActivity --> getSubFormFields");
×
1809
            }
1✔
1810
        }
1811

1812
        return fieldArray;
1✔
1813
    }
1814

1815
    public Form getForm() {
1816
        return form;
1✔
1817
    }
1818

1819
    private boolean isRelevant(Facts curValueMap, JSONObject curRelevance) throws Exception {
1820
        if (curRelevance != null) {
×
1821
            if (curRelevance.has(JsonFormConstants.JSON_FORM_KEY.EX_RULES)) {
×
1822

1823
                JSONObject exRulesObject = curRelevance.getJSONObject(JsonFormConstants.JSON_FORM_KEY.EX_RULES);
×
1824

1825
                if (exRulesObject.has(RuleConstant.RULES_FILE)) {
×
1826

1827
                    return curValueMap.asMap().size() != 0 && getRulesEngineFactory().getRelevance(curValueMap,
×
1828
                            exRulesObject.getString(RuleConstant.RULES_FILE));
×
1829

1830
                } else if (exRulesObject.has(RuleConstant.RULES_DYNAMIC)) {
×
1831

1832
                    return curValueMap.asMap().size() != 0 && getRulesEngineFactory()
×
1833
                            .getDynamicRelevance(curValueMap, exRulesObject.optJSONArray(RuleConstant.RULES_DYNAMIC));
×
1834

1835
                }
1836

1837
            } else if (curRelevance.has(JsonFormConstants.JSON_FORM_KEY.EX_CHECKBOX)) {
×
1838
                JSONArray exArray = curRelevance.getJSONArray(JsonFormConstants.JSON_FORM_KEY.EX_CHECKBOX);
×
1839

1840
                for (int i = 0; i < exArray.length(); i++) {
×
1841
                    ExObjectResult exObjectResult = isExObjectRelevant(curValueMap, exArray.getJSONObject(i));
×
1842
                    if (exObjectResult.isRelevant()) {
×
1843
                        return true;
×
1844
                    } else if (!exObjectResult.isRelevant() && exObjectResult.isFinal()) {
×
1845
                        return false;
×
1846
                    }
1847

1848
                }
1849
                return false;
×
1850
            } else {
1851
                Object currObjectValue = curValueMap.get(JsonFormConstants.VALUE);
×
1852
                if (currObjectValue != null) {
×
1853
                    String curValue = String.valueOf(currObjectValue);
×
1854
                    return doComparison(curValue, curRelevance);
×
1855
                }
1856
                return doComparison("", curRelevance);
×
1857
            }
1858
        }
1859
        return false;
×
1860
    }
1861

1862
    private ExObjectResult isExObjectRelevant(Facts curValueMap, JSONObject object) throws
1863
            Exception {
1864
        if (object.has(JsonFormConstants.JSON_FORM_KEY.NOT)) {
×
1865
            JSONArray orArray = object.getJSONArray(JsonFormConstants.JSON_FORM_KEY.NOT);
×
1866

1867
            for (int i = 0; i < orArray.length(); i++) {
×
1868
                String curValue = curValueMap.get(orArray.getString(i));
×
1869

1870
                if (curValue != null && !Boolean.valueOf(curValue)) {
×
1871
                    return new ExObjectResult(true, false);
×
1872
                } else {
1873
                    return new ExObjectResult(false, true);
×
1874
                }
1875
            }
1876
        }
1877

1878
        if (object.has(JsonFormConstants.JSON_FORM_KEY.OR)) {
×
1879
            JSONArray orArray = object.getJSONArray(JsonFormConstants.JSON_FORM_KEY.OR);
×
1880

1881
            for (int i = 0; i < orArray.length(); i++) {
×
1882
                String curValue = curValueMap.get(orArray.getString(i));
×
1883
                if (curValue != null && Boolean.valueOf(curValue)) {
×
1884
                    return new ExObjectResult(true, true);
×
1885
                }
1886

1887
            }
1888

1889
        }
1890

1891
        if (object.has(JsonFormConstants.JSON_FORM_KEY.AND)) {
×
1892
            JSONArray andArray = object.getJSONArray(JsonFormConstants.JSON_FORM_KEY.AND);
×
1893

1894
            for (int i = 0; i < andArray.length(); i++) {
×
1895
                String curValue = curValueMap.get(andArray.getString(i));
×
1896
                if (curValue != null && !Boolean.valueOf(curValue)) {
×
1897
                    return new ExObjectResult(false, false);
×
1898
                }
1899
            }
1900
            return new ExObjectResult(true, false);
×
1901

1902
        }
1903

1904
        return new ExObjectResult(false, false);
×
1905
    }
1906

1907
    private List<String> getRules(String filename, String fieldKey, boolean readAllRules) {
1908
        List<String> rules = ruleKeys.get(filename + ":" + fieldKey);
×
1909

1910
        if (rules == null) {
×
1911
            try {
1912
                Yaml yaml = new Yaml();
×
1913
                BufferedReader inputStreamReader = getRules(getApplicationContext(), getRulesEngineFactory().getRulesFolderPath() + filename);
×
1914
                Iterable<Object> ruleObjects = yaml.loadAll(inputStreamReader);
×
1915

1916
                for (Object object : ruleObjects) {
×
1917

1918
                    Map<String, Object> map = ((Map<String, Object>) object);
×
1919

1920
                    String name = map.get(RuleConstant.NAME).toString();
×
1921
                    if (ruleKeys.containsKey(filename + ":" + name)) {
×
1922
                        continue;
×
1923
                    }
1924

1925
                    List<String> actions = new ArrayList<>();
×
1926

1927
                    StringBuilder conditionString = new StringBuilder();
×
1928
                    conditionString.append(map.get(RuleConstant.CONDITION).toString());
×
1929

1930
                    List<String> fields = (List<String>) map.get(RuleConstant.ACTIONS);
×
1931
                    if (fields != null) {
×
1932
                        for (String field : fields) {
×
1933
                            if (field.trim().startsWith(RuleConstant.CALCULATION) ||
×
1934
                                    field.trim().startsWith(RuleConstant.CONSTRAINT)) {
×
1935
                                conditionString.append(" " + field);
×
1936
                            }
1937
                        }
×
1938

1939
                    }
1940

1941
                    actions.addAll(getConditionKeys(conditionString.toString()));
×
1942
                    ruleKeys.put(filename + ":" + name, actions);
×
1943

1944
                    if (!readAllRules && name.equals(fieldKey)) {
×
1945
                        break;
×
1946
                    }
1947
                }
×
1948

1949
            } catch (Exception e) {
×
1950
                Timber.e(e, "JsonFormActivity --> getRules");
×
1951
            }
×
1952

1953
            return ruleKeys.get(filename + ":" + fieldKey);
×
1954
        } else {
1955
            return rules;
×
1956
        }
1957
    }
1958

1959
    private List<String> getConditionKeys(String condition) {
1960
        return Utils.getConditionKeys(condition);
×
1961
    }
1962

1963
    private void updateUiByCalculation(@NonNull String calculationValue, final View view) {
1964
        String calculation = calculationValue;
×
1965
        if (view instanceof CheckBox) {
×
1966
            //For now were only handling checkbox titles only
1967
            TextView checkboxLabel = ((View) view.getParent().getParent()).findViewById(R.id.label_text);
×
1968
            if (checkboxLabel != null) {
×
1969
                checkboxLabel.setText(getRenderText(calculation, checkboxLabel.getTag(R.id.original_text).toString(), false));
×
1970
            }
1971

1972
        } else if (view instanceof TextableView) {
×
1973
            TextableView textView = ((TextableView) view);
×
1974
            if (!TextUtils.isEmpty(calculation)) {
×
1975
                CharSequence spanned = calculation.charAt(0) == '{' ? getRenderText(calculation, textView.getTag(R.id.original_text).toString(), true) :
×
1976
                        (textView.getTag(R.id.original_text) != null && "0".equals(calculation)) ? textView.getTag(R.id.original_text).toString() : calculation;
×
1977
                textView.setText(spanned);
×
1978
            }
1979
        } else if (view instanceof EditText) {
×
1980
            String type = (String) view.getTag(R.id.type);
×
1981
            if (JsonFormConstants.HIDDEN.equals(type) && TextUtils.isEmpty(calculation)) {
×
1982
                calculation = "0";
×
1983
            }
1984

1985
            if (!TextUtils.isEmpty(calculation)) {
×
1986
                final String finalCalculation = calculation;
×
1987
                getAppExecutors().mainThread().execute(new Runnable() {
×
1988
                    @Override
1989
                    public void run() {
1990
                        ((EditText) view).setText(finalCalculation);
×
1991
                    }
×
1992
                });
1993
            }
1994

1995
        } else if (view instanceof RadioGroup) {
×
1996
            setRadioButtonCalculation((RadioGroup) view, calculation);
×
1997

1998
        } else if (view instanceof LinearLayout) {
×
1999
            LinearLayout linearLayout = (LinearLayout) view;
×
2000
            String type = (String) linearLayout.getTag(R.id.type);
×
2001
            if (JsonFormConstants.NUMBER_SELECTOR.equals(type)) {
×
2002
                setNumberSelectorCalculation(calculation, linearLayout);
×
2003
            }
2004
            try {
2005
                View viewRadioGroup = linearLayout.getChildAt(0);
×
2006
                if (viewRadioGroup instanceof RadioGroup) {
×
2007
                    setRadioButtonCalculation((RadioGroup) viewRadioGroup, calculation);
×
2008
                }
2009
            } catch (IndexOutOfBoundsException e) {
×
2010
                Timber.e(e);
×
2011
            }
×
2012
        } else {
×
2013
            ((TextView) view).setText(calculation);
×
2014
        }
2015

2016
    }
×
2017

2018
    private void updateCalculation(Facts valueMap, View view, String[] address,
2019
                                   boolean isForNextStep) {
2020
        String calculation;
2021
        try {
2022
            if (address[0].equals(RuleConstant.RULES_DYNAMIC)) {
×
2023
                calculation = getRulesEngineFactory().getDynamicCalculation(valueMap, new JSONArray(address[1]));
×
2024
            } else {
2025
                calculation = getRulesEngineFactory().getCalculation(valueMap, address[1]);
×
2026
            }
2027

2028
            if (!isForNextStep) {
×
2029
                updateUiByCalculation(calculation, view);
×
2030
            }
2031

2032
        } catch (Exception e) {
×
2033
            Timber.e(e, "calling updateCalculation on Non TextView or Text View decendant");
×
2034
        }
×
2035

2036
    }
×
2037

2038
    private void setRadioButtonCalculation(final RadioGroup view, final String calculation) {
2039
        int count = view.getChildCount();
×
2040
        for (int i = 0; i < count; i++) {
×
2041
            final int childPosition = i;
×
2042
            getAppExecutors().mainThread().execute(new Runnable() {
×
2043
                @Override
2044
                public void run() {
2045
                    addRadioButtonCalculation(calculation, view, childPosition);
×
2046
                }
×
2047
            });
2048
        }
2049
    }
×
2050

2051
    private void addRadioButtonCalculation(String calculation, RadioGroup view,
2052
                                           int childPosition) {
2053
        if (!TextUtils.isEmpty(calculation)) {
×
2054
            RelativeLayout radioButtonLayout = (RelativeLayout) view.getChildAt(childPosition);
×
2055
            int radioButtonViewId = (int) radioButtonLayout.getTag(R.id.native_radio_button_view_id);
×
2056
            RadioButton radioButton = radioButtonLayout.findViewById(radioButtonViewId);
×
2057
            boolean showExtraInfo = (boolean) radioButton.getTag(R.id.native_radio_button_extra_info);
×
2058
            String radioButtonKey = (String) radioButton.getTag(R.id.childKey);
×
2059

2060
            if (!TextUtils.isEmpty(radioButtonKey) && calculation.equals(radioButtonKey)) {
×
2061
                radioButton.setChecked(true);
×
2062
                radioButton.performClick();
×
2063
            }
2064

2065
            if (showExtraInfo) {
×
2066
                CustomTextView renderView = view.getChildAt(childPosition).findViewById(R.id.extraInfoTextView);
×
2067

2068
                if (renderView.getTag(R.id.original_text) == null) {
×
2069
                    renderView.setTag(R.id.original_text, renderView.getText());
×
2070
                }
2071

2072

2073
                if (!TextUtils.isEmpty(calculation)) {
×
2074
                    renderView.setText(calculation.charAt(0) == '{' ? getRenderText(calculation, renderView.getTag(R.id.original_text).toString(), false) : calculation);
×
2075
                }
2076

2077
                renderView.setVisibility(renderView.getText().toString().contains("{") ||
×
2078
                        renderView.getText().toString().equals("0") ? View.GONE : View.VISIBLE);
×
2079
            }
2080
        }
2081
    }
×
2082

2083
    private void setNumberSelectorCalculation(String calculation, LinearLayout linearLayout) {
2084
        if (!TextUtils.isEmpty(calculation)) {
×
2085
            int childCount = linearLayout.getChildCount();
×
2086
            for (int i = 0; i < childCount; i++) {
×
2087
                if (linearLayout.getChildAt(i) instanceof TextView) {
×
2088
                    TextView textView = (TextView) linearLayout.getChildAt(i);
×
2089
                    String text = textView.getText().toString();
×
2090
                    CommonListener commonListener =
×
2091
                            (CommonListener) textView.getTag(R.id.number_selector_listener);
×
2092

2093
                    String selectedNumber = "";
×
2094
                    if (selectedTextView != null) {
×
2095
                        selectedNumber = selectedTextView.getText().toString();
×
2096
                    }
2097

2098
                    if (i + 1 == childCount) {
×
2099
                        String[] splitValue = text.split("");
×
2100
                        if (splitValue.length > 2) {
×
2101
                            String value = splitValue[1];
×
2102

2103
                            if (Integer.valueOf(calculation) > Integer.valueOf(value)) {
×
2104
                                if (!calculation.equals(selectedNumber)) {
×
2105
                                    textView.setText(calculation);
×
2106
                                    textView.setOnClickListener(commonListener);
×
2107
                                    textView.performClick();
×
2108
                                }
2109
                            } else {
2110
                                selectNumber(calculation, textView, value, commonListener);
×
2111
                            }
2112
                        }
2113
                    } else {
×
2114
                        selectNumber(calculation, textView, text, commonListener);
×
2115
                    }
2116
                }
2117
            }
2118
        }
2119
    }
×
2120

2121
    private void selectNumber(String calculation, TextView textView, String
2122
            text, CommonListener commonListener) {
2123
        if (calculation.equals(text) && !textView.equals(selectedTextView)) {
×
2124
            selectedTextView = textView;
×
2125
            textView.setOnClickListener(commonListener);
×
2126
            textView.performClick();
×
2127
        }
2128
    }
×
2129

2130
    private CharSequence getRenderText(String calculation, String textTemplate, boolean makeBold) {
2131
        Map<String, Object> valueMap = new Gson().fromJson(calculation, new TypeToken<HashMap<String, Object>>() {
×
2132
        }.getType());
×
2133
        return stringFormat(textTemplate, valueMap, makeBold);
×
2134
    }
2135

2136
    public Spanned stringFormat(String string, Map<String, Object> valueMap, boolean makeBold) {
2137
        String resString = string;
1✔
2138
        for (Map.Entry<String, Object> entry : valueMap.entrySet()) {
1✔
2139
            String templateValue = getTemplateValue(entry.getValue());
1✔
2140
            if (makeBold) {
1✔
2141
                templateValue = "<b>" + getTemplateValue(entry.getValue()) + "</b>";
1✔
2142
            }
2143
            resString = resString.replace("{" + entry.getKey() + "}", templateValue);
1✔
2144
        }
1✔
2145

2146
        return Html.fromHtml(resString);
1✔
2147
    }
2148

2149
    protected Object getValue(JSONObject object) throws JSONException {
2150
        Object value;
2151

2152
        if (object.has(JsonFormConstants.VALUE)) {
1✔
2153
            value = Utils.getValueAfterTranslation(object);
×
2154
            if (isNumberWidget(object)) {
×
2155
                value = TextUtils.isEmpty(object.optString(JsonFormConstants.VALUE)) ? 0 : processNumberValues(object.optString(JsonFormConstants.VALUE));
×
2156
            } else if (value != null && !TextUtils.isEmpty(object.getString(JsonFormConstants.VALUE)) && canHaveNumber(object)) {
×
2157
                value = processNumberValues(value);
×
2158
            }
2159

2160
        } else {
2161
            value = isNumberWidget(object) ? 0 : "";
1✔
2162
        }
2163

2164
        return value;
1✔
2165
    }
2166

2167
    protected Object processNumberValues(Object object) {
2168
        Object jsonObject = object;
×
2169
        try {
2170
            if (jsonObject.toString().contains(".")) {
×
2171
                jsonObject = String.valueOf((float) Math.round(Float.valueOf(jsonObject.toString()) * 100) / 100);
×
2172
            } else {
2173
                jsonObject = Integer.valueOf(jsonObject.toString());
×
2174
            }
2175
        } catch (NumberFormatException e) {
×
2176
            //Log.e(TAG, "Error trying to convert " + object + " to a number ", e);
2177
        }
×
2178
        return jsonObject;
×
2179
    }
2180

2181
    protected String getKey(JSONObject object) throws JSONException {
2182
        return object.has(RuleConstant.IS_RULE_CHECK) && object.getBoolean(RuleConstant.IS_RULE_CHECK) ?
1✔
2183
                object.get(RuleConstant.STEP) + "_" + object.get(JsonFormConstants.KEY) : JsonFormConstants.VALUE;
1✔
2184
    }
2185

2186
    private void clearHiddenViewsValues(JSONObject object, String addressString) {
2187
        if (object != null) {
1✔
2188
            String objectKey = addressString.replace(":", "_");
1✔
2189
            formValuesCacheMap.remove(objectKey);
1✔
2190
            formValuesCacheMap.put(objectKey, "");
1✔
2191
            if (object.has(JsonFormConstants.VALUE)) {
1✔
2192
                object.remove(JsonFormConstants.VALUE);
×
2193
            }
2194
        }
2195
    }
1✔
2196

2197
    private void updateCanvas(View view, boolean visible, JSONArray canvasViewIds, String
2198
            addressString, JSONObject object)
2199
            throws JSONException {
2200
        for (int i = 0; i < canvasViewIds.length(); i++) {
1✔
2201
            int curId = canvasViewIds.getInt(i);
1✔
2202
            View curCanvasView = view.getRootView().findViewById(curId);
1✔
2203

2204
            if (curCanvasView == null) {
1✔
2205
                continue;
×
2206
            }
2207

2208
            if (visible) {
1✔
2209
                curCanvasView.setEnabled(true);
1✔
2210
                curCanvasView.setVisibility(View.VISIBLE);
1✔
2211

2212
                if (curCanvasView instanceof RelativeLayout || view instanceof LinearLayout) {
1✔
2213
                    curCanvasView.setFocusable(true);
×
2214
                }
2215
                if (view instanceof EditText) {
1✔
2216
                    view.setFocusable(true);
1✔
2217
                }
2218

2219
                curCanvasView.invalidate();
1✔
2220
            } else {
2221
                if (!JsonFormConstants.REPEATING_GROUP.contains(object.optString(JsonFormConstants.TYPE))) {
1✔
2222
                    clearHiddenViewsValues(object, addressString);
1✔
2223
                }
2224
                curCanvasView.setEnabled(false);
1✔
2225
                curCanvasView.setVisibility(View.GONE);
1✔
2226
                refreshViews(curCanvasView);
1✔
2227
            }
2228

2229
            curCanvasView.setTag(R.id.relevance_decided, visible);
1✔
2230

2231
            if (object != null) {
1✔
2232
                object.put(JsonFormConstants.IS_VISIBLE, visible);
1✔
2233
                //Only keep track of required fields that are invisible
2234
                if (object.has(JsonFormConstants.V_REQUIRED) && object.getJSONObject(JsonFormConstants.V_REQUIRED)
1✔
2235
                        .getBoolean(JsonFormConstants.VALUE)) {
1✔
2236
                    trackInvisibleFields(object, visible);
1✔
2237
                }
2238
            }
2239
        }
2240
    }
1✔
2241

2242
    @SuppressLint("StaticFieldLeak")
2243
    private void trackInvisibleFields(final JSONObject object, final boolean visible) {
2244
        new AsyncTask<Void, Void, Void>() {
1✔
2245
            @Override
2246
            protected Void doInBackground(Void... voids) {
2247
                try {
2248
                    synchronized (invisibleRequiredFields) {
1✔
2249
                        if (visible) {
1✔
2250
                            invisibleRequiredFields.remove(object.getString(JsonFormConstants.KEY));
1✔
2251
                        } else {
2252
                            invisibleRequiredFields.add(object.getString(JsonFormConstants.KEY));
1✔
2253
                        }
2254
                        getmJSONObject().put(JsonFormConstants.INVISIBLE_REQUIRED_FIELDS, invisibleRequiredFields);
1✔
2255
                    }
1✔
2256
                } catch (JSONException e) {
×
2257
                    Timber.e(e);
×
2258
                }
1✔
2259
                return null;
1✔
2260
            }
2261
        }.execute();
1✔
2262
    }
1✔
2263

2264
    private void refreshViews(View childElement) {
2265
        if (childElement instanceof ViewGroup) {
1✔
2266
            childElement.setFocusable(true);
×
2267
            ViewGroup group = (ViewGroup) childElement;
×
2268
            refreshNumberSelector(group);
×
2269
            for (int id = 0; id < group.getChildCount(); id++) {
×
2270
                View child = group.getChildAt(id);
×
2271
                if (child instanceof Spinner) {
×
2272
                    ((Spinner) child).setSelection(0);
×
2273
                }
2274
                if (child instanceof CheckBox) {
×
2275
                    ((CheckBox) child).setChecked(false);
×
2276
                } else if (child instanceof RadioButton) {
×
2277
                    ((RadioButton) child).setChecked(false);
×
2278
                } else if (child instanceof EditText) {
×
2279
                    EditText editText = (EditText) child;
×
2280
                    if (!TextUtils.isEmpty(editText.getText().toString())) {
×
2281
                        editText.setText("");
×
2282
                    }
2283
                } else if (child instanceof MaterialSpinner) {
×
2284
                    MaterialSpinner spinner = (MaterialSpinner) child;
×
2285
                    spinner.setSelected(false);
×
2286
                } else if (child instanceof CustomTextView) {
×
2287
                    resetSelectedNumberBackground(child);
×
2288

2289
                } else if (child instanceof TextView && child.getId() == R.id.duration) {
×
2290
                    // clear duration for custom date picker
2291
                    ((TextView) child).setText("");
×
2292
                }
2293
                refreshViews(group.getChildAt(id));
×
2294
            }
2295
        }
2296
    }
1✔
2297

2298
    private void refreshNumberSelector(View group) {
2299
        //reset value for number selector linear layout
2300
        if (group instanceof LinearLayout) {
×
2301
            LinearLayout numSelectorLayout = (LinearLayout) group;
×
2302
            if (numSelectorLayout.getTag(R.id.is_number_selector_linear_layout) != null &&
×
2303
                    Boolean.TRUE.equals(numSelectorLayout.getTag(R.id.is_number_selector_linear_layout))) {
×
2304
                numSelectorLayout.setTag(R.id.selected_number_value, null);
×
2305
            }
2306
        }
2307
    }
×
2308

2309
    /**
2310
     * Resets the background of the selected text in number selector
2311
     *
2312
     * @param child Selected textview
2313
     */
2314
    private void resetSelectedNumberBackground(View child) {
2315
        Drawable background = child.getBackground();
×
2316
        if (background instanceof ColorDrawable) {
×
2317
            int color = ((ColorDrawable) background).getColor();
×
2318
            if (color == child.getContext().getResources().getColor(R.color.native_number_selector_selected)) {
×
2319
                child.setBackgroundColor(child.getContext().getResources().getColor(R.color.native_number_selector));
×
2320
            }
2321
        } else if (background instanceof GradientDrawable) {
×
2322
            ((GradientDrawable) background)
×
2323
                    .setColor(child.getContext().getResources().getColor(R.color.native_number_selector));
×
2324
            child.setBackground(background);
×
2325
        }
2326
        ((CustomTextView) child).setTextColor(child.getContext().getResources().getColor(R.color.primary_text));
×
2327
    }
×
2328

2329
    public JSONArray getExtraFieldsWithValues() {
2330
        return extraFieldsWithValues;
×
2331
    }
2332

2333
    public void setExtraFieldsWithValues(JSONArray extraFieldsWithValues) {
2334
        this.extraFieldsWithValues = extraFieldsWithValues;
×
2335
    }
×
2336

2337
    @Override
2338
    protected void onStart() {
2339
        super.onStart();
1✔
2340
        for (LifeCycleListener lifeCycleListener : lifeCycleListeners) {
1✔
2341
            lifeCycleListener.onStart();
×
2342
        }
×
2343
    }
1✔
2344

2345
    @Override
2346
    protected void onStop() {
2347
        super.onStop();
×
2348
        for (LifeCycleListener lifeCycleListener : lifeCycleListeners) {
×
2349
            lifeCycleListener.onStop();
×
2350
        }
×
2351
    }
×
2352

2353
    @Override
2354
    protected void onDestroy() {
2355
        super.onDestroy();
×
2356
        for (LifeCycleListener lifeCycleListener : lifeCycleListeners) {
×
2357
            lifeCycleListener.onDestroy();
×
2358
        }
×
2359
    }
×
2360

2361
    @Override
2362
    public void onSaveInstanceState(Bundle outState) {
2363
        super.onSaveInstanceState(outState);
×
2364
        outState.putString(JSON_STATE, mJSONObject.toString());
×
2365
        outState.putSerializable(FORM_STATE, form);
×
2366
        for (LifeCycleListener lifeCycleListener : lifeCycleListeners) {
×
2367
            lifeCycleListener.onSaveInstanceState(outState);
×
2368
        }
×
2369
    }
×
2370

2371
    private String getTemplateValue(Object object) {
2372
        StringBuilder result = new StringBuilder();
1✔
2373
        if (object instanceof List) {
1✔
2374
            List<String> valueList = (List<String>) object;
1✔
2375
            for (int i = 0; i < valueList.size(); i++) {
1✔
2376
                result.append(valueList.get(i));
1✔
2377
                if (i != (valueList.size() - 1)) {
1✔
2378
                    result.append(", ");
1✔
2379
                }
2380
            }
2381
        } else {
1✔
2382
            result.append(
1✔
2383
                    object.toString().contains(".0") ? object.toString().substring(0, object.toString().indexOf(".0")) :
1✔
2384
                            object.toString()); //Fix automatic conversion float bug
1✔
2385
        }
2386

2387
        return result.toString();
1✔
2388
    }
2389

2390
    public LocalBroadcastManager getLocalBroadcastManager() {
2391
        return localBroadcastManager;
×
2392
    }
2393

2394
    private void cacheFormMapValues(String stepName, String parentKey, String childKey, String
2395
            value) {
2396
        formValuesCacheMap.put(stepName + "_" + (parentKey != null ? parentKey + "_" : "") + childKey, value);
1✔
2397
    }
1✔
2398

2399
    private boolean invokeRefreshLogic(String stepName, String parentKey, String
2400
            childKey, String value) {
2401
        String oldValue = formValuesCacheMap.get(stepName + "_" + (parentKey != null ? parentKey + "_" : "") + childKey);
1✔
2402

2403
        return !value.equals(oldValue);
1✔
2404

2405
    }
2406

2407
    protected boolean canHaveNumber(JSONObject object) throws JSONException {
2408
        return isNumberWidget(object) || object.getString(JsonFormConstants.TYPE).equals(JsonFormConstants.HIDDEN) ||
×
2409
                object.getString(JsonFormConstants.TYPE).equals(JsonFormConstants.SPINNER);
×
2410
    }
2411

2412
    protected boolean isNumberWidget(JSONObject object) throws JSONException {
2413
        return object.has(JsonFormConstants.EDIT_TYPE) &&
1✔
2414
                object.getString(JsonFormConstants.EDIT_TYPE).equals(JsonFormConstants.EDIT_TEXT_TYPE.NUMBER) ||
1✔
2415
                object.getString(JsonFormConstants.TYPE).equals(JsonFormConstants.NUMBER_SELECTOR);
1✔
2416
    }
2417

2418
    @Subscribe(threadMode = ThreadMode.ASYNC)
2419
    public void refreshExpansionPanel(RefreshExpansionPanelEvent refreshExpansionPanelEvent) {
2420
        if (refreshExpansionPanelEvent != null) {
1✔
2421
            try {
2422
                final List<String> values = getExpansionPanelValues(refreshExpansionPanelEvent);
1✔
2423
                final LinearLayout linearLayout = refreshExpansionPanelEvent.getLinearLayout();
1✔
2424
                getAppExecutors().mainThread().execute(new Runnable() {
1✔
2425
                    @Override
2426
                    public void run() {
2427
                        utils.enableExpansionPanelViews(linearLayout);
1✔
2428
                    }
1✔
2429
                });
2430

2431
                RelativeLayout layoutHeader = (RelativeLayout) linearLayout.getChildAt(0);
1✔
2432
                final ImageView status = layoutHeader.findViewById(R.id.statusImageView);
1✔
2433

2434
                getAppExecutors().mainThread().execute(new Runnable() {
1✔
2435
                    @Override
2436
                    public void run() {
2437
                        try {
2438
                            formUtils.updateExpansionPanelRecyclerView(values, status, getApplicationContext());
1✔
2439
                        } catch (JSONException e) {
×
2440
                            Timber.e(e);
×
2441
                        }
1✔
2442

2443
                    }
1✔
2444
                });
2445

2446
                final LinearLayout contentLayout = (LinearLayout) linearLayout.getChildAt(1);
1✔
2447
                final LinearLayout mainContentView = contentLayout.findViewById(R.id.contentView);
1✔
2448

2449
                getAppExecutors().mainThread().execute(new Runnable() {
1✔
2450
                    @Override
2451
                    public void run() {
2452
                        formUtils.addValuesDisplay(values, mainContentView, getApplicationContext());
1✔
2453
                    }
1✔
2454
                });
2455

2456
                final LinearLayout buttonLayout = contentLayout.findViewById(R.id.accordion_bottom_navigation);
1✔
2457
                final Button undoButton = buttonLayout.findViewById(R.id.undo_button);
1✔
2458
                getAppExecutors().mainThread().execute(new Runnable() {
1✔
2459
                    @Override
2460
                    public void run() {
2461
                        if (values.size() > 0) {
1✔
2462
                            undoButton.setVisibility(View.VISIBLE);
1✔
2463
                            contentLayout.setVisibility(View.VISIBLE);
1✔
2464
                            buttonLayout.setVisibility(View.VISIBLE);
1✔
2465
                        } else {
2466
                            undoButton.setVisibility(View.GONE);
×
2467
                            contentLayout.setVisibility(View.GONE);
×
2468
                            buttonLayout.setVisibility(View.GONE);
×
2469
                            status.setImageDrawable(JsonFormActivity.this.getResources().getDrawable(R.drawable.icon_task_256));
×
2470
                        }
2471
                    }
1✔
2472
                });
2473

2474

2475
            } catch (JSONException e) {
×
2476
                Timber.e(e, "JsonFormActivity --> refreshExpansionPanel");
×
2477
            }
1✔
2478
        }
2479

2480
    }
1✔
2481

2482
    /**
2483
     * Get the expansion panel values from the Refresh Expansion panel event {@link RefreshExpansionPanelEvent}
2484
     *
2485
     * @param refreshExpansionPanelEvent {@link RefreshExpansionPanelEvent}
2486
     * @return values {@link List<String>}
2487
     * @throws JSONException
2488
     */
2489
    private List<String> getExpansionPanelValues(RefreshExpansionPanelEvent
2490
                                                         refreshExpansionPanelEvent) throws JSONException {
2491
        List<String> values;
2492
        if (refreshExpansionPanelEvent.getValues() != null) {
1✔
2493
            values = utils.createExpansionPanelChildren(refreshExpansionPanelEvent.getValues());
1✔
2494
        } else {
2495
            values = new ArrayList<>();
×
2496
        }
2497
        return values;
1✔
2498
    }
2499

2500
    @Override
2501
    public AppExecutors getAppExecutors() {
2502
        return appExecutors;
1✔
2503
    }
2504

2505
    @Override
2506
    public Map<String, Boolean> stepSkipLogicPresenceMap() {
2507
        return stepSkipLogicPresenceMap;
1✔
2508
    }
2509

2510
    @Override
2511
    public boolean isNextStepRelevant() {
2512
        return isNextStepRelevant;
×
2513
    }
2514

2515
    public void setNextStepRelevant(boolean nextStepRelevant) {
2516
        isNextStepRelevant = nextStepRelevant;
×
2517
    }
×
2518

2519
    @Override
2520
    public String nextStep() {
2521
        return nextStep;
1✔
2522
    }
2523

2524
    @Override
2525
    public void setNextStep(String nextStep) {
2526
        this.nextStep = nextStep;
1✔
2527
    }
1✔
2528

2529
    @Override
2530
    public Map<String, JSONObject> getFormFieldsMap() {
2531
        return formFields;
1✔
2532
    }
2533

2534
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc