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

opensrp / opensrp-client-native-form / #57

12 Sep 2023 07:43AM UTC coverage: 66.18% (+0.2%) from 66.013%
#57

push

github-actions

web-flow
Merge pull request #667 from opensrp/fix_tech_debt_660

Add tests for setUpOptiBpActivityResultListener

8432 of 12741 relevant lines covered (66.18%)

0.66 hits per line

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

81.03
android-json-form-wizard/src/main/java/com/vijay/jsonwizard/widgets/DatePickerFactory.java
1
package com.vijay.jsonwizard.widgets;
2

3
import android.app.Activity;
4
import android.app.Fragment;
5
import android.app.FragmentTransaction;
6
import android.content.Context;
7
import android.os.Build;
8
import android.os.Bundle;
9
import androidx.annotation.NonNull;
10
import androidx.annotation.VisibleForTesting;
11
import android.view.LayoutInflater;
12
import android.view.View;
13
import android.widget.DatePicker;
14
import android.widget.ImageView;
15
import android.widget.RelativeLayout;
16
import android.widget.TextView;
17

18
import com.rengwuxian.materialedittext.MaterialEditText;
19
import com.rey.material.util.ViewUtil;
20
import com.vijay.jsonwizard.R;
21
import com.vijay.jsonwizard.constants.JsonFormConstants;
22
import com.vijay.jsonwizard.customviews.DatePickerDialog;
23
import com.vijay.jsonwizard.customviews.GenericTextWatcher;
24
import com.vijay.jsonwizard.domain.Form;
25
import com.vijay.jsonwizard.fragments.JsonFormFragment;
26
import com.vijay.jsonwizard.interfaces.CommonListener;
27
import com.vijay.jsonwizard.interfaces.FormWidgetFactory;
28
import com.vijay.jsonwizard.interfaces.JsonApi;
29
import com.vijay.jsonwizard.utils.DateUtil;
30
import com.vijay.jsonwizard.utils.FormUtils;
31
import com.vijay.jsonwizard.utils.NativeFormLangUtils;
32
import com.vijay.jsonwizard.utils.NativeFormsProperties;
33
import com.vijay.jsonwizard.utils.Utils;
34
import com.vijay.jsonwizard.validators.edittext.RequiredValidator;
35

36
import org.apache.commons.lang3.StringUtils;
37
import org.jetbrains.annotations.NotNull;
38
import org.json.JSONArray;
39
import org.json.JSONException;
40
import org.json.JSONObject;
41

42
import java.text.SimpleDateFormat;
43
import java.util.ArrayList;
44
import java.util.Calendar;
45
import java.util.HashSet;
46
import java.util.List;
47
import java.util.Locale;
48
import java.util.Set;
49

50
import timber.log.Timber;
51

52
/**
53
 * @author Jason Rogena - jrogena@ona.io
54
 * @since 25/01/2017
55
 */
56
public class DatePickerFactory implements FormWidgetFactory {
1✔
57
    public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy");
1✔
58
    public static final String DATE_FORMAT_REGEX = "(^(((0[1-9]|1[0-9]|2[0-8])[-](0[1-9]|1[012]))|((29|30|31)[-](0[13578]|1[02]))|((29|30)[-](0[4,6,9]|11)))[-](19|[2-9][0-9])\\d\\d$)|(^29[-]02[-](19|[2-9][0-9])(00|04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)$)|\\s*";
59
    public static final SimpleDateFormat DATE_FORMAT_LOCALE_INDEPENDENT = new SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH);
1✔
60
    private static final String TAG = "DatePickerFactory";
61
    private FormUtils formUtils = new FormUtils();
1✔
62

63

64
    @VisibleForTesting
