]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorFragment.java
more pronounced day/month delimiters in the transaction list
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / TemplateDetailSourceSelectorFragment.java
1 /*
2  * Copyright © 2021 Damyan Ivanov.
3  * This file is part of MoLe.
4  * MoLe is free software: you can distribute it and/or modify it
5  * under the term of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your opinion), any later version.
8  *
9  * MoLe is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License terms for details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
16  */
17
18 package net.ktnx.mobileledger.ui;
19
20 import android.app.Dialog;
21 import android.content.Context;
22 import android.os.Bundle;
23 import android.view.LayoutInflater;
24 import android.view.View;
25
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 import androidx.annotation.StringRes;
29 import androidx.appcompat.app.AppCompatDialogFragment;
30 import androidx.lifecycle.ViewModelProvider;
31 import androidx.recyclerview.widget.GridLayoutManager;
32 import androidx.recyclerview.widget.LinearLayoutManager;
33 import androidx.recyclerview.widget.RecyclerView;
34
35 import net.ktnx.mobileledger.R;
36 import net.ktnx.mobileledger.databinding.FragmentTemplateDetailSourceSelectorListBinding;
37 import net.ktnx.mobileledger.model.TemplateDetailSource;
38 import net.ktnx.mobileledger.utils.Logger;
39 import net.ktnx.mobileledger.utils.Misc;
40
41 import java.util.ArrayList;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44
45 /**
46  * A fragment representing a list of Items.
47  * <p/>
48  * Activities containing this fragment MUST implement the {@link OnSourceSelectedListener}
49  * interface.
50  */
51 public class TemplateDetailSourceSelectorFragment extends AppCompatDialogFragment
52         implements OnSourceSelectedListener {
53
54     public static final int DEFAULT_COLUMN_COUNT = 1;
55     public static final String ARG_COLUMN_COUNT = "column-count";
56     public static final String ARG_PATTERN = "pattern";
57     public static final String ARG_TEST_TEXT = "test-text";
58     private int mColumnCount = DEFAULT_COLUMN_COUNT;
59     private ArrayList<TemplateDetailSource> mSources;
60     private TemplateDetailSourceSelectorModel model;
61     private OnSourceSelectedListener onSourceSelectedListener;
62     private @StringRes
63     int mPatternProblem;
64
65     /**
66      * Mandatory empty constructor for the fragment manager to instantiate the
67      * fragment (e.g. upon screen orientation changes).
68      */
69     public TemplateDetailSourceSelectorFragment() {
70     }
71     @SuppressWarnings("unused")
72     public static TemplateDetailSourceSelectorFragment newInstance() {
73         return newInstance(DEFAULT_COLUMN_COUNT, null, null);
74     }
75     public static TemplateDetailSourceSelectorFragment newInstance(int columnCount,
76                                                                    @Nullable String pattern,
77                                                                    @Nullable String testText) {
78         TemplateDetailSourceSelectorFragment fragment = new TemplateDetailSourceSelectorFragment();
79         Bundle args = new Bundle();
80         args.putInt(ARG_COLUMN_COUNT, columnCount);
81         if (pattern != null)
82             args.putString(ARG_PATTERN, pattern);
83         if (testText != null)
84             args.putString(ARG_TEST_TEXT, testText);
85         fragment.setArguments(args);
86         return fragment;
87     }
88     @Override
89     public void onCreate(Bundle savedInstanceState) {
90         super.onCreate(savedInstanceState);
91
92         if (getArguments() != null) {
93             mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT, DEFAULT_COLUMN_COUNT);
94             final String patternText = getArguments().getString(ARG_PATTERN);
95             final String testText = getArguments().getString(ARG_TEST_TEXT);
96             if (Misc.emptyIsNull(patternText) == null) {
97                 mPatternProblem = R.string.missing_pattern_error;
98             }
99             else {
100                 if (Misc.emptyIsNull(testText) == null) {
101                     mPatternProblem = R.string.missing_test_text;
102                 }
103                 else {
104                     Pattern pattern = Pattern.compile(patternText);
105                     Matcher matcher = pattern.matcher(testText);
106                     Logger.debug("templates",
107                             String.format("Trying to match pattern '%s' against text '%s'",
108                                     patternText, testText));
109                     if (matcher.find()) {
110                         if (matcher.groupCount() >= 0) {
111                             ArrayList<TemplateDetailSource> list = new ArrayList<>();
112                             for (short g = 1; g <= matcher.groupCount(); g++) {
113                                 list.add(new TemplateDetailSource(g, matcher.group(g)));
114                             }
115                             mSources = list;
116                         }
117                         else {
118                             mPatternProblem = R.string.pattern_without_groups;
119                         }
120                     }
121                     else {
122                         mPatternProblem = R.string.pattern_does_not_match;
123                     }
124                 }
125             }
126         }
127     }
128     @NonNull
129     @Override
130     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
131         Context context = requireContext();
132         Dialog csd = new Dialog(context);
133         FragmentTemplateDetailSourceSelectorListBinding b =
134                 FragmentTemplateDetailSourceSelectorListBinding.inflate(
135                         LayoutInflater.from(context));
136         csd.setContentView(b.getRoot());
137         csd.setTitle(R.string.choose_template_detail_source_label);
138
139         if (mSources != null && !mSources.isEmpty()) {
140             RecyclerView recyclerView = b.list;
141
142             if (mColumnCount <= 1) {
143                 recyclerView.setLayoutManager(new LinearLayoutManager(context));
144             }
145             else {
146                 recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
147             }
148             model = new ViewModelProvider(this).get(TemplateDetailSourceSelectorModel.class);
149             if (onSourceSelectedListener != null)
150                 model.setOnSourceSelectedListener(onSourceSelectedListener);
151             model.setSourcesList(mSources);
152
153             TemplateDetailSourceSelectorRecyclerViewAdapter adapter =
154                     new TemplateDetailSourceSelectorRecyclerViewAdapter();
155             model.groups.observe(this, adapter::submitList);
156
157             recyclerView.setAdapter(adapter);
158             adapter.setSourceSelectedListener(this);
159         }
160         else {
161             b.list.setVisibility(View.GONE);
162             b.templateError.setText(
163                     (mPatternProblem != 0) ? mPatternProblem : R.string.pattern_without_groups);
164             b.templateError.setVisibility(View.VISIBLE);
165         }
166
167         b.literalButton.setOnClickListener(v -> onSourceSelected(true, (short) -1));
168
169         return csd;
170     }
171     public void setOnSourceSelectedListener(OnSourceSelectedListener listener) {
172         onSourceSelectedListener = listener;
173
174         if (model != null)
175             model.setOnSourceSelectedListener(listener);
176     }
177     public void resetOnSourceSelectedListener() {
178         model.resetOnSourceSelectedListener();
179     }
180     @Override
181     public void onSourceSelected(boolean literal, short group) {
182         if (model != null)
183             model.triggerOnSourceSelectedListener(literal, group);
184         if (onSourceSelectedListener != null)
185             onSourceSelectedListener.onSourceSelected(literal, group);
186
187         dismiss();
188     }
189 }