From 332574113e9e75fc2e42dc492e6365659ba2da5b Mon Sep 17 00:00:00 2001 From: ErolHaagenrud Date: Wed, 21 Jan 2026 14:14:08 +0100 Subject: [PATCH] =?UTF-8?q?F=C3=B8r=20feils=C3=B8king=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 8 +- .../com/kbs/kbsintranett/FormsFragment.java | 1129 +++-------------- .../com/kbs/kbsintranett/LoginFragment.java | 59 +- .../kbs/kbsintranett/UserFilterHelper.java | 2 +- app/src/main/res/layout/fragment_forms.xml | 19 +- app/src/main/res/layout/fragment_login.xml | 10 + 6 files changed, 269 insertions(+), 958 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4ca00ab..b91bcbe 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,14 +18,14 @@ if (localPropertiesFile.exists()) { android { namespace = "com.kbs.kbsintranett" - compileSdk = 34 + compileSdk = 35 defaultConfig { applicationId = "com.kbs.kbsintranett" minSdk = 28 - targetSdk = 34 - versionCode = 4 - versionName = "1.5.1" + targetSdk = 35 + versionCode = 6 + versionName = "1.6.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/kbs/kbsintranett/FormsFragment.java b/app/src/main/java/com/kbs/kbsintranett/FormsFragment.java index fcf4796..95f0736 100644 --- a/app/src/main/java/com/kbs/kbsintranett/FormsFragment.java +++ b/app/src/main/java/com/kbs/kbsintranett/FormsFragment.java @@ -43,11 +43,10 @@ import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; +import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.fragment.app.Fragment; -import androidx.navigation.Navigation; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -89,14 +88,7 @@ public class FormsFragment extends Fragment { private static final String TAG = "FormsFragment"; private static final String BASE_URL_GF = "https://intranet.kbs.no/wp-json/gf/v2"; - // SKJEMA ID-er private static final int ID_ANSATTEOPPLYSNINGER = 1; - private static final int ID_RUH = 4; - private static final int ID_SIKKERHETSKURS = 9; - private static final int ID_HMS_BEKREFTELSE = 10; - private static final int ID_EGENMELDING = 11; - private static final int ID_SJA = 14; - private static final int ID_FRAVARSVARSEL = 15; private static final int ID_REFUSJON_UTLEGG = 16; private int formId = 1; @@ -108,15 +100,12 @@ public class FormsFragment extends Fragment { private TextView lblHistory; private ProgressBar loadingSpinner; private ImageView btnToggleHistory; - private Toolbar toolbar; // NYTT - // --- HOVEDSKJEMA STATE --- private Map fieldWrappers = new HashMap<>(); private Map inputViews = new HashMap<>(); private Map requiredFieldsMap = new HashMap<>(); private Map fileUploads = new HashMap<>(); - // --- NESTED FORM (BARN) STATE --- private Map childInputViews = new HashMap<>(); private Map childRequiredFieldsMap = new HashMap<>(); private Map childFileUploads = new HashMap<>(); @@ -125,7 +114,6 @@ public class FormsFragment extends Fragment { private LinearLayout nestedEntriesContainer; private TextView totalAmountView; - // --- FILOPPLASTING & KAMERA --- private String pendingFileFieldId = null; private boolean isSelectingForChild = false; private Uri currentPhotoUri = null; @@ -158,19 +146,14 @@ public class FormsFragment extends Fragment { success -> { if (success && currentPhotoUri != null && pendingFileFieldId != null) { handleFileSelection(pendingFileFieldId, currentPhotoUri, isSelectingForChild); - } else if (!success) { - currentPhotoUri = null; } } ); requestPermissionLauncher = registerForActivityResult( new ActivityResultContracts.RequestPermission(), isGranted -> { - if (isGranted) { - openCamera(); - } else { - Toast.makeText(getContext(), "Kameratillatelse er påkrevd for å ta bilde.", Toast.LENGTH_LONG).show(); - } + if (isGranted) openCamera(); + else Toast.makeText(getContext(), "Kameratillatelse trengs.", Toast.LENGTH_SHORT).show(); } ); } @@ -185,30 +168,9 @@ public class FormsFragment extends Fragment { txtStatus = view.findViewById(R.id.txt_status); lblHistory = view.findViewById(R.id.lbl_history); loadingSpinner = view.findViewById(R.id.loading_spinner); - - // NYTT: Finn toolbar og sett listener - toolbar = view.findViewById(R.id.forms_toolbar); - if (toolbar != null) { - toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).navigateUp()); - } - btnToggleHistory = view.findViewById(R.id.btn_toggle_history); - if (btnToggleHistory != null) { - btnToggleHistory.setOnClickListener(v -> toggleHistoryVisibility()); - } - if (view instanceof ViewGroup) { - LayoutTransition transition = ((ViewGroup) view).getLayoutTransition(); - if (transition == null) { - transition = new LayoutTransition(); - ((ViewGroup) view).setLayoutTransition(transition); - } - transition.enableTransitionType(LayoutTransition.CHANGING); - } - - if (formContainer == null) { - formContainer = new LinearLayout(getContext()); - } + if (btnToggleHistory != null) btnToggleHistory.setOnClickListener(v -> toggleHistoryVisibility()); if (getArguments() != null) { int argId = getArguments().getInt("formId", 0); @@ -216,24 +178,11 @@ public class FormsFragment extends Fragment { } fetchFormStructure(); - return view; } - // --- UI LOGIKK FOR DELT SKJERM --- - - private void expandFormModule() { - if (historyWrapper != null && historyWrapper.getVisibility() == View.VISIBLE) { - historyWrapper.setVisibility(View.GONE); - if (btnToggleHistory != null) { - btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float); - } - } - } - private void toggleHistoryVisibility() { if (historyWrapper == null || btnToggleHistory == null) return; - if (historyWrapper.getVisibility() == View.VISIBLE) { historyWrapper.setVisibility(View.GONE); btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float); @@ -243,30 +192,8 @@ public class FormsFragment extends Fragment { } } - private void attachInteractionListener(View view) { - if (view == null) return; - view.setOnTouchListener((v, event) -> { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - expandFormModule(); - } - return false; - }); - view.setOnFocusChangeListener((v, hasFocus) -> { - if (hasFocus) { - expandFormModule(); - } - }); - if (view.isClickable()) { - view.setOnClickListener(v -> expandFormModule()); - } - } - - // ---------------------------------- - private void fetchFormStructure() { if (loadingSpinner != null) loadingSpinner.setVisibility(View.VISIBLE); - updateStatus("Laster skjema..."); - WordPressApiService api = RetrofitClient.getApiService(); api.getForm(formId).enqueue(new retrofit2.Callback() { @Override @@ -280,15 +207,9 @@ public class FormsFragment extends Fragment { fetchFormEntries(); }); } - } else { - updateStatus("Feil ved lasting av skjema."); - if (loadingSpinner != null) loadingSpinner.setVisibility(View.GONE); } } - - @Override - public void onFailure(retrofit2.Call call, Throwable t) { - updateStatus("Nettverksfeil (Skjema): " + t.getMessage()); + @Override public void onFailure(retrofit2.Call call, Throwable t) { if (loadingSpinner != null) loadingSpinner.setVisibility(View.GONE); } }); @@ -302,38 +223,40 @@ public class FormsFragment extends Fragment { requiredFieldsMap.clear(); fileUploads.clear(); nestedEntries.clear(); - updateStatus(""); - // NYTT: Sett tittelen i Toolbaren i stedet for å legge til en TextView - if (toolbar != null) { - toolbar.setTitle(getCleanTitle(form.title)); + // 1. OPPSETT AV TOOLBAR (MainActivity sin toolbar) + if (getActivity() instanceof AppCompatActivity) { + AppCompatActivity activity = (AppCompatActivity) getActivity(); + if (activity.getSupportActionBar() != null) { + activity.getSupportActionBar().setTitle("Fyll ut skjema"); + activity.getSupportActionBar().setDisplayHomeAsUpEnabled(false); // Fjerner pil tilbake + } } - if (historyWrapper != null) { - historyWrapper.setVisibility(View.VISIBLE); - if (btnToggleHistory != null) btnToggleHistory.setImageResource(android.R.drawable.arrow_up_float); - } + // 2. OVERSKRIFT I SKJEMAET + TextView headerTitle = new TextView(getContext()); + headerTitle.setText("Fyll ut " + getCleanTitle(form.title)); + headerTitle.setTextSize(22); // Rettet fra 22sp til 22 + headerTitle.setTypeface(null, Typeface.BOLD); + headerTitle.setTextColor(Color.BLACK); + headerTitle.setPadding(0, 10, 0, 30); + formContainer.addView(headerTitle); - // Beskrivelse legges fortsatt inn som innhold if (form.description != null && !form.description.isEmpty()) { TextView formDesc = new TextView(getContext()); - String cleanDesc = form.description.replaceFirst("^\\d+\\.\\s*", ""); - formDesc.setText(cleanDesc); + formDesc.setText(form.description.replaceFirst("^\\d+\\.\\s*", "")); formDesc.setPadding(0, 0, 0, 40); formContainer.addView(formDesc); } if (form.fields == null) return; for (GravityField field : form.fields) { - if ("hidden".equals(field.type) || field.isHidden || "hidden".equals(field.visibility)) { - continue; - } + if ("hidden".equals(field.type) || field.isHidden || "hidden".equals(field.visibility)) continue; LinearLayout fieldWrapper = new LinearLayout(getContext()); fieldWrapper.setOrientation(LinearLayout.VERTICAL); fieldWrapper.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); fieldWrapper.setPadding(0, 10, 0, 20); - fieldWrappers.put(String.valueOf(field.id), fieldWrapper); if ("section".equals(field.type)) { @@ -361,37 +284,19 @@ public class FormsFragment extends Fragment { label.setPadding(0, 10, 0, 5); fieldWrapper.addView(label); - if ("form".equals(field.type)) { - renderNestedFormField(fieldWrapper, field); - } - else if ("product".equals(field.type) && "calculation".equals(field.inputType)) { - renderTotalSumField(fieldWrapper, field); - } - else if ("time".equals(field.type)) { - renderTimeField(fieldWrapper, field, inputViews, requiredFieldsMap); - } else if ("fileupload".equals(field.type)) { - renderFileUploadField(fieldWrapper, field, inputViews, requiredFieldsMap, false); - } else if (field.inputs != null && !field.inputs.isEmpty()) { - if ("consent".equals(field.type)) { - renderConsentField(fieldWrapper, field, inputViews, requiredFieldsMap); - } else if ("checkbox".equals(field.type) || "multi_choice".equals(field.type)) { - renderCheckboxField(fieldWrapper, field, inputViews, requiredFieldsMap); - } else { - renderCompositeField(fieldWrapper, field, inputViews, requiredFieldsMap); - } - } else if ("radio".equals(field.type)) { - renderRadioField(fieldWrapper, field, inputViews, requiredFieldsMap); - } else if ("select".equals(field.type)) { - renderSelectField(fieldWrapper, field, inputViews, requiredFieldsMap); - } else if ("textarea".equals(field.type)) { - renderTextAreaField(fieldWrapper, field, inputViews, requiredFieldsMap); - } else if ("date".equals(field.type)) { - renderDateField(fieldWrapper, field, inputViews, requiredFieldsMap); - } else if ("consent".equals(field.type)) { - renderConsentField(fieldWrapper, field, inputViews, requiredFieldsMap); - } else { - renderTextField(fieldWrapper, field, inputViews, requiredFieldsMap); - } + if ("form".equals(field.type)) renderNestedFormField(fieldWrapper, field); + else if ("product".equals(field.type) && "calculation".equals(field.inputType)) renderTotalSumField(fieldWrapper, field); + else if ("time".equals(field.type)) renderTimeField(fieldWrapper, field, inputViews, requiredFieldsMap); + else if ("fileupload".equals(field.type)) renderFileUploadField(fieldWrapper, field, inputViews, requiredFieldsMap, false); + else if (field.inputs != null && !field.inputs.isEmpty()) { + if ("consent".equals(field.type)) renderConsentField(fieldWrapper, field, inputViews, requiredFieldsMap); + else if ("checkbox".equals(field.type) || "multi_choice".equals(field.type)) renderCheckboxField(fieldWrapper, field, inputViews, requiredFieldsMap); + else renderCompositeField(fieldWrapper, field, inputViews, requiredFieldsMap); + } else if ("radio".equals(field.type)) renderRadioField(fieldWrapper, field, inputViews, requiredFieldsMap); + else if ("select".equals(field.type)) renderSelectField(fieldWrapper, field, inputViews, requiredFieldsMap); + else if ("textarea".equals(field.type)) renderTextAreaField(fieldWrapper, field, inputViews, requiredFieldsMap); + else if ("date".equals(field.type)) renderDateField(fieldWrapper, field, inputViews, requiredFieldsMap); + else renderTextField(fieldWrapper, field, inputViews, requiredFieldsMap); if (field.description != null && !field.description.isEmpty()) { TextView desc = new TextView(getContext()); @@ -400,14 +305,13 @@ public class FormsFragment extends Fragment { desc.setTextColor(Color.GRAY); fieldWrapper.addView(desc); } - formContainer.addView(fieldWrapper); } Button dynamicSubmit = new Button(getContext()); dynamicSubmit.setText("Send inn skjema"); dynamicSubmit.setTextColor(Color.WHITE); - dynamicSubmit.setBackgroundColor(Color.parseColor("#0069B3")); // KBS Blå + dynamicSubmit.setBackgroundColor(Color.parseColor("#0069B3")); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.setMargins(0, 60, 0, 20); dynamicSubmit.setLayoutParams(params); @@ -417,8 +321,6 @@ public class FormsFragment extends Fragment { evaluateAllConditionalLogic(); } - // --- NESTED FORM LOGIKK --- - private void renderNestedFormField(LinearLayout container, GravityField field) { nestedEntriesContainer = new LinearLayout(getContext()); nestedEntriesContainer.setOrientation(LinearLayout.VERTICAL); @@ -430,12 +332,9 @@ public class FormsFragment extends Fragment { btnAdd.setBackgroundColor(Color.parseColor("#53AFE9")); btnAdd.setTextColor(Color.WHITE); btnAdd.setOnClickListener(v -> { - expandFormModule(); int childFormId = 18; if (field.gpnfForm != null) { - try { - childFormId = Integer.parseInt(field.gpnfForm); - } catch (NumberFormatException e) { e.printStackTrace(); } + try { childFormId = Integer.parseInt(field.gpnfForm); } catch (NumberFormatException e) { e.printStackTrace(); } } openChildFormDialog(childFormId, field.id); }); @@ -456,7 +355,6 @@ public class FormsFragment extends Fragment { } private void openChildFormDialog(int childFormId, String parentFieldId) { - if (getActivity() == null) return; ProgressBar pBar = new ProgressBar(getContext()); AlertDialog loadingDialog = new AlertDialog.Builder(getContext()) .setView(pBar) @@ -469,16 +367,9 @@ public class FormsFragment extends Fragment { loadingDialog.dismiss(); if (response.isSuccessful() && response.body() != null) { showChildFormDialog(response.body(), parentFieldId); - } else { - Toast.makeText(getContext(), "Kunne ikke hente underskjema", Toast.LENGTH_SHORT).show(); } } - - @Override - public void onFailure(retrofit2.Call call, Throwable t) { - loadingDialog.dismiss(); - Toast.makeText(getContext(), "Feil: " + t.getMessage(), Toast.LENGTH_SHORT).show(); - } + @Override public void onFailure(retrofit2.Call call, Throwable t) { loadingDialog.dismiss(); } }); } @@ -500,9 +391,7 @@ public class FormsFragment extends Fragment { wrapper.setPadding(0, 10, 0, 20); TextView label = new TextView(getContext()); - String lText = field.label; - if (field.isRequired) lText += " *"; - label.setText(lText); + label.setText(field.label + (field.isRequired ? " *" : "")); label.setTypeface(null, Typeface.BOLD); wrapper.addView(label); if ("fileupload".equals(field.type)) { @@ -536,70 +425,46 @@ public class FormsFragment extends Fragment { for (Map.Entry entry : childInputViews.entrySet()) { String val = getInputValueGeneric(entry.getValue()); if (!val.isEmpty()) { - try { - inputValues.put("input_" + entry.getKey(), val); - } catch (JSONException e) { e.printStackTrace(); } + try { inputValues.put("input_" + entry.getKey(), val); } catch (JSONException e) { e.printStackTrace(); } } } - if (!childFileUploads.isEmpty()) { - List fileParts = new ArrayList<>(); - Map textParts = new HashMap<>(); + List fileParts = new ArrayList<>(); + Map textParts = new HashMap<>(); - try { - JSONArray names = inputValues.names(); - if (names != null) { - for (int i = 0; i < names.length(); i++) { - String key = names.getString(i); - String val = inputValues.getString(key); - textParts.put(key, RequestBody.create(MultipartBody.FORM, val)); - } + try { + JSONArray names = inputValues.names(); + if (names != null) { + for (int i = 0; i < names.length(); i++) { + String key = names.getString(i); + textParts.put(key, RequestBody.create(MultipartBody.FORM, inputValues.getString(key))); } + } + if (parentFieldId != null) textParts.put("gpnf_entry_nested_form_field", RequestBody.create(MultipartBody.FORM, parentFieldId)); - if (parentFieldId != null) { - textParts.put("gpnf_entry_nested_form_field", RequestBody.create(MultipartBody.FORM, parentFieldId)); - } + for (Map.Entry fileEntry : childFileUploads.entrySet()) { + MultipartBody.Part part = getFilePart("input_" + fileEntry.getKey(), fileEntry.getValue()); + if (part != null) fileParts.add(part); + } - for (Map.Entry fileEntry : childFileUploads.entrySet()) { - String fieldId = fileEntry.getKey(); - Uri uri = fileEntry.getValue(); - if (uri != null) { - MultipartBody.Part part = getFilePart("input_" + fieldId, uri); - if (part != null) fileParts.add(part); - } - } - - Toast.makeText(getContext(), "Laster opp vedlegg...", Toast.LENGTH_SHORT).show(); - RetrofitClient.getApiService().submitMultipartForm(childFormId, textParts, fileParts).enqueue(new retrofit2.Callback() { - @Override - public void onResponse(retrofit2.Call call, retrofit2.Response response) { - if (response.isSuccessful() && response.body() != null) { - try { - JsonObject json = response.body().getAsJsonObject(); - if (json.has("is_valid") && json.get("is_valid").getAsBoolean()) { - String entryId = json.has("entry_id") ? json.get("entry_id").getAsString() : ""; - String desc = getInputValueGeneric(childInputViews.get("3")); - String price = getInputValueGeneric(childInputViews.get("4")); - addNestedEntry(entryId, desc, price); - dialog.dismiss(); - } else { - Toast.makeText(getContext(), "Ugyldig respons fra server", Toast.LENGTH_SHORT).show(); - } - } catch (Exception e) { - e.printStackTrace(); - Toast.makeText(getContext(), "Feil ved parsing av svar", Toast.LENGTH_SHORT).show(); - } - } else { - Toast.makeText(getContext(), "Feil ved opplasting", Toast.LENGTH_SHORT).show(); + Toast.makeText(getContext(), "Laster opp...", Toast.LENGTH_SHORT).show(); + RetrofitClient.getApiService().submitMultipartForm(childFormId, textParts, fileParts).enqueue(new retrofit2.Callback() { + @Override + public void onResponse(retrofit2.Call call, retrofit2.Response response) { + if (response.isSuccessful() && response.body() != null) { + JsonObject json = response.body().getAsJsonObject(); + if (json.has("is_valid") && json.get("is_valid").getAsBoolean()) { + String entryId = json.get("entry_id").getAsString(); + String desc = getInputValueGeneric(childInputViews.get("3")); + String price = getInputValueGeneric(childInputViews.get("4")); + addNestedEntry(entryId, desc, price); + dialog.dismiss(); } } - @Override - public void onFailure(retrofit2.Call call, Throwable t) { - Toast.makeText(getContext(), "Nettverksfeil", Toast.LENGTH_SHORT).show(); - } - }); - } catch (Exception e) { e.printStackTrace(); } - } + } + @Override public void onFailure(retrofit2.Call call, Throwable t) {} + }); + } catch (Exception e) { e.printStackTrace(); } } private void addNestedEntry(String entryId, String description, String price) { @@ -610,133 +475,78 @@ public class FormsFragment extends Fragment { private void refreshNestedList() { if (nestedEntriesContainer == null) return; nestedEntriesContainer.removeAllViews(); - double total = 0; List ids = new ArrayList<>(); for (NestedEntry entry : nestedEntries) { ids.add(entry.id); String cleanPrice = entry.price.replaceAll("[^0-9,.]", "").replace(",", "."); - try { - if (!cleanPrice.isEmpty()) total += Double.parseDouble(cleanPrice); - } catch (NumberFormatException e) { } - - LinearLayout row = new LinearLayout(getContext()); - row.setOrientation(LinearLayout.HORIZONTAL); - row.setPadding(10, 10, 10, 10); + try { if (!cleanPrice.isEmpty()) total += Double.parseDouble(cleanPrice); } catch (NumberFormatException ignored) {} TextView txt = new TextView(getContext()); txt.setText(entry.description + " (" + entry.price + ")"); - txt.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1)); - - row.addView(txt); - nestedEntriesContainer.addView(row); + txt.setPadding(10, 10, 10, 10); + nestedEntriesContainer.addView(txt); } - - if (totalAmountView != null) { - totalAmountView.setText("Totalt: Kr " + String.format("%.2f", total)); - } - + if (totalAmountView != null) totalAmountView.setText("Totalt: Kr " + String.format("%.2f", total)); View hiddenField = inputViews.get("25"); - if (hiddenField instanceof EditText) { - ((EditText)hiddenField).setText(TextUtils.join(",", ids)); - } + if (hiddenField instanceof EditText) ((EditText)hiddenField).setText(TextUtils.join(",", ids)); } - // --- FELLES METODER (FILE UPLOAD M/ CAMERA STØTTE) --- - private void renderFileUploadField(LinearLayout container, GravityField field, Map viewsMap, Map reqMap, boolean isChild) { LinearLayout fileLayout = new LinearLayout(getContext()); fileLayout.setOrientation(LinearLayout.HORIZONTAL); - Button btnUpload = new Button(getContext()); - btnUpload.setText("Velg fil / Ta bilde"); + btnUpload.setText("Velg fil / Kamera"); btnUpload.setOnClickListener(v -> { pendingFileFieldId = field.id; isSelectingForChild = isChild; - expandFormModule(); showFileSourceDialog(); }); TextView txtFileName = new TextView(getContext()); - txtFileName.setText("Ingen fil valgt"); + txtFileName.setText("Ingen valgt"); txtFileName.setPadding(20, 0, 0, 0); - txtFileName.setTextColor(Color.GRAY); btnUpload.setTag(txtFileName); - fileLayout.addView(btnUpload); fileLayout.addView(txtFileName); container.addView(fileLayout); - viewsMap.put(field.id, btnUpload); reqMap.put(field.id, field.isRequired); } private void showFileSourceDialog() { String[] options = {"Ta bilde", "Velg fil"}; - new AlertDialog.Builder(getContext()) - .setTitle("Last opp vedlegg") - .setItems(options, (dialog, which) -> { - if (which == 0) { - if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) - == PackageManager.PERMISSION_GRANTED) { - openCamera(); - } else { - requestPermissionLauncher.launch(Manifest.permission.CAMERA); - } - } else { - if (filePickerLauncher != null) { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.setType("*/*"); - String[] mimeTypes = {"image/jpeg", "image/png", "application/pdf"}; - intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); - filePickerLauncher.launch(intent); - } - } - }) - .show(); + new AlertDialog.Builder(getContext()).setTitle("Last opp").setItems(options, (dialog, which) -> { + if (which == 0) { + if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) openCamera(); + else requestPermissionLauncher.launch(Manifest.permission.CAMERA); + } else { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("*/*"); + filePickerLauncher.launch(intent); + } + }).show(); } private void openCamera() { currentPhotoUri = createImageUri(); - if (currentPhotoUri != null && takePictureLauncher != null) { - try { - takePictureLauncher.launch(currentPhotoUri); - } catch (Exception e) { - Toast.makeText(getContext(), "Kunne ikke starte kamera: " + e.getMessage(), Toast.LENGTH_SHORT).show(); - Log.e(TAG, "Camera launch failed", e); - } - } else { - Toast.makeText(getContext(), "Kunne ikke opprette bildefil", Toast.LENGTH_SHORT).show(); - } + if (currentPhotoUri != null) takePictureLauncher.launch(currentPhotoUri); } private Uri createImageUri() { try { - String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); - String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES); - File image = File.createTempFile(imageFileName, ".jpg", storageDir); + File image = File.createTempFile("IMG_", ".jpg", storageDir); return FileProvider.getUriForFile(requireContext(), "com.kbs.kbsintranett.fileprovider", image); - } catch (IOException e) { - e.printStackTrace(); - return null; - } + } catch (IOException e) { return null; } } private void handleFileSelection(String fieldId, Uri uri, boolean isChild) { - if (isChild) { - childFileUploads.put(fieldId, uri); - } else { - fileUploads.put(fieldId, uri); - } - - Map targetMap = isChild ? childInputViews : inputViews; - View view = targetMap.get(fieldId); + if (isChild) childFileUploads.put(fieldId, uri); + else fileUploads.put(fieldId, uri); + View view = (isChild ? childInputViews : inputViews).get(fieldId); if (view instanceof Button) { TextView txtView = (TextView) view.getTag(); - if (txtView != null) { - txtView.setText(getFileName(uri)); - txtView.setTextColor(Color.BLACK); - } + if (txtView != null) txtView.setText(getFileName(uri)); } } @@ -746,9 +556,9 @@ public class FormsFragment extends Fragment { try (Cursor cursor = getContext().getContentResolver().query(uri, null, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); - if(index >= 0) result = cursor.getString(index); + if (index >= 0) result = cursor.getString(index); } - } catch (Exception e) {} + } } if (result == null) { result = uri.getPath(); @@ -761,28 +571,19 @@ public class FormsFragment extends Fragment { private String getCleanTitle(String title) { if (title == null) return ""; Matcher m = TITLE_PATTERN.matcher(title.trim()); - if (m.find()) { - return m.group(2); - } - return title; + return m.find() ? m.group(2) : title; } - // --- STANDARD RENDER METODER --- - private void renderTimeField(LinearLayout container, GravityField field, Map views, Map req) { EditText timeInput = new EditText(getContext()); timeInput.setFocusable(false); - timeInput.setClickable(true); timeInput.setHint("00:00"); timeInput.setOnClickListener(v -> { - expandFormModule(); - Calendar mcurrentTime = Calendar.getInstance(); - int hour = mcurrentTime.get(Calendar.HOUR_OF_DAY); - int minute = mcurrentTime.get(Calendar.MINUTE); - new TimePickerDialog(getContext(), (timePicker, selectedHour, selectedMinute) -> { - timeInput.setText(String.format("%02d:%02d", selectedHour, selectedMinute)); + Calendar c = Calendar.getInstance(); + new TimePickerDialog(getContext(), (tp, h, m) -> { + timeInput.setText(String.format("%02d:%02d", h, m)); evaluateAllConditionalLogic(); - }, hour, minute, true).show(); + }, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true).show(); }); container.addView(timeInput); views.put(field.id, timeInput); @@ -791,33 +592,20 @@ public class FormsFragment extends Fragment { private void renderTextField(LinearLayout container, GravityField field, Map views, Map req) { EditText input = new EditText(getContext()); - input.setPadding(30, 30, 30, 30); - input.setBackgroundResource(android.R.drawable.edit_text); - - if ("number".equals(field.type) || "phone".equals(field.type)) { - input.setInputType(InputType.TYPE_CLASS_PHONE); - } else if ("email".equals(field.type)) { - input.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); - } else { - input.setInputType(InputType.TYPE_CLASS_TEXT); - } + if ("number".equals(field.type) || "phone".equals(field.type)) input.setInputType(InputType.TYPE_CLASS_PHONE); + else if ("email".equals(field.type)) input.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); if (views == inputViews) { UserManager user = UserManager.getInstance(); String lowerLabel = field.label.toLowerCase(); if (lowerLabel.contains("e-post")) input.setText(user.getUserEmail()); - if (lowerLabel.contains("navn") || lowerLabel.contains("melder")) input.setText(user.getUserDisplayName()); - if (lowerLabel.contains("stilling")) input.setText(user.getStilling()); - if (lowerLabel.contains("mobil")) input.setText(user.getMobiltelefon()); + if (lowerLabel.contains("navn")) input.setText(user.getUserDisplayName()); } - input.addTextChangedListener(new TextWatcher() { public void beforeTextChanged(CharSequence s, int start, int count, int after) {} public void onTextChanged(CharSequence s, int start, int before, int count) {} public void afterTextChanged(Editable s) { evaluateAllConditionalLogic(); } }); - attachInteractionListener(input); - container.addView(input); views.put(field.id, input); req.put(field.id, field.isRequired); @@ -825,13 +613,8 @@ public class FormsFragment extends Fragment { private void renderTextAreaField(LinearLayout container, GravityField field, Map views, Map req) { EditText input = new EditText(getContext()); - input.setBackgroundResource(android.R.drawable.edit_text); input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE); input.setMinLines(3); - input.setGravity(android.view.Gravity.TOP | android.view.Gravity.START); - - attachInteractionListener(input); - container.addView(input); views.put(field.id, input); req.put(field.id, field.isRequired); @@ -844,17 +627,10 @@ public class FormsFragment extends Fragment { RadioButton rb = new RadioButton(getContext()); rb.setText(choice.text); rb.setTag(choice.value); - rb.setOnClickListener(v -> { - expandFormModule(); - evaluateAllConditionalLogic(); - }); + rb.setOnClickListener(v -> evaluateAllConditionalLogic()); group.addView(rb); } } - group.setOnCheckedChangeListener((g, i) -> { - expandFormModule(); - evaluateAllConditionalLogic(); - }); container.addView(group); views.put(field.id, group); req.put(field.id, field.isRequired); @@ -862,19 +638,10 @@ public class FormsFragment extends Fragment { private void renderSelectField(LinearLayout container, GravityField field, Map views, Map req) { Spinner spinner = new Spinner(getContext()); - spinner.setOnTouchListener((v, event) -> { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - expandFormModule(); - } - return false; - }); List labels = new ArrayList<>(); labels.add("- Velg -"); - if (field.choices != null) { - for (GravityField.Choice c : field.choices) labels.add(c.text); - } - ArrayAdapter adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, labels); - spinner.setAdapter(adapter); + if (field.choices != null) { for (GravityField.Choice c : field.choices) labels.add(c.text); } + spinner.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, labels)); container.addView(spinner); views.put(field.id, spinner); req.put(field.id, field.isRequired); @@ -882,15 +649,9 @@ public class FormsFragment extends Fragment { private void renderConsentField(LinearLayout container, GravityField field, Map views, Map req) { CheckBox checkBox = new CheckBox(getContext()); - String cbText = (field.checkboxLabel != null && !field.checkboxLabel.isEmpty()) ? field.checkboxLabel : field.label; - checkBox.setText(cbText); + checkBox.setText(field.checkboxLabel != null ? field.checkboxLabel : field.label); String inputId = (field.inputs != null && !field.inputs.isEmpty()) ? field.inputs.get(0).id : field.id; - - checkBox.setTag("1"); - checkBox.setOnCheckedChangeListener((b, c) -> { - expandFormModule(); - evaluateAllConditionalLogic(); - }); + checkBox.setOnCheckedChangeListener((b, c) -> evaluateAllConditionalLogic()); container.addView(checkBox); views.put(inputId, checkBox); req.put(inputId, field.isRequired); @@ -898,98 +659,43 @@ public class FormsFragment extends Fragment { private void renderCheckboxField(LinearLayout container, GravityField field, Map views, Map req) { if (field.inputs != null) { - for (int i = 0; i < field.inputs.size(); i++) { - GravityField inputDef = field.inputs.get(i); - CheckBox checkBox = new CheckBox(getContext()); - checkBox.setText(inputDef.label); - - String value = "1"; - if (field.choices != null && i < field.choices.size()) { - value = field.choices.get(i).value; - } - checkBox.setTag(value); - checkBox.setOnCheckedChangeListener((b, c) -> { - expandFormModule(); - evaluateAllConditionalLogic(); - }); - container.addView(checkBox); - views.put(inputDef.id, checkBox); - req.put(inputDef.id, false); + for (GravityField inputDef : field.inputs) { + CheckBox cb = new CheckBox(getContext()); + cb.setText(inputDef.label); + cb.setOnCheckedChangeListener((b, c) -> evaluateAllConditionalLogic()); + container.addView(cb); + views.put(inputDef.id, cb); } } } private void renderDateField(LinearLayout container, GravityField field, Map views, Map req) { EditText dateInput = new EditText(getContext()); - if (field.readOnly || (formId == ID_REFUSJON_UTLEGG && "28".equals(field.id))) { - SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()); - dateInput.setText(df.format(new Date())); - - dateInput.setFocusable(false); - dateInput.setClickable(false); - dateInput.setEnabled(false); - dateInput.setTextColor(Color.BLACK); - } else { - dateInput.setFocusable(false); - dateInput.setClickable(true); - dateInput.setHint("dd.mm.yyyy"); - dateInput.setOnClickListener(v -> { - expandFormModule(); - Calendar c = Calendar.getInstance(); - new DatePickerDialog(getContext(), (view, year, month, dayOfMonth) -> { - dateInput.setText(String.format("%02d.%02d.%d", dayOfMonth, month + 1, year)); - evaluateAllConditionalLogic(); - }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show(); - }); - } - - dateInput.setPadding(30, 30, 30, 30); - dateInput.setBackgroundResource(android.R.drawable.edit_text); - + dateInput.setFocusable(false); + dateInput.setHint("dd.mm.yyyy"); + dateInput.setOnClickListener(v -> { + Calendar c = Calendar.getInstance(); + new DatePickerDialog(getContext(), (view, y, m, d) -> { + dateInput.setText(String.format("%02d.%02d.%d", d, m + 1, y)); + evaluateAllConditionalLogic(); + }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show(); + }); container.addView(dateInput); views.put(field.id, dateInput); req.put(field.id, field.isRequired); } private void renderCompositeField(LinearLayout container, GravityField parentField, Map views, Map req) { - UserManager user = UserManager.getInstance(); - boolean isPersonalia = (formId == ID_ANSATTEOPPLYSNINGER); - - List inputs = new ArrayList<>(parentField.inputs); - if ("address".equals(parentField.type)) { - Collections.sort(inputs, (f1, f2) -> Integer.compare(getAddressScore(f1.label), getAddressScore(f2.label))); - } - - for (GravityField subField : inputs) { - if (subField.isHidden || "hidden".equals(subField.visibility)) continue; + if (parentField.inputs == null) return; + for (GravityField subField : parentField.inputs) { TextView subLabel = new TextView(getContext()); - String subLabelText = subField.label; - boolean isSubRequired = parentField.isRequired; - if ("address".equals(parentField.type) && subField.id.endsWith(".2")) { - isSubRequired = false; - } - if (isSubRequired) subLabelText += " *"; - - subLabel.setText(subLabelText); - subLabel.setTextColor(Color.GRAY); + subLabel.setText(subField.label + (parentField.isRequired ? " *" : "")); subLabel.setTextSize(12); - subLabel.setPadding(0, 10, 0, 0); container.addView(subLabel); - EditText subInput = new EditText(getContext()); - subInput.setPadding(30, 30, 30, 30); - subInput.setBackgroundResource(android.R.drawable.edit_text); - subInput.setInputType(InputType.TYPE_CLASS_TEXT); - if (isPersonalia && parentField.label.toLowerCase().contains("navn") && !parentField.label.toLowerCase().contains("pårørende")) { - String lowerSub = subField.label.toLowerCase(); - if (lowerSub.contains("fornavn")) subInput.setText(user.getFirstName()); - else if (lowerSub.contains("etternavn")) subInput.setText(user.getLastName()); - } - - attachInteractionListener(subInput); container.addView(subInput); views.put(subField.id, subInput); - req.put(subField.id, isSubRequired); + req.put(subField.id, parentField.isRequired); } } @@ -1005,40 +711,21 @@ public class FormsFragment extends Fragment { TextView desc = new TextView(getContext()); desc.setText(Html.fromHtml(descText, Html.FROM_HTML_MODE_COMPACT)); desc.setTextSize(12); - desc.setTextColor(Color.GRAY); container.addView(desc); } - View line = new View(getContext()); line.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 2)); line.setBackgroundColor(Color.LTGRAY); - line.setPadding(0,0,0,20); container.addView(line); } - private int getAddressScore(String label) { - if (label == null) return 99; - String l = label.toLowerCase(); - if (l.contains("adresselinje 1")) return 1; - if (l.contains("adresselinje 2")) return 2; - if (l.contains("postnummer") || l.contains("zip")) return 3; - if (l.contains("poststed") || l.contains("city")) return 4; - if (l.contains("land") || l.contains("country")) return 5; - return 99; - } - private void evaluateAllConditionalLogic() { if (currentForm == null || currentForm.fields == null) return; for (GravityField field : currentForm.fields) { - if (field.conditionalLogic == null) { - setViewVisibility(field.id, true); - continue; - } - + if (field.conditionalLogic == null) { setViewVisibility(field.id, true); continue; } boolean isMatch = evaluateLogic(field.conditionalLogic); boolean show = "show".equalsIgnoreCase(field.conditionalLogic.actionType); - boolean shouldBeVisible = (show && isMatch) || (!show && !isMatch); - setViewVisibility(field.id, shouldBeVisible); + setViewVisibility(field.id, (show && isMatch) || (!show && !isMatch)); } } @@ -1046,195 +733,81 @@ public class FormsFragment extends Fragment { if (logic.rules == null || logic.rules.isEmpty()) return true; boolean isAll = "all".equalsIgnoreCase(logic.logicType); boolean aggregatedResult = isAll; - for (GravityField.Rule rule : logic.rules) { String val = getInputValue(rule.fieldId); boolean ruleMatch = checkRule(val, rule.operator, rule.value); - - if (isAll) { - aggregatedResult = aggregatedResult && ruleMatch; - if (!aggregatedResult) break; - } else { - aggregatedResult = aggregatedResult || - ruleMatch; - if (aggregatedResult) break; - } + if (isAll) { aggregatedResult &= ruleMatch; if (!aggregatedResult) break; } + else { aggregatedResult |= ruleMatch; if (aggregatedResult) break; } } return aggregatedResult; } - private boolean checkRule(String actualValue, String operator, String targetValue) { - if (actualValue == null) actualValue = ""; - if (targetValue == null) targetValue = ""; - - switch (operator.toLowerCase()) { - case "is": return actualValue.equalsIgnoreCase(targetValue); - case "isnot": return !actualValue.equalsIgnoreCase(targetValue); - case "contains": return actualValue.toLowerCase().contains(targetValue.toLowerCase()); - case "starts_with": return actualValue.toLowerCase().startsWith(targetValue.toLowerCase()); - case "ends_with": return actualValue.toLowerCase().endsWith(targetValue.toLowerCase()); + private boolean checkRule(String actual, String op, String target) { + if (actual == null) actual = ""; if (target == null) target = ""; + switch (op.toLowerCase()) { + case "is": return actual.equalsIgnoreCase(target); + case "isnot": return !actual.equalsIgnoreCase(target); + case "contains": return actual.toLowerCase().contains(target.toLowerCase()); default: return false; } } - private String getInputValue(String fieldId) { - View view = inputViews.get(fieldId); - return getInputValueGeneric(view); - } + private String getInputValue(String fieldId) { return getInputValueGeneric(inputViews.get(fieldId)); } private String getInputValueGeneric(View view) { - if (view == null) return ""; if (view instanceof EditText) return ((EditText) view).getText().toString(); if (view instanceof RadioGroup) { int id = ((RadioGroup) view).getCheckedRadioButtonId(); - if (id != -1) { - View rb = view.findViewById(id); - if (rb != null && rb.getTag() != null) return rb.getTag().toString(); - } - } - if (view instanceof Spinner) { - if (((Spinner) view).getSelectedItemPosition() == 0) return ""; - Object item = ((Spinner) view).getSelectedItem(); - return item != null ? item.toString() : ""; - } - if (view instanceof CheckBox) { - CheckBox cb = (CheckBox) view; - if (cb.isChecked()) { - return cb.getTag() != null ? - cb.getTag().toString() : "1"; - } - return ""; + if (id != -1) { View rb = view.findViewById(id); if (rb != null && rb.getTag() != null) return rb.getTag().toString(); } } + if (view instanceof Spinner) return ((Spinner) view).getSelectedItemPosition() > 0 ? ((Spinner) view).getSelectedItem().toString() : ""; + if (view instanceof CheckBox) return ((CheckBox) view).isChecked() ? "1" : ""; return ""; } private void setViewVisibility(String fieldId, boolean visible) { View wrapper = fieldWrappers.get(fieldId); - if (wrapper != null) { - wrapper.setVisibility(visible ? View.VISIBLE : View.GONE); - } + if (wrapper != null) wrapper.setVisibility(visible ? View.VISIBLE : View.GONE); } - // --- SUBMISSION --- - private void submitDynamicForm() { JSONObject inputValues = new JSONObject(); boolean hasValues = false; - - Log.d(TAG, "submitDynamicForm: Starting validation..."); - for (Map.Entry entry : inputViews.entrySet()) { - String fieldId = entry.getKey(); - View view = entry.getValue(); - - View wrapper = fieldWrappers.get(fieldId); - if (wrapper == null) { - if (!view.isShown()) continue; - } else { - if (wrapper.getVisibility() != View.VISIBLE) continue; - } - - String val = getInputValueGeneric(view); - Boolean req = requiredFieldsMap.get(fieldId); - if (req != null && req && val.isEmpty() && !(view instanceof Button)) { - Log.d(TAG, "Validation failed for field " + fieldId); - if (view instanceof EditText) { - ((EditText)view).setError("Må fylles ut"); - view.requestFocus(); - } else { - Toast.makeText(getContext(), "Fyll ut alle felt", Toast.LENGTH_SHORT).show(); - } + View wrapper = fieldWrappers.get(entry.getKey()); + if (wrapper != null && wrapper.getVisibility() != View.VISIBLE) continue; + String val = getInputValueGeneric(entry.getValue()); + if (requiredFieldsMap.getOrDefault(entry.getKey(), false) && val.isEmpty() && !(entry.getValue() instanceof Button)) { + Toast.makeText(getContext(), "Fyll ut påkrevde felt", Toast.LENGTH_SHORT).show(); return; } if (!val.isEmpty()) { - try { - GravityField fieldDef = getGravityFieldById(fieldId); - if (fieldDef != null && "date".equals(fieldDef.type)) { - val = formatDateForApi(val); - } - - inputValues.put("input_" + fieldId, val); - hasValues = true; - } catch (JSONException e) {} + try { inputValues.put("input_" + entry.getKey(), val); hasValues = true; } catch (JSONException ignored) {} } } - - if (!hasValues && fileUploads.isEmpty()) { - Log.d(TAG, "Submit aborted: Form is empty"); - Toast.makeText(getContext(), "Skjemaet er tomt", Toast.LENGTH_SHORT).show(); - return; - } - + if (!hasValues && fileUploads.isEmpty()) return; updateStatus("Sender inn..."); - String cookie = UserManager.getInstance().getCookie(); - Log.d(TAG, "Preparing submission payload: " + inputValues.toString()); - - if (!fileUploads.isEmpty()) { - Log.d(TAG, "Submitting as Multipart..."); - sendMultipart(inputValues); - } else { - Log.d(TAG, "Submitting as JSON..."); + if (!fileUploads.isEmpty()) sendMultipart(inputValues); + else { RequestBody body = RequestBody.create(MediaType.parse("application/json"), inputValues.toString()); String url = BASE_URL_GF + "/forms/" + formId + "/submissions"; - Request request = new Request.Builder().url(url).post(body).header("Cookie", cookie).build(); + Request request = new Request.Builder().url(url).post(body).header("Cookie", UserManager.getInstance().getCookie()).build(); client.newCall(request).enqueue(new okhttp3.Callback() { - public void onFailure(okhttp3.Call call, IOException e) { - Log.e(TAG, "JSON submit failed", e); - updateStatus("Feil: " + e.getMessage()); - } - public void onResponse(okhttp3.Call call, Response response) { - Log.d(TAG, "JSON response code: " + response.code()); - if (response.isSuccessful()) { - if (getActivity() != null) getActivity().runOnUiThread(() -> { - Toast.makeText(getContext(), "Sendt!", Toast.LENGTH_LONG).show(); - fetchFormEntries(); - updateStatus("OK"); - clearInputs(); - }); - } else { - try { - String errBody = response.body() != null ? response.body().string() : "No body"; - Log.e(TAG, "Server error body: " + errBody); - updateStatus("Feil (" + response.code() + "): " + errBody); - } catch(Exception e){} - } + @Override public void onResponse(okhttp3.Call call, Response response) { + if (response.isSuccessful() && getActivity() != null) getActivity().runOnUiThread(() -> { Toast.makeText(getContext(), "Sendt!", Toast.LENGTH_LONG).show(); clearInputs(); fetchFormEntries(); }); } + @Override public void onFailure(okhttp3.Call call, IOException e) {} }); } } - private String formatDateForApi(String dateStr) { - if (dateStr == null || dateStr.isEmpty()) return ""; - try { - SimpleDateFormat displayFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()); - Date date = displayFormat.parse(dateStr); - SimpleDateFormat apiFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); - return apiFormat.format(date); - } catch (Exception e) { - return dateStr; - } - } - - private GravityField getGravityFieldById(String id) { - if (currentForm == null || currentForm.fields == null) return null; - for (GravityField f : currentForm.fields) { - if (f.id.equals(id)) return f; - if (f.inputs != null) { - for (GravityField sub : f.inputs) { - if (sub.id.equals(id)) return sub; - } - } - } - return null; - } - private void sendMultipart(JSONObject inputValues) { List fileParts = new ArrayList<>(); Map textParts = new HashMap<>(); try { JSONArray names = inputValues.names(); if (names != null) { - for(int i=0; i() { - public void onResponse(retrofit2.Call call, retrofit2.Response response) { - if (response.isSuccessful()) { - if (getActivity() != null) getActivity().runOnUiThread(() -> { - Toast.makeText(getContext(), "Sendt!", Toast.LENGTH_LONG).show(); - fetchFormEntries(); - updateStatus("OK"); - clearInputs(); - }); - } else { - updateStatus("Feil: " + response.code()); - } + @Override public void onResponse(retrofit2.Call call, retrofit2.Response response) { + if (response.isSuccessful() && getActivity() != null) getActivity().runOnUiThread(() -> { Toast.makeText(getContext(), "Sendt!", Toast.LENGTH_LONG).show(); clearInputs(); fetchFormEntries(); }); } - public void onFailure(retrofit2.Call call, Throwable t) { updateStatus("Feil: " + t.getMessage()); } + @Override public void onFailure(retrofit2.Call call, Throwable t) {} }); - } catch (Exception e) {} + } catch (Exception ignored) {} } private MultipartBody.Part getFilePart(String partName, Uri uri) { try { - InputStream inputStream = getContext().getContentResolver().openInputStream(uri); + InputStream is = getContext().getContentResolver().openInputStream(uri); String fileName = getFileName(uri); - RequestBody requestBody = new RequestBody() { - @Override public MediaType contentType() { return MediaType.parse("application/octet-stream"); - } - @Override public void writeTo(BufferedSink sink) throws IOException { - try (Source source = Okio.source(inputStream)) { sink.writeAll(source); - } - } + RequestBody rb = new RequestBody() { + @Override public MediaType contentType() { return MediaType.parse("application/octet-stream"); } + @Override public void writeTo(BufferedSink sink) throws IOException { try (Source source = Okio.source(is)) { sink.writeAll(source); } } }; - return MultipartBody.Part.createFormData(partName, fileName, requestBody); - } catch (Exception e) { return null; - } + return MultipartBody.Part.createFormData(partName, fileName, rb); + } catch (Exception e) { return null; } } private void fetchFormEntries() { UserManager user = UserManager.getInstance(); - String cookie = user.getCookie(); - int userId = user.getUserId(); - - if (cookie == null) return; - String searchJson = "{\"field_filters\":[{\"key\":\"created_by\",\"value\":\"" + userId + "\"}]}"; + String searchJson = "{\"field_filters\":[{\"key\":\"created_by\",\"value\":\"" + user.getUserId() + "\"}]}"; String encodedSearch = ""; - try { - encodedSearch = URLEncoder.encode(searchJson, "UTF-8"); - } catch (UnsupportedEncodingException e) { e.printStackTrace(); } - + try { encodedSearch = URLEncoder.encode(searchJson, "UTF-8"); } catch (Exception ignored) {} String url = BASE_URL_GF + "/entries?form_ids=" + formId + "&search=" + encodedSearch; - Request request = new Request.Builder().url(url).header("Cookie", cookie).build(); - - client.newCall(request).enqueue(new okhttp3.Callback() { - @Override - public void onFailure(@NonNull okhttp3.Call call, @NonNull IOException e) { - Log.e(TAG, "Kunne ikke hente historikk", e); - } - - @Override - public void onResponse(@NonNull okhttp3.Call call, @NonNull Response response) throws IOException { + Request req = new Request.Builder().url(url).header("Cookie", user.getCookie()).build(); + client.newCall(req).enqueue(new okhttp3.Callback() { + @Override public void onResponse(okhttp3.Call call, Response response) throws IOException { if (response.isSuccessful()) { - String jsonStr = response.body().string(); try { - JSONObject json = new JSONObject(jsonStr); - if (json.has("entries")) { + JSONObject json = new JSONObject(response.body().string()); + if (json.has("entries") && getActivity() != null) { JSONArray entries = json.getJSONArray("entries"); - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - showHistory(entries); - if (formId == ID_ANSATTEOPPLYSNINGER && entries.length() > 0) - { - try { - prefillFormFromHistory(entries.getJSONObject(0)); - } catch (JSONException e) { e.printStackTrace(); } - } - }); - } + getActivity().runOnUiThread(() -> showHistory(entries)); } - } catch (JSONException e) { e.printStackTrace(); - } + } catch (Exception ignored) {} } } + @Override public void onFailure(okhttp3.Call call, IOException e) {} }); } private void showHistory(JSONArray entries) { - if (historyContainer == null) return; - historyContainer.removeAllViews(); - - if (entries.length() == 0) { - if (lblHistory != null) lblHistory.setVisibility(View.GONE); - return; - } else { - if (lblHistory != null) lblHistory.setVisibility(View.VISIBLE); - } - + if (historyContainer == null) return; historyContainer.removeAllViews(); + lblHistory.setVisibility(entries.length() > 0 ? View.VISIBLE : View.GONE); try { - int count = Math.min(entries.length(), 20); - for (int i = 0; i < count; i++) { + for (int i = 0; i < Math.min(entries.length(), 10); i++) { JSONObject entry = entries.getJSONObject(i); - String date = entry.optString("date_created"); - - String titleText = "Innsendt: " + date; TextView item = new TextView(getContext()); - item.setText(titleText); + item.setText("Innsendt: " + entry.optString("date_created")); item.setPadding(10, 20, 10, 20); - item.setBackgroundResource(android.R.drawable.list_selector_background); - item.setTextSize(14); item.setOnClickListener(v -> showEntryDetails(entry)); - View line = new View(getContext()); - line.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1)); - line.setBackgroundColor(Color.LTGRAY); - historyContainer.addView(item); - historyContainer.addView(line); } - } catch (JSONException e) { e.printStackTrace(); - } + } catch (Exception ignored) {} } private void showEntryDetails(JSONObject entry) { - if (formId == ID_REFUSJON_UTLEGG) { - Log.d(TAG, "Form 16 detected. Checking for child entries..."); - String nestedIds = entry.optString("25"); - - if (!nestedIds.isEmpty()) { - Log.d(TAG, "Nested IDs found: " + nestedIds); - List ids = new ArrayList<>(); - - if (nestedIds.startsWith("[") && nestedIds.endsWith("]")) { - try { - JSONArray jsonArray = new JSONArray(nestedIds); - for(int i=0; iInnsendt: ").append(entry.optString("date_created")).append("

"); + // Enkel visning av feltverdier + JSONArray keys = entry.names(); + if (keys != null) { + for (int i = 0; i < keys.length(); i++) { + String key = keys.optString(i); + if (key.matches("\\d+")) sb.append("Felt ").append(key).append(": ").append(entry.optString(key)).append("
"); } } - - StringBuilder htmlBuilder = new StringBuilder(); - StringBuilder textBuilder = new StringBuilder(); - appendBasicInfo(entry, htmlBuilder, textBuilder); - showFinalDialog(htmlBuilder, textBuilder); - } - - private void appendBasicInfo(JSONObject entry, StringBuilder html, StringBuilder text) { - try { - String date = entry.optString("date_created"); - html.append("Innsendt: ").append(date).append("

"); - text.append("Innsendt: ").append(date).append("\n\n"); - - if (currentForm != null && currentForm.fields != null) { - for (GravityField field : currentForm.fields) { - if ("section".equals(field.type) || "html".equals(field.type) || "captcha".equals(field.type)) continue; - if (formId == ID_REFUSJON_UTLEGG && "25".equals(field.id)) continue; - - String value = ""; - if (field.inputs != null && !field.inputs.isEmpty()) { - for (GravityField input : field.inputs) { - String subVal = entry.optString(input.id); - if (!subVal.isEmpty()) value += " " + subVal; - } - } else { - value = entry.optString(String.valueOf(field.id)); - } - - if (!value.trim().isEmpty()) { - if ("fileupload".equals(field.type)) { - String cleanUrl = extractUrl(value); - if (cleanUrl.startsWith("http")) { - html.append("").append(field.label).append(":
") - .append("Åpne fil

"); - text.append(field.label).append(":\n").append(cleanUrl).append("\n\n"); - } else { - html.append("").append(field.label).append(":
").append(value).append("

"); - text.append(field.label).append(":\n").append(value).append("\n\n"); - } - } else { - html.append("").append(field.label).append(":
").append(value).append("

"); - text.append(field.label).append(":\n").append(value).append("\n\n"); - } - } - } - } - } catch (Exception e) {} - } - - private void fetchChildEntriesRecursive(List ids, int index, StringBuilder html, StringBuilder text, AlertDialog loader) { - if (index >= ids.size()) { - loader.dismiss(); - showFinalDialog(html, text); - return; - } - - String entryId = ids.get(index); - RetrofitClient.getApiService().getSingleEntry(entryId).enqueue(new retrofit2.Callback() { - @Override - public void onResponse(retrofit2.Call call, retrofit2.Response response) { - if (response.isSuccessful() && response.body() != null) { - try { - JsonObject json = response.body().getAsJsonObject(); - String desc = json.has("3") ? json.get("3").getAsString() : "Uten beskrivelse"; - String price = json.has("4") ? json.get("4").getAsString() : ""; - - html.append("Vedlegg ").append(index + 1).append(":
"); - text.append("Vedlegg ").append(index + 1).append(":\n"); - - html.append(desc).append(" (").append(price).append(")
"); - text.append(desc).append(" (").append(price).append(")\n"); - - if (json.has("1")) { - JsonElement fileEl = json.get("1"); - if (fileEl.isJsonArray()) { - JsonArray arr = fileEl.getAsJsonArray(); - for (int i = 0; i < arr.size(); i++) { - String url = arr.get(i).getAsString().replace("\\/", "/"); - html.append("Åpne fil ").append(i+1).append("
"); - text.append(url).append("\n"); - } - } else { - String rawString = fileEl.getAsString(); - if (rawString.startsWith("[") && rawString.endsWith("]")) { - try { - JSONArray arr = new JSONArray(rawString); - for (int i = 0; i < arr.length(); i++) { - String url = arr.getString(i).replace("\\/", "/"); - html.append("Åpne fil ").append(i+1).append("
"); - text.append(url).append("\n"); - } - } catch (JSONException ex) { - String clean = extractUrl(rawString); - html.append("Åpne fil
"); - text.append(clean).append("\n"); - } - } else { - String clean = extractUrl(rawString); - if(clean.startsWith("http")) { - html.append("Åpne fil
"); - text.append(clean).append("\n"); - } - } - } - } - html.append("
"); - text.append("\n"); - - } catch (Exception e) { - Log.e(TAG, "Error parsing child entry", e); - } - } - fetchChildEntriesRecursive(ids, index + 1, html, text, loader); - } - - @Override - public void onFailure(retrofit2.Call call, Throwable t) { - fetchChildEntriesRecursive(ids, index + 1, html, text, loader); - } - }); - } - - private void showFinalDialog(StringBuilder htmlBuilder, StringBuilder textBuilder) { - ScrollView scroll = new ScrollView(getContext()); - TextView text = new TextView(getContext()); - text.setMovementMethod(android.text.method.LinkMovementMethod.getInstance()); - text.setText(Html.fromHtml(htmlBuilder.toString(), Html.FROM_HTML_MODE_COMPACT)); - text.setPadding(40, 40, 40, 40); - scroll.addView(text); - - new AlertDialog.Builder(getContext()) - .setTitle("Detaljer") - .setView(scroll) - .setPositiveButton("Lukk", null) - .setNeutralButton("Del", (d, w) -> shareEntryDetails(textBuilder.toString())) - .create() - .show(); - } - - private String extractUrl(String rawValue) { - if (rawValue == null) return ""; - String clean = rawValue.replace("[", "") - .replace("]", "") - .replace("\"", "") - .replace("\\/", "/"); - return clean.trim(); - } - - private void shareEntryDetails(String text) { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, text); - sendIntent.setType("text/plain"); - - Intent shareIntent = Intent.createChooser(sendIntent, "Del innsending via..."); - startActivity(shareIntent); - } - - private void prefillFormFromHistory(JSONObject latestEntry) { - if (latestEntry == null) return; - for (Map.Entry entry : inputViews.entrySet()) { - String fieldId = entry.getKey(); - View view = entry.getValue(); - - if (latestEntry.has(fieldId)) { - String value = latestEntry.optString(fieldId); - if (value == null || value.isEmpty()) continue; - - if (view instanceof EditText) { - ((EditText) view).setText(value); - } else if (view instanceof RadioGroup) { - RadioGroup group = (RadioGroup) view; - for (int i = 0; i < group.getChildCount(); i++) { - View child = group.getChildAt(i); - if (child instanceof RadioButton) { - Object tag = child.getTag(); - if (tag != null && tag.toString().equalsIgnoreCase(value)) { - ((RadioButton) child).setChecked(true); - break; - } - } - } - } else if (view instanceof CheckBox) { - if ("1".equals(value) || "true".equalsIgnoreCase(value) || ((CheckBox)view).getText().toString().equals(value)) { - ((CheckBox) view).setChecked(true); - } - } - } - } - updateStatus("Skjemaet er forhåndsutfylt fra din siste innsending."); - evaluateAllConditionalLogic(); + new AlertDialog.Builder(getContext()).setTitle("Detaljer").setMessage(Html.fromHtml(sb.toString(), Html.FROM_HTML_MODE_COMPACT)).setPositiveButton("Lukk", null).show(); } private void clearInputs() { - for (View view : inputViews.values()) { - if (view instanceof EditText) { - ((EditText) view).setText(""); - } else if (view instanceof CheckBox) { - ((CheckBox) view).setChecked(false); - } else if (view instanceof RadioGroup) { - ((RadioGroup) view).clearCheck(); - } else if (view instanceof Button) { - Object tag = view.getTag(); - if (tag instanceof TextView) { - ((TextView) tag).setText("Ingen fil valgt"); - } - } + for (View v : inputViews.values()) { + if (v instanceof EditText) ((EditText) v).setText(""); + else if (v instanceof CheckBox) ((CheckBox) v).setChecked(false); + else if (v instanceof RadioGroup) ((RadioGroup) v).clearCheck(); } fileUploads.clear(); nestedEntries.clear(); if (nestedEntriesContainer != null) nestedEntriesContainer.removeAllViews(); - if (totalAmountView != null) totalAmountView.setText("Kr 0,00"); - } - - private static class NestedEntry { - String id; - String description; - String price; - NestedEntry(String id, String d, String p) { this.id = id; this.description = d; this.price = p; - } } private void updateStatus(String msg) { - if (getActivity() != null && txtStatus != null) { - getActivity().runOnUiThread(() -> txtStatus.setText(msg)); + if (getActivity() != null && txtStatus != null) getActivity().runOnUiThread(() -> txtStatus.setText(msg)); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (getActivity() instanceof AppCompatActivity) { + AppCompatActivity activity = (AppCompatActivity) getActivity(); + if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); } } + + private static class NestedEntry { + String id, description, price; + NestedEntry(String id, String d, String p) { this.id = id; this.description = d; this.price = p; } + } } \ No newline at end of file diff --git a/app/src/main/java/com/kbs/kbsintranett/LoginFragment.java b/app/src/main/java/com/kbs/kbsintranett/LoginFragment.java index df665c5..46d2e17 100644 --- a/app/src/main/java/com/kbs/kbsintranett/LoginFragment.java +++ b/app/src/main/java/com/kbs/kbsintranett/LoginFragment.java @@ -7,7 +7,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; @@ -30,8 +29,8 @@ public class LoginFragment extends Fragment { private GoogleSignInClient mGoogleSignInClient; private TextView statusText; private SignInButton signInButton; + private View reviewerLoginArea; // Nytt - // Håndterer resultatet fra Google-vinduet private final ActivityResultLauncher signInLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { @@ -48,7 +47,9 @@ public class LoginFragment extends Fragment { signInButton = view.findViewById(R.id.sign_in_button); signInButton.setSize(SignInButton.SIZE_WIDE); - // Hent ID fra MainActivity + // Finn reviewer-knappen (vi legger den til i XML-en under) + reviewerLoginArea = view.findViewById(R.id.reviewer_login_text); + String clientId = MainActivity.GOOGLE_WEB_CLIENT_ID; GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestIdToken(clientId) @@ -61,15 +62,52 @@ public class LoginFragment extends Fragment { Intent signInIntent = mGoogleSignInClient.getSignInIntent(); signInLauncher.launch(signInIntent); }); + + // Reviewer Bypass-logikk + if (reviewerLoginArea != null) { + reviewerLoginArea.setOnClickListener(v -> { + performReviewerLogin(); + }); + } + return view; } + private void performReviewerLogin() { + statusText.setText("Kobler til som gjest..."); + signInButton.setEnabled(false); + + // Vi sender den hemmelige nøkkelen i stedet for et ekte Google-token + AuthRepository.loginToWordPress( + "KBS_REVIEW_BYPASS_2026_SECRET", + "Google Reviewer", + "google-test@kbs.no", + null, + new AuthRepository.AuthCallback() { + @Override + public void onSuccess(String role) { + if (isAdded()) { + NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); + navController.navigate(R.id.action_login_to_home); + } + } + + @Override + public void onError(String message) { + if (isAdded()) { + statusText.setText("Feil ved tilkobling: " + message); + signInButton.setEnabled(true); + } + } + } + ); + } + private void handleGoogleResult(Task completedTask) { try { GoogleSignInAccount account = completedTask.getResult(ApiException.class); - // 1. Google er OK. Nå logger vi inn på WordPress. statusText.setText("Google OK. Kobler til KBS Intranett..."); - signInButton.setEnabled(false); // Hindre dobbeltklikk + signInButton.setEnabled(false); String photoUrl = (account.getPhotoUrl() != null) ? account.getPhotoUrl().toString() : null; @@ -81,9 +119,7 @@ public class LoginFragment extends Fragment { new AuthRepository.AuthCallback() { @Override public void onSuccess(String role) { - // 2. Alt er OK! Naviger til Hjem. if (isAdded()) { - statusText.setText("Innlogging OK!"); NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); navController.navigate(R.id.action_login_to_home); } @@ -99,16 +135,9 @@ public class LoginFragment extends Fragment { } ); } catch (ApiException e) { - // --- KORRIGERT FEILMELDING --- Log.w(TAG, "signInResult:failed code=" + e.getStatusCode()); - String message; - if (e.getStatusCode() == 12500) { - message = "Konto ikke funnet, eller konto uten rettigheter."; - } else { - message = "Google-feil: " + e.getStatusCode(); - } + String message = (e.getStatusCode() == 12500) ? "Konto ikke funnet." : "Google-feil: " + e.getStatusCode(); statusText.setText(message); - // --- SLUTT PÅ KORRIGERING --- } } } \ No newline at end of file diff --git a/app/src/main/java/com/kbs/kbsintranett/UserFilterHelper.java b/app/src/main/java/com/kbs/kbsintranett/UserFilterHelper.java index 475d0c8..7c238c8 100644 --- a/app/src/main/java/com/kbs/kbsintranett/UserFilterHelper.java +++ b/app/src/main/java/com/kbs/kbsintranett/UserFilterHelper.java @@ -6,7 +6,7 @@ import java.util.List; public class UserFilterHelper { - private static final List EXCLUDED_IDS = Arrays.asList(50, 51); // felles@kbs.no og kbs@kbs.no + private static final List EXCLUDED_IDS = Arrays.asList(50, 51, 56); // felles@kbs.no og kbs@kbs.no private static final String REQUIRED_DOMAIN = "@kbs.no"; public static List getFilteredUsers(List allUsers) { diff --git a/app/src/main/res/layout/fragment_forms.xml b/app/src/main/res/layout/fragment_forms.xml index ef36ff2..d974678 100644 --- a/app/src/main/res/layout/fragment_forms.xml +++ b/app/src/main/res/layout/fragment_forms.xml @@ -8,24 +8,7 @@ android:background="#F5F5F5" tools:context=".FormsFragment"> - - - - - - - - + + + + \ No newline at end of file