65
    protected void showDatePickerDialog(Activity context, DatePickerDialog datePickerDialog, MaterialEditText editText) {
66

67
        FragmentTransaction ft = context.getFragmentManager().beginTransaction();
1✔
68
        Fragment prev = context.getFragmentManager().findFragmentByTag(TAG);
1✔
69

70
        if (!(prev != null && prev.isAdded())) {
1✔
71

72
            datePickerDialog.show(ft, TAG);
1✔
73

74
            //Fragments are committed asynchronously, force commit
75
            context.getFragmentManager().executePendingTransactions();
1✔
76

77
            String text = editText.getText().toString();
1✔
78
            Calendar date = FormUtils.getDate(StringUtils.isNoneBlank(Form.getDatePickerDisplayFormat()) ?
1✔
79
                    Utils.formatDateToPattern(text, Form.getDatePickerDisplayFormat(), DATE_FORMAT.toPattern())
×
80
                    : text);
1✔
81
            if (text.isEmpty()) {
1✔
82
                Object defaultValue = datePickerDialog.getArguments().get(JsonFormConstants.DEFAULT);
×
83
                if (defaultValue != null)
×
84
                    datePickerDialog.setDate(FormUtils.getDate(defaultValue.toString()).getTime());
×
85
                else
86
                    datePickerDialog.setDate(date.getTime());
×
87
            } else {
×
88
                datePickerDialog.setDate(date.getTime());
1✔
89
            }
90
        }
91
    }
1✔
92

93

94
    private void updateDateText(Context context, final MaterialEditText editText, final TextView duration, final String date) {
95
        ((JsonApi) context).getAppExecutors().mainThread().execute(new Runnable() {
1✔
96
            @Override
97
            public void run() {
98
                editText.setText(StringUtils.isNoneBlank(Form.getDatePickerDisplayFormat()) ?
1✔
99
                        Utils.formatDateToPattern(date, DATE_FORMAT.toPattern(), Form.getDatePickerDisplayFormat())
×
100
                        : date);
1✔
101
            }
1✔
102
        });
103

104
        String durationLabel = (String) duration.getTag(R.id.label);
1✔
105
        if (StringUtils.isNotBlank(durationLabel)) {
1✔
106
            Locale locale = getSetLanguage(context);
1✔
107
            String durationText = getDurationText(context, date, locale);
1✔
108
            if (StringUtils.isNotBlank(durationText)) {
1✔
109
                durationText = String.format("(%s: %s)", durationLabel, durationText);
1✔
110
            }
111
            final String finalDurationText = durationText;
1✔
112
            ((JsonApi) context).getAppExecutors().mainThread().execute(new Runnable() {
1✔
113
                @Override
114
                public void run() {
115
                    duration.setText(finalDurationText);
1✔
116
                }
1✔
117
            });
118
        }
119
    }
1✔
120

121
    @NotNull
122
    @VisibleForTesting
123
    protected String getDurationText(Context context, String date, Locale locale) {
124
        return DateUtil.getDuration(DateUtil.getDurationTimeDifference(date, null), locale.getLanguage().equals("ar") ? Locale.ENGLISH : locale, context);
1✔
125
    }
126

127
    @NotNull
128
    @VisibleForTesting
129
    protected Locale getSetLanguage(Context context) {
130
        return new Locale(NativeFormLangUtils.getLanguage(context));
×
131
    }
132

133
    @Override
134
    public List<View> getViewsFromJson(String stepName, Context context, JsonFormFragment formFragment,
135
                                       JSONObject jsonObject,
136
                                       CommonListener listener, boolean popup) {
137
        return attachJson(stepName, context, formFragment, jsonObject, popup, listener);
×
138
    }
139

140
    @Override
141
    public List<View> getViewsFromJson(String stepName, Context context, JsonFormFragment formFragment,
142
                                       JSONObject jsonObject, CommonListener listener) throws Exception {
143
        return attachJson(stepName, context, formFragment, jsonObject, false, listener);
1✔
144
    }
145

146
    @Override
147
    @NonNull
