2 * Copyright © 2020 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.
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.
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/>.
18 package net.ktnx.mobileledger.ui.profiles;
20 import android.text.TextUtils;
22 import androidx.lifecycle.LifecycleOwner;
23 import androidx.lifecycle.MutableLiveData;
24 import androidx.lifecycle.Observer;
25 import androidx.lifecycle.ViewModel;
27 import net.ktnx.mobileledger.async.SendTransactionTask;
28 import net.ktnx.mobileledger.model.Currency;
29 import net.ktnx.mobileledger.model.HledgerVersion;
30 import net.ktnx.mobileledger.model.MobileLedgerProfile;
31 import net.ktnx.mobileledger.utils.Colors;
32 import net.ktnx.mobileledger.utils.Logger;
33 import net.ktnx.mobileledger.utils.Misc;
34 import net.ktnx.mobileledger.utils.NetworkUtil;
36 import java.io.BufferedReader;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.InputStreamReader;
40 import java.net.HttpURLConnection;
41 import java.util.Locale;
42 import java.util.Objects;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
46 public class ProfileDetailModel extends ViewModel {
47 private static final String HTTPS_URL_START = "https://";
48 private final MutableLiveData<String> profileName = new MutableLiveData<>();
49 private final MutableLiveData<Boolean> postingPermitted = new MutableLiveData<>(true);
50 private final MutableLiveData<Currency> defaultCommodity = new MutableLiveData<>(null);
51 private final MutableLiveData<MobileLedgerProfile.FutureDates> futureDates =
52 new MutableLiveData<>(MobileLedgerProfile.FutureDates.None);
53 private final MutableLiveData<Boolean> showCommodityByDefault = new MutableLiveData<>(false);
54 private final MutableLiveData<Boolean> showCommentsByDefault = new MutableLiveData<>(true);
55 private final MutableLiveData<Boolean> useAuthentication = new MutableLiveData<>(false);
56 private final MutableLiveData<SendTransactionTask.API> apiVersion =
57 new MutableLiveData<>(SendTransactionTask.API.auto);
58 private final MutableLiveData<String> url = new MutableLiveData<>(null);
59 private final MutableLiveData<String> authUserName = new MutableLiveData<>(null);
60 private final MutableLiveData<String> authPassword = new MutableLiveData<>(null);
61 private final MutableLiveData<String> preferredAccountsFilter = new MutableLiveData<>(null);
62 private final MutableLiveData<Integer> themeId = new MutableLiveData<>(-1);
63 private final MutableLiveData<HledgerVersion> detectedVersion = new MutableLiveData<>(null);
64 public int initialThemeHue = Colors.DEFAULT_HUE_DEG;
65 private VersionDetectionThread versionDetectionThread;
66 public ProfileDetailModel() {
68 String getProfileName() {
69 return profileName.getValue();
71 void setProfileName(String newValue) {
72 if (!Misc.nullIsEmpty(newValue)
73 .equals(Misc.nullIsEmpty(profileName.getValue())))
74 profileName.setValue(newValue);
76 void setProfileName(CharSequence newValue) {
77 setProfileName(String.valueOf(newValue));
79 void observeProfileName(LifecycleOwner lfo, Observer<String> o) {
80 profileName.observe(lfo, o);
82 Boolean getPostingPermitted() {
83 return postingPermitted.getValue();
85 void setPostingPermitted(boolean newValue) {
86 if (newValue != postingPermitted.getValue())
87 postingPermitted.setValue(newValue);
89 void observePostingPermitted(LifecycleOwner lfo, Observer<Boolean> o) {
90 postingPermitted.observe(lfo, o);
92 public void setShowCommentsByDefault(boolean newValue) {
93 if (newValue != showCommentsByDefault.getValue())
94 showCommentsByDefault.setValue(newValue);
96 void observeShowCommentsByDefault(LifecycleOwner lfo, Observer<Boolean> o) {
97 showCommentsByDefault.observe(lfo, o);
99 MobileLedgerProfile.FutureDates getFutureDates() {
100 return futureDates.getValue();
102 void setFutureDates(MobileLedgerProfile.FutureDates newValue) {
103 if (newValue != futureDates.getValue())
104 futureDates.setValue(newValue);
106 void observeFutureDates(LifecycleOwner lfo, Observer<MobileLedgerProfile.FutureDates> o) {
107 futureDates.observe(lfo, o);
109 Currency getDefaultCommodity() {
110 return defaultCommodity.getValue();
112 void setDefaultCommodity(Currency newValue) {
113 if (newValue != defaultCommodity.getValue())
114 defaultCommodity.setValue(newValue);
116 void observeDefaultCommodity(LifecycleOwner lfo, Observer<Currency> o) {
117 defaultCommodity.observe(lfo, o);
119 Boolean getShowCommodityByDefault() {
120 return showCommodityByDefault.getValue();
122 void setShowCommodityByDefault(boolean newValue) {
123 if (newValue != showCommodityByDefault.getValue())
124 showCommodityByDefault.setValue(newValue);
126 void observeShowCommodityByDefault(LifecycleOwner lfo, Observer<Boolean> o) {
127 showCommodityByDefault.observe(lfo, o);
129 Boolean getUseAuthentication() {
130 return useAuthentication.getValue();
132 void setUseAuthentication(boolean newValue) {
133 if (newValue != useAuthentication.getValue())
134 useAuthentication.setValue(newValue);
136 void observeUseAuthentication(LifecycleOwner lfo, Observer<Boolean> o) {
137 useAuthentication.observe(lfo, o);
139 SendTransactionTask.API getApiVersion() {
140 return apiVersion.getValue();
142 void setApiVersion(SendTransactionTask.API newValue) {
143 if (newValue != apiVersion.getValue())
144 apiVersion.setValue(newValue);
146 void observeApiVersion(LifecycleOwner lfo, Observer<SendTransactionTask.API> o) {
147 apiVersion.observe(lfo, o);
149 HledgerVersion getDetectedVersion() { return detectedVersion.getValue(); }
150 void setDetectedVersion(HledgerVersion newValue) {
151 if (!Objects.equals(detectedVersion.getValue(), newValue))
152 detectedVersion.setValue(newValue);
154 void observeDetectedVersion(LifecycleOwner lfo, Observer<HledgerVersion> o) {
155 detectedVersion.observe(lfo, o);
158 return url.getValue();
160 void setUrl(String newValue) {
161 if (!Misc.nullIsEmpty(newValue)
162 .equals(Misc.nullIsEmpty(url.getValue())))
163 url.setValue(newValue);
165 void setUrl(CharSequence newValue) {
166 setUrl(String.valueOf(newValue));
168 void observeUrl(LifecycleOwner lfo, Observer<String> o) {
171 String getAuthUserName() {
172 return authUserName.getValue();
174 void setAuthUserName(String newValue) {
175 if (!Misc.nullIsEmpty(newValue)
176 .equals(Misc.nullIsEmpty(authUserName.getValue())))
177 authUserName.setValue(newValue);
179 void setAuthUserName(CharSequence newValue) {
180 setAuthUserName(String.valueOf(newValue));
182 void observeUserName(LifecycleOwner lfo, Observer<String> o) {
183 authUserName.observe(lfo, o);
185 String getAuthPassword() {
186 return authPassword.getValue();
188 void setAuthPassword(String newValue) {
189 if (!Misc.nullIsEmpty(newValue)
190 .equals(Misc.nullIsEmpty(authPassword.getValue())))
191 authPassword.setValue(newValue);
193 void setAuthPassword(CharSequence newValue) {
194 setAuthPassword(String.valueOf(newValue));
196 void observePassword(LifecycleOwner lfo, Observer<String> o) {
197 authPassword.observe(lfo, o);
199 String getPreferredAccountsFilter() {
200 return preferredAccountsFilter.getValue();
202 void setPreferredAccountsFilter(String newValue) {
203 if (!Misc.nullIsEmpty(newValue)
204 .equals(Misc.nullIsEmpty(preferredAccountsFilter.getValue())))
205 preferredAccountsFilter.setValue(newValue);
207 void setPreferredAccountsFilter(CharSequence newValue) {
208 setPreferredAccountsFilter(String.valueOf(newValue));
210 void observePreferredAccountsFilter(LifecycleOwner lfo, Observer<String> o) {
211 preferredAccountsFilter.observe(lfo, o);
214 return themeId.getValue();
216 void setThemeId(int newValue) {
217 themeId.setValue(newValue);
219 void observeThemeId(LifecycleOwner lfo, Observer<Integer> o) {
220 themeId.observe(lfo, o);
222 void setValuesFromProfile(MobileLedgerProfile mProfile, int newProfileHue) {
223 final int profileThemeId;
224 if (mProfile != null) {
225 profileName.setValue(mProfile.getName());
226 postingPermitted.setValue(mProfile.isPostingPermitted());
227 showCommentsByDefault.setValue(mProfile.getShowCommentsByDefault());
228 showCommodityByDefault.setValue(mProfile.getShowCommodityByDefault());
230 String comm = mProfile.getDefaultCommodity();
231 if (TextUtils.isEmpty(comm))
232 setDefaultCommodity(null);
234 setDefaultCommodity(new Currency(-1, comm));
236 futureDates.setValue(mProfile.getFutureDates());
237 apiVersion.setValue(mProfile.getApiVersion());
238 url.setValue(mProfile.getUrl());
239 useAuthentication.setValue(mProfile.isAuthEnabled());
240 authUserName.setValue(mProfile.isAuthEnabled() ? mProfile.getAuthUserName() : "");
241 authPassword.setValue(mProfile.isAuthEnabled() ? mProfile.getAuthPassword() : "");
242 preferredAccountsFilter.setValue(mProfile.getPreferredAccountsFilter());
243 themeId.setValue(mProfile.getThemeHue());
244 detectedVersion.setValue(mProfile.getDetectedVersion());
247 profileName.setValue(null);
248 url.setValue(HTTPS_URL_START);
249 postingPermitted.setValue(true);
250 showCommentsByDefault.setValue(true);
251 showCommodityByDefault.setValue(false);
252 setFutureDates(MobileLedgerProfile.FutureDates.None);
253 setApiVersion(SendTransactionTask.API.auto);
254 useAuthentication.setValue(false);
255 authUserName.setValue("");
256 authPassword.setValue("");
257 preferredAccountsFilter.setValue(null);
258 themeId.setValue(newProfileHue);
259 detectedVersion.setValue(null);
262 void updateProfile(MobileLedgerProfile mProfile) {
263 mProfile.setName(profileName.getValue());
264 mProfile.setUrl(url.getValue());
265 mProfile.setPostingPermitted(postingPermitted.getValue());
266 mProfile.setShowCommentsByDefault(showCommentsByDefault.getValue());
267 Currency c = defaultCommodity.getValue();
268 mProfile.setDefaultCommodity((c == null) ? null : c.getName());
269 mProfile.setShowCommodityByDefault(showCommodityByDefault.getValue());
270 mProfile.setPreferredAccountsFilter(preferredAccountsFilter.getValue());
271 mProfile.setAuthEnabled(useAuthentication.getValue());
272 mProfile.setAuthUserName(authUserName.getValue());
273 mProfile.setAuthPassword(authPassword.getValue());
274 mProfile.setThemeHue(themeId.getValue());
275 mProfile.setFutureDates(futureDates.getValue());
276 mProfile.setApiVersion(apiVersion.getValue());
277 mProfile.setDetectedVersion(detectedVersion.getValue());
279 synchronized public void triggerVersionDetection() {
280 if (versionDetectionThread != null)
281 versionDetectionThread.interrupt();
283 versionDetectionThread = new VersionDetectionThread(this);
284 versionDetectionThread.start();
286 static class VersionDetectionThread extends Thread {
287 private final Pattern versionPattern =
288 Pattern.compile("^\"(\\d+)\\.(\\d+)(?:\\.(\\d+))?\"$");
289 private final ProfileDetailModel model;
290 public VersionDetectionThread(ProfileDetailModel model) {
296 HttpURLConnection http = NetworkUtil.prepareConnection(model.getUrl(), "version",
297 model.getUseAuthentication());
298 switch (http.getResponseCode()) {
302 model.detectedVersion.postValue(new HledgerVersion(true));
305 Logger.debug("profile", String.format(Locale.US,
306 "HTTP error detecting hledger-web version: [%d] %s",
307 http.getResponseCode(), http.getResponseMessage()));
308 model.detectedVersion.postValue(null);
311 InputStream stream = http.getInputStream();
312 BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
313 String version = reader.readLine();
314 Matcher m = versionPattern.matcher(version);
316 int major = Integer.parseInt(Objects.requireNonNull(m.group(1)));
317 int minor = Integer.parseInt(Objects.requireNonNull(m.group(2)));
318 final boolean hasPatch = m.groupCount() >= 3;
319 int patch = hasPatch ? Integer.parseInt(Objects.requireNonNull(m.group(3))) : 0;
321 model.detectedVersion.postValue(
322 hasPatch ? new HledgerVersion(major, minor, patch)
323 : new HledgerVersion(major, minor));
326 Logger.debug("profile",
327 String.format("Unrecognised version string '%s'", version));
330 catch (IOException e) {