148
    public Set<String> getCustomTranslatableWidgetFields() {
149
        Set<String> customTranslatableWidgetFields = new HashSet<>();
1✔
150
        customTranslatableWidgetFields.add(DatePickerFactory.KEY.DURATION + "." + JsonFormConstants.LABEL);
1✔
151
        customTranslatableWidgetFields.add(JsonFormConstants.LABEL_INFO_TITLE);
1✔
152
        customTranslatableWidgetFields.add(JsonFormConstants.LABEL_INFO_TEXT);
1✔
153
        return customTranslatableWidgetFields;
1✔
154
    }
155

156
    protected List<View> attachJson(String stepName, Context context, JsonFormFragment formFragment, JSONObject jsonObject,
157
                                    boolean popup, CommonListener listener) {
158
        List<View> views = new ArrayList<>(1);
1✔
159
        try {
160

161
            RelativeLayout dateViewRelativeLayout = getRelativeLayout(context);
1✔
162
            MaterialEditText editText = dateViewRelativeLayout.findViewById(R.id.edit_text);
1✔
163
            TextView duration = dateViewRelativeLayout.findViewById(R.id.duration);
1✔
164

165
            attachLayout(stepName, context, formFragment, jsonObject, editText, duration);
1✔
166

167
            JSONArray canvasIds = new JSONArray();
1✔
168
            dateViewRelativeLayout.setId(ViewUtil.generateViewId());
1✔
169
            canvasIds.put(dateViewRelativeLayout.getId());
1✔
170
            editText.setTag(R.id.canvas_ids, canvasIds.toString());
1✔
171
            editText.setTag(R.id.extraPopup, popup);
1✔
172

173
            ((JsonApi) context).addFormDataView(editText);
1✔
174
            views.add(dateViewRelativeLayout);
1✔
175
            attachInfoIcon(stepName, jsonObject, dateViewRelativeLayout, canvasIds, listener);
1✔
176

177
        } catch (Exception e) {
×
178
            e.printStackTrace();
×
179
        }
1✔
180

181
        return views;
1✔
182
    }
183

184
    @VisibleForTesting
185
    protected RelativeLayout getRelativeLayout(Context context) {
186
        return (RelativeLayout) LayoutInflater.from(context).inflate(getLayout(), null);
×
187
    }
188

189
    protected void attachLayout(String stepName, final Context context, JsonFormFragment formFragment, JSONObject jsonObject,
190
                                final MaterialEditText editText, final TextView duration) {
191

192
        try {
193
            String openMrsEntityParent = jsonObject.getString(JsonFormConstants.OPENMRS_ENTITY_PARENT);
1✔
194
            String openMrsEntity = jsonObject.getString(JsonFormConstants.OPENMRS_ENTITY);
1✔
195
            String openMrsEntityId = jsonObject.getString(JsonFormConstants.OPENMRS_ENTITY_ID);
1✔
196
            String relevance = jsonObject.optString(JsonFormConstants.RELEVANCE);
1✔
197
            String constraints = jsonObject.optString(JsonFormConstants.CONSTRAINTS);
1✔
198
            String calculations = jsonObject.optString(JsonFormConstants.CALCULATION);
1✔
199

200
            duration.setTag(R.id.key, jsonObject.getString(KEY.KEY));
1✔
201
            duration.setTag(R.id.type, jsonObject.getString(JsonFormConstants.TYPE));
1✔
202
            duration.setTag(R.id.openmrs_entity_parent, openMrsEntityParent);
1✔
203
            duration.setTag(R.id.openmrs_entity, openMrsEntity);
1✔
204
            duration.setTag(R.id.openmrs_entity_id, openMrsEntityId);
1✔
205
            if (jsonObject.has(DatePickerFactory.KEY.DURATION) && jsonObject.getJSONObject(DatePickerFactory.KEY.DURATION) != null) {
1✔
206
                duration.setTag(R.id.label, jsonObject.getJSONObject(DatePickerFactory.KEY.DURATION).getString(JsonFormConstants.LABEL));
1✔
207
            }
208

209
            updateEditText(editText, jsonObject, stepName, context, duration);
1✔
210
            editText.setTag(R.id.json_object, jsonObject);
1✔
211

212
            final DatePickerDialog datePickerDialog = createDateDialog(context, duration, editText, jsonObject);
1✔
213
            if (formFragment != null) {
1✔
214
                NativeFormsProperties nativeFormsProperties = JsonFormFragment.getNativeFormProperties();
1✔
215
                if (nativeFormsProperties != null) {
1✔
216
                    datePickerDialog.setNumericDatePicker(nativeFormsProperties.isTrue(NativeFormsProperties.KEY.WIDGET_DATEPICKER_IS_NUMERIC));
1✔
217
                }
218
            }
219

220
            if (jsonObject.has(JsonFormConstants.EXPANDED) && jsonObject.getBoolean(JsonFormConstants.EXPANDED)
1✔
221
                    && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
222
                datePickerDialog.setCalendarViewShown(true);
×
223
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
1✔
224
                datePickerDialog.setCalendarViewShown(false);
1✔
225
            }
226

227
            GenericTextWatcher genericTextWatcher = getGenericTextWatcher(stepName, (Activity) context, formFragment,
1✔
228
                    editText, datePickerDialog);
229
            editText.addTextChangedListener(genericTextWatcher);
1✔
230
            addRefreshLogicView(context, editText, relevance, constraints, calculations);
1✔
231
            Bundle datePickerArgs = new Bundle();
1✔
232
            datePickerArgs.putString(JsonFormConstants.DEFAULT, jsonObject.optString(JsonFormConstants.DEFAULT));
1✔
233
            datePickerDialog.setArguments(datePickerArgs);
1✔
234
            editText.setOnClickListener(new View.OnClickListener() {
1✔
235
                @Override
236
                public void onClick(View v) {
237
                    showDatePickerDialog((Activity) context, datePickerDialog, editText);
1✔
238
                }
1✔
239
            });
240

241
            editText.setOnLongClickListener(new View.OnLongClickListener() {
1✔
242
                @Override
243
                public boolean onLongClick(View v) {
244
                    updateDateText(context, editText, duration, "");
×
245
                    return true;
×
246
                }
247
            });
248
            editText.setFocusable(false);
1✔
249
        } catch (Exception e) {
×
250
            Timber.e(e.getMessage(), TAG);
×
251
        }
1✔
252

253
    }
1✔
254

255
    private void attachInfoIcon(String stepName, JSONObject jsonObject, RelativeLayout rootLayout, JSONArray canvasIds,
256
                                CommonListener listener) throws JSONException {
257
        if (jsonObject.has(JsonFormConstants.LABEL_INFO_TEXT)) {
1✔
258
            ImageView infoIcon = rootLayout.findViewById(R.id.date_picker_info_icon);
1✔
259
            formUtils.showInfoIcon(stepName, jsonObject, listener, FormUtils.getInfoDialogAttributes(jsonObject), infoIcon, canvasIds);
1✔
260
        }
261

262
    }
1✔
263

264
    @NonNull
265
    private GenericTextWatcher getGenericTextWatcher(String stepName, final Activity context, JsonFormFragment formFragment,
266
                                                     final MaterialEditText editText,
267
                                                     final DatePickerDialog datePickerDialog) {
268
        GenericTextWatcher genericTextWatcher = new GenericTextWatcher(stepName, formFragment, editText);
1✔
269
        genericTextWatcher.addOnFocusChangeListener(new View.OnFocusChangeListener() {
1✔
270
            @Override
271
            public void onFocusChange(View v, boolean hasFocus) {
272
                if (hasFocus) {
×
273
                    datePickerDialog.setArguments(new Bundle());
×
274
                    showDatePickerDialog(context, datePickerDialog, editText);
×
275
                }
276
            }
×
277
        });
278
        return genericTextWatcher;
1✔
279
    }
280

281
    private void addRefreshLogicView(Context context, MaterialEditText editText, String relevance, String constraints,
282
                                     String calculations) {
283
        if (StringUtils.isNotBlank(relevance) && context instanceof JsonApi) {
1✔
284
            editText.setTag(R.id.relevance, relevance);
1✔
285
            ((JsonApi) context).addSkipLogicView(editText);
1✔
286
        }
287

288
        if (StringUtils.isNotBlank(constraints) && context instanceof JsonApi) {
1✔
289
            editText.setTag(R.id.constraints, constraints);
1✔
290
            ((JsonApi) context).addConstrainedView(editText);
1✔
291
        }
292

293
        if (StringUtils.isNotBlank(calculations) && context instanceof JsonApi) {
1✔
294
            editText.setTag(R.id.calculation, calculations);
1✔
295
            ((JsonApi) context).addCalculationLogicView(editText);
1✔
296
        }
297
    }
1✔
298

299
    private void updateEditText(MaterialEditText editText, JSONObject jsonObject, String stepName, Context context, TextView duration) throws JSONException {
300

301
        Locale locale = getCurrentLocale(context);
1✔
302
        final SimpleDateFormat DATE_FORMAT_LOCALE = new SimpleDateFormat("dd-MM-yyyy", locale);
1✔
303

304
        String openMrsEntityParent = jsonObject.getString(JsonFormConstants.OPENMRS_ENTITY_PARENT);
1✔
305
        String openMrsEntity = jsonObject.getString(JsonFormConstants.OPENMRS_ENTITY);
1✔
306
        String openMrsEntityId = jsonObject.getString(JsonFormConstants.OPENMRS_ENTITY_ID);
1✔
307
        String dateValue = "";
1✔
308
        if (StringUtils.isNotBlank(jsonObject.optString(KEY.VALUE))) {
1✔
309
            dateValue = StringUtils.isNoneBlank(Form.getDatePickerDisplayFormat())
1✔
310
                    ? Utils.formatDateToPattern(jsonObject.optString(KEY.VALUE), Form.getDatePickerDisplayFormat(), FormUtils.NATIIVE_FORM_DATE_FORMAT_PATTERN)
×
311
                    : jsonObject.optString(KEY.VALUE);
1✔
312
        }
313

314
        editText.setHint(jsonObject.getString(KEY.HINT));
1✔
315
        editText.setFloatingLabelText(jsonObject.getString(KEY.HINT));
1✔
316
        editText.setId(ViewUtil.generateViewId());
1✔
317
        editText.setTag(R.id.key, jsonObject.getString(KEY.KEY));
1✔
318
        editText.setTag(R.id.type, jsonObject.getString(JsonFormConstants.TYPE));
1✔
319
        editText.setTag(R.id.openmrs_entity_parent, openMrsEntityParent);
1✔
320
        editText.setTag(R.id.openmrs_entity, openMrsEntity);
1✔
321
        editText.setTag(R.id.openmrs_entity_id, openMrsEntityId);
1✔
322
        editText.setTag(R.id.address, stepName + ":" + jsonObject.getString(KEY.KEY));
1✔
323
        editText.setTag(R.id.locale_independent_value, dateValue);
1✔
324

325
        if (jsonObject.has(JsonFormConstants.V_REQUIRED)) {
1✔
326
            JSONObject requiredObject = jsonObject.optJSONObject(JsonFormConstants.V_REQUIRED);
1✔
327
            boolean requiredValue = requiredObject.getBoolean(KEY.VALUE);
1✔
328
            if (Boolean.TRUE.equals(requiredValue)) {
1✔
329
                editText.addValidator(new RequiredValidator(requiredObject.getString(JsonFormConstants.ERR)));
1✔
330
                FormUtils.setRequiredOnHint(editText);
1✔
331
            }
332
        }
333

334
        if (StringUtils.isNotBlank(dateValue)) {
1✔
335
            updateDateText(context, editText, duration, DATE_FORMAT_LOCALE.format(FormUtils.getDate(dateValue).getTime()));
1✔
336
        }
337

338
        if (jsonObject.has(JsonFormConstants.READ_ONLY)) {
1✔
339
            boolean readOnly = jsonObject.getBoolean(JsonFormConstants.READ_ONLY);
1✔
340
            editText.setEnabled(!readOnly);
1✔
341
            editText.setFocusable(!readOnly);
1✔
342
        }
343
    }
1✔
344

345

346
    @VisibleForTesting
347
    protected Locale getCurrentLocale(Context context) {
348
        return context.getResources().getConfiguration().locale.getLanguage().equals("ar") ? Locale.ENGLISH : context.getResources().getConfiguration().locale;//Arabic should render normal numbers/numeric digits
×
349
    }
350

351
    protected DatePickerDialog createDateDialog(final Context context, final TextView duration, final MaterialEditText editText,
352
                                                JSONObject jsonObject) throws JSONException {
353
        final DatePickerDialog datePickerDialog = new DatePickerDialog();
1✔
354
        datePickerDialog.setContext(context);
1✔
355

356
        Locale locale = getCurrentLocale(context);
1✔
357
        final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy", locale);
1✔
358

359
        datePickerDialog.setOnDateSetListener(new android.app.DatePickerDialog.OnDateSetListener() {
1✔
360
            @Override
361
            public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
362
                Calendar calendarDate = Calendar.getInstance();
×
363
                calendarDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
×
364
                calendarDate.set(Calendar.MONTH, monthOfYear);
×
365
                calendarDate.set(Calendar.YEAR, year);
×
366

367
                editText.setTag(R.id.locale_independent_value, DATE_FORMAT_LOCALE_INDEPENDENT.format(calendarDate.getTime()));
×
368

369
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
×
370
                        && calendarDate.getTimeInMillis() >= view.getMinDate()
×
371
                        && calendarDate.getTimeInMillis() <= view.getMaxDate()) {
×
372
                    updateDateText(context, editText, duration,
×
373
                            DATE_FORMAT.format(calendarDate.getTime()));
×
374
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
×
375
                    updateDateText(context, editText, duration, "");
×
376
                }
377
            }
×
378
        });
379

380
        if (jsonObject.has(JsonFormConstants.MIN_DATE) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
1✔
381
            Calendar minDate = FormUtils.getDate(jsonObject.getString(JsonFormConstants.MIN_DATE));
1✔
382
            minDate.set(Calendar.HOUR_OF_DAY, 0);
1✔
383
            minDate.set(Calendar.MINUTE, 0);
1✔
384
            minDate.set(Calendar.SECOND, 0);
1✔
385
            minDate.set(Calendar.MILLISECOND, 0);
1✔
386
            datePickerDialog.setMinDate(minDate.getTimeInMillis());
1✔
387
        }
388

389
        if (jsonObject.has(JsonFormConstants.MAX_DATE) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
1✔
390
            Calendar maxDate = FormUtils.getDate(jsonObject.getString(JsonFormConstants.MAX_DATE));
1✔
391
            maxDate.set(Calendar.HOUR_OF_DAY, 23);
1✔
392
            maxDate.set(Calendar.MINUTE, 59);
1✔
393
            maxDate.set(Calendar.SECOND, 59);
1✔
394
            maxDate.set(Calendar.MILLISECOND, 999);
1✔
395
            datePickerDialog.setMaxDate(maxDate.getTimeInMillis());
1✔
396
        }
397

398
        return datePickerDialog;
1✔
399
    }
400

401

402
    protected int getLayout() {
403
        return R.layout.native_form_item_date_picker;
1✔
404
    }
405

406
    public static class KEY {
×
407
        public static final String DURATION = "duration";
408
        public static final String HINT = "hint";
409
        public static final String KEY = "key";
410
        public static final String VALUE = (JsonFormConstants.VALUE);
411
        public static final String DEFAULT = "default";
412
    }
413
}
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

© 2026 Coveralls, Inc