android:supportsRtl="true"
tools:ignore="GoogleAppIndexingWarning">
<activity
- android:name=".ui.patterns.PatternsActivity"
- android:label="@string/title_activity_patterns"
+ android:name=".ui.templates.TemplatesActivity"
+ android:label="@string/title_activity_templates"
android:theme="@style/AppTheme.default" />
<activity
android:name=".ui.activity.SplashActivity"
import androidx.room.Query;
import androidx.room.Update;
-import net.ktnx.mobileledger.db.PatternAccount;
+import net.ktnx.mobileledger.db.TemplateAccount;
import java.util.List;
@Dao
public interface PatternAccountDAO {
@Insert
- Long insert(PatternAccount item);
+ Long insert(TemplateAccount item);
@Update
- void update(PatternAccount... items);
+ void update(TemplateAccount... items);
@Delete
- void delete(PatternAccount item);
+ void delete(TemplateAccount item);
@Query("SELECT * FROM pattern_accounts WHERE pattern_id=:pattern_id")
- LiveData<List<PatternAccount>> getPatternAccounts(Long pattern_id);
+ LiveData<List<TemplateAccount>> getPatternAccounts(Long pattern_id);
@Query("SELECT * FROM pattern_accounts WHERE id = :id")
- LiveData<PatternAccount> getPatternAccountById(Long id);
+ LiveData<TemplateAccount> getPatternAccountById(Long id);
// not useful for now
// @Transaction
import androidx.room.Transaction;
import androidx.room.Update;
-import net.ktnx.mobileledger.db.PatternHeader;
import net.ktnx.mobileledger.db.PatternWithAccounts;
+import net.ktnx.mobileledger.db.TemplateHeader;
import java.util.List;
@Dao
public interface PatternHeaderDAO {
@Insert()
- long insert(PatternHeader item);
+ long insert(TemplateHeader item);
@Update
- void update(PatternHeader... items);
+ void update(TemplateHeader... items);
@Delete
- void delete(PatternHeader item);
+ void delete(TemplateHeader item);
@Query("SELECT * FROM patterns ORDER BY UPPER(name)")
- LiveData<List<PatternHeader>> getPatterns();
+ LiveData<List<TemplateHeader>> getPatterns();
@Query("SELECT * FROM patterns WHERE id = :id")
- LiveData<PatternHeader> getPattern(Long id);
+ LiveData<TemplateHeader> getPattern(Long id);
@Transaction
@Query("SELECT * FROM patterns WHERE id = :id")
import net.ktnx.mobileledger.dao.PatternHeaderDAO;
import net.ktnx.mobileledger.utils.MobileLedgerDatabase;
-@Database(version = 53, entities = {PatternHeader.class, PatternAccount.class, Currency.class})
+@Database(version = 53, entities = {TemplateHeader.class, TemplateAccount.class, Currency.class})
abstract public class DB extends RoomDatabase {
private static DB instance;
public static DB get() {
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.db;
-
-import androidx.annotation.NonNull;
-import androidx.room.ColumnInfo;
-import androidx.room.Entity;
-import androidx.room.ForeignKey;
-import androidx.room.Index;
-import androidx.room.PrimaryKey;
-
-import org.jetbrains.annotations.NotNull;
-
-@Entity(tableName = "pattern_accounts",
- indices = {@Index(name = "un_pattern_accounts", unique = true, value = "id"),
- @Index(name = "fk_pattern_accounts_pattern", value = "pattern_id"),
- @Index(name = "fk_pattern_accounts_currency", value = "currency")
- }, foreignKeys = {@ForeignKey(childColumns = "pattern_id", parentColumns = "id",
- entity = PatternHeader.class),
- @ForeignKey(childColumns = "currency", parentColumns = "id",
- entity = Currency.class)
-})
-public class PatternAccount extends PatternBase {
- @NonNull
- @ColumnInfo(name = "pattern_id")
- private Long patternId;
- @PrimaryKey(autoGenerate = true)
- @NotNull
- private Long id;
- @ColumnInfo(name = "acc")
- private String accountName;
- @ColumnInfo(name = "position")
- @NonNull
- private Long position;
- @ColumnInfo(name = "acc_match_group")
- private Integer accountNameMatchGroup;
- @ColumnInfo(name = "currency")
- private Integer currency;
- @ColumnInfo(name = "currency_match_group")
- private Integer currencyMatchGroup;
- @ColumnInfo(name = "amount")
- private Float amount;
- @ColumnInfo(name = "amount_match_group")
- private Integer amountMatchGroup;
- @ColumnInfo(name = "comment")
- private String accountComment;
- @ColumnInfo(name = "comment_match_group")
- private Integer accountCommentMatchGroup;
- @ColumnInfo(name = "negate_amount")
- private Boolean negateAmount;
- public PatternAccount(@NotNull Long id, @NonNull Long patternId, @NonNull Long position) {
- this.id = id;
- this.patternId = patternId;
- this.position = position;
- }
- public Long getId() {
- return id;
- }
- public void setId(Long id) {
- this.id = id;
- }
- public Boolean getNegateAmount() {
- return negateAmount;
- }
- public void setNegateAmount(Boolean negateAmount) {
- this.negateAmount = negateAmount;
- }
- public @NotNull Long getPatternId() {
- return patternId;
- }
- public void setPatternId(@NonNull Long patternId) {
- this.patternId = patternId;
- }
- @NonNull
- public String getAccountName() {
- return accountName;
- }
- public void setAccountName(@NonNull String accountName) {
- this.accountName = accountName;
- }
- @NonNull
- public Long getPosition() {
- return position;
- }
- public void setPosition(@NonNull Long position) {
- this.position = position;
- }
- public void setPosition(int position) {
- this.position = (long) position;
- }
- public Integer getAccountNameMatchGroup() {
- return accountNameMatchGroup;
- }
- public void setAccountNameMatchGroup(Integer accountNameMatchGroup) {
- this.accountNameMatchGroup = accountNameMatchGroup;
- }
- public Integer getCurrency() {
- return currency;
- }
- public void setCurrency(Integer currency) {
- this.currency = currency;
- }
- public Integer getCurrencyMatchGroup() {
- return currencyMatchGroup;
- }
- public void setCurrencyMatchGroup(Integer currencyMatchGroup) {
- this.currencyMatchGroup = currencyMatchGroup;
- }
- public Float getAmount() {
- return amount;
- }
- public void setAmount(Float amount) {
- this.amount = amount;
- }
- public Integer getAmountMatchGroup() {
- return amountMatchGroup;
- }
- public void setAmountMatchGroup(Integer amountMatchGroup) {
- this.amountMatchGroup = amountMatchGroup;
- }
- public String getAccountComment() {
- return accountComment;
- }
- public void setAccountComment(String accountComment) {
- this.accountComment = accountComment;
- }
- public Integer getAccountCommentMatchGroup() {
- return accountCommentMatchGroup;
- }
- public void setAccountCommentMatchGroup(Integer accountCommentMatchGroup) {
- this.accountCommentMatchGroup = accountCommentMatchGroup;
- }
-}
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.db;
-
-public class PatternBase {}
+++ /dev/null
-package net.ktnx.mobileledger.db;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.room.ColumnInfo;
-import androidx.room.Entity;
-import androidx.room.Index;
-import androidx.room.PrimaryKey;
-
-import net.ktnx.mobileledger.utils.Misc;
-
-import org.jetbrains.annotations.NotNull;
-
-@Entity(tableName = "patterns",
- indices = {@Index(name = "un_patterns_id", value = "id", unique = true)})
-public class PatternHeader extends PatternBase {
- @PrimaryKey(autoGenerate = true)
- @NonNull
- private Long id;
- @ColumnInfo(name = "name")
- @NonNull
- private String name;
- @NonNull
- @ColumnInfo(name = "regular_expression")
- private String regularExpression;
- @ColumnInfo(name = "test_text")
- private String testText;
- @ColumnInfo(name = "transaction_description")
- private String transactionDescription;
- @ColumnInfo(name = "transaction_description_match_group")
- private Integer transactionDescriptionMatchGroup;
- @ColumnInfo(name = "transaction_comment")
- private String transactionComment;
- @ColumnInfo(name = "transaction_comment_match_group")
- private Integer transactionCommentMatchGroup;
- @ColumnInfo(name = "date_year")
- private Integer dateYear;
- @ColumnInfo(name = "date_year_match_group")
- private Integer dateYearMatchGroup;
- @ColumnInfo(name = "date_month")
- private Integer dateMonth;
- @ColumnInfo(name = "date_month_match_group")
- private Integer dateMonthMatchGroup;
- @ColumnInfo(name = "date_day")
- private Integer dateDay;
- @ColumnInfo(name = "date_day_match_group")
- private Integer dateDayMatchGroup;
- public PatternHeader(@NotNull Long id, @NonNull String name,
- @NonNull String regularExpression) {
- this.id = id;
- this.name = name;
- this.regularExpression = regularExpression;
- }
- public String getTestText() {
- return testText;
- }
- public void setTestText(String testText) {
- this.testText = testText;
- }
- public Integer getTransactionDescriptionMatchGroup() {
- return transactionDescriptionMatchGroup;
- }
- public void setTransactionDescriptionMatchGroup(Integer transactionDescriptionMatchGroup) {
- this.transactionDescriptionMatchGroup = transactionDescriptionMatchGroup;
- }
- public Integer getTransactionCommentMatchGroup() {
- return transactionCommentMatchGroup;
- }
- public void setTransactionCommentMatchGroup(Integer transactionCommentMatchGroup) {
- this.transactionCommentMatchGroup = transactionCommentMatchGroup;
- }
- public Integer getDateYear() {
- return dateYear;
- }
- public void setDateYear(Integer dateYear) {
- this.dateYear = dateYear;
- }
- public Integer getDateMonth() {
- return dateMonth;
- }
- public void setDateMonth(Integer dateMonth) {
- this.dateMonth = dateMonth;
- }
- public Integer getDateDay() {
- return dateDay;
- }
- public void setDateDay(Integer dateDay) {
- this.dateDay = dateDay;
- }
- @NonNull
- public Long getId() {
- return id;
- }
- public void setId(@NonNull Long id) {
- this.id = id;
- }
- @NonNull
- public String getName() {
- return name;
- }
- public void setName(@NonNull String name) {
- this.name = name;
- }
- @NonNull
- public String getRegularExpression() {
- return regularExpression;
- }
- public void setRegularExpression(@NonNull String regularExpression) {
- this.regularExpression = regularExpression;
- }
- public String getTransactionDescription() {
- return transactionDescription;
- }
- public void setTransactionDescription(String transactionDescription) {
- this.transactionDescription = transactionDescription;
- }
- public String getTransactionComment() {
- return transactionComment;
- }
- public void setTransactionComment(String transactionComment) {
- this.transactionComment = transactionComment;
- }
- public Integer getDateYearMatchGroup() {
- return dateYearMatchGroup;
- }
- public void setDateYearMatchGroup(Integer dateYearMatchGroup) {
- this.dateYearMatchGroup = dateYearMatchGroup;
- }
- public Integer getDateMonthMatchGroup() {
- return dateMonthMatchGroup;
- }
- public void setDateMonthMatchGroup(Integer dateMonthMatchGroup) {
- this.dateMonthMatchGroup = dateMonthMatchGroup;
- }
- public Integer getDateDayMatchGroup() {
- return dateDayMatchGroup;
- }
- public void setDateDayMatchGroup(Integer dateDayMatchGroup) {
- this.dateDayMatchGroup = dateDayMatchGroup;
- }
- @Override
- public boolean equals(@Nullable Object obj) {
- if (obj == null)
- return false;
- if (!(obj instanceof PatternHeader))
- return false;
-
- PatternHeader o = (PatternHeader) obj;
-
- return Misc.equalLongs(id, o.id) && Misc.equalStrings(name, o.name) &&
- Misc.equalStrings(regularExpression, o.regularExpression) &&
- Misc.equalStrings(transactionDescription, o.transactionDescription) &&
- Misc.equalStrings(transactionComment, o.transactionComment) &&
- Misc.equalIntegers(transactionDescriptionMatchGroup,
- o.transactionDescriptionMatchGroup) &&
- Misc.equalIntegers(transactionCommentMatchGroup, o.transactionCommentMatchGroup) &&
- Misc.equalIntegers(dateDay, o.dateDay) &&
- Misc.equalIntegers(dateDayMatchGroup, o.dateDayMatchGroup) &&
- Misc.equalIntegers(dateMonth, o.dateMonth) &&
- Misc.equalIntegers(dateMonthMatchGroup, o.dateMonthMatchGroup) &&
- Misc.equalIntegers(dateYear, o.dateYear) &&
- Misc.equalIntegers(dateYearMatchGroup, o.dateYearMatchGroup);
- }
-}
public class PatternWithAccounts {
@Embedded
- public PatternHeader header;
+ public TemplateHeader header;
@Relation(parentColumn = "id", entityColumn = "pattern_id")
- public List<PatternAccount> accounts;
+ public List<TemplateAccount> accounts;
public Long getId() {
return header.getId();
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.db;
+
+import androidx.annotation.NonNull;
+import androidx.room.ColumnInfo;
+import androidx.room.Entity;
+import androidx.room.ForeignKey;
+import androidx.room.Index;
+import androidx.room.PrimaryKey;
+
+import org.jetbrains.annotations.NotNull;
+
+@Entity(tableName = "pattern_accounts",
+ indices = {@Index(name = "un_pattern_accounts", unique = true, value = "id"),
+ @Index(name = "fk_pattern_accounts_pattern", value = "pattern_id"),
+ @Index(name = "fk_pattern_accounts_currency", value = "currency")
+ }, foreignKeys = {@ForeignKey(childColumns = "pattern_id", parentColumns = "id",
+ entity = TemplateHeader.class),
+ @ForeignKey(childColumns = "currency", parentColumns = "id",
+ entity = Currency.class)
+})
+public class TemplateAccount extends TemplateBase {
+ @NonNull
+ @ColumnInfo(name = "pattern_id")
+ private Long patternId;
+ @PrimaryKey(autoGenerate = true)
+ @NotNull
+ private Long id;
+ @ColumnInfo(name = "acc")
+ private String accountName;
+ @ColumnInfo(name = "position")
+ @NonNull
+ private Long position;
+ @ColumnInfo(name = "acc_match_group")
+ private Integer accountNameMatchGroup;
+ @ColumnInfo(name = "currency")
+ private Integer currency;
+ @ColumnInfo(name = "currency_match_group")
+ private Integer currencyMatchGroup;
+ @ColumnInfo(name = "amount")
+ private Float amount;
+ @ColumnInfo(name = "amount_match_group")
+ private Integer amountMatchGroup;
+ @ColumnInfo(name = "comment")
+ private String accountComment;
+ @ColumnInfo(name = "comment_match_group")
+ private Integer accountCommentMatchGroup;
+ @ColumnInfo(name = "negate_amount")
+ private Boolean negateAmount;
+ public TemplateAccount(@NotNull Long id, @NonNull Long patternId, @NonNull Long position) {
+ this.id = id;
+ this.patternId = patternId;
+ this.position = position;
+ }
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public Boolean getNegateAmount() {
+ return negateAmount;
+ }
+ public void setNegateAmount(Boolean negateAmount) {
+ this.negateAmount = negateAmount;
+ }
+ public @NotNull Long getPatternId() {
+ return patternId;
+ }
+ public void setPatternId(@NonNull Long patternId) {
+ this.patternId = patternId;
+ }
+ @NonNull
+ public String getAccountName() {
+ return accountName;
+ }
+ public void setAccountName(@NonNull String accountName) {
+ this.accountName = accountName;
+ }
+ @NonNull
+ public Long getPosition() {
+ return position;
+ }
+ public void setPosition(@NonNull Long position) {
+ this.position = position;
+ }
+ public void setPosition(int position) {
+ this.position = (long) position;
+ }
+ public Integer getAccountNameMatchGroup() {
+ return accountNameMatchGroup;
+ }
+ public void setAccountNameMatchGroup(Integer accountNameMatchGroup) {
+ this.accountNameMatchGroup = accountNameMatchGroup;
+ }
+ public Integer getCurrency() {
+ return currency;
+ }
+ public void setCurrency(Integer currency) {
+ this.currency = currency;
+ }
+ public Integer getCurrencyMatchGroup() {
+ return currencyMatchGroup;
+ }
+ public void setCurrencyMatchGroup(Integer currencyMatchGroup) {
+ this.currencyMatchGroup = currencyMatchGroup;
+ }
+ public Float getAmount() {
+ return amount;
+ }
+ public void setAmount(Float amount) {
+ this.amount = amount;
+ }
+ public Integer getAmountMatchGroup() {
+ return amountMatchGroup;
+ }
+ public void setAmountMatchGroup(Integer amountMatchGroup) {
+ this.amountMatchGroup = amountMatchGroup;
+ }
+ public String getAccountComment() {
+ return accountComment;
+ }
+ public void setAccountComment(String accountComment) {
+ this.accountComment = accountComment;
+ }
+ public Integer getAccountCommentMatchGroup() {
+ return accountCommentMatchGroup;
+ }
+ public void setAccountCommentMatchGroup(Integer accountCommentMatchGroup) {
+ this.accountCommentMatchGroup = accountCommentMatchGroup;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.db;
+
+public class TemplateBase {}
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.db;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.room.ColumnInfo;
+import androidx.room.Entity;
+import androidx.room.Index;
+import androidx.room.PrimaryKey;
+
+import net.ktnx.mobileledger.utils.Misc;
+
+import org.jetbrains.annotations.NotNull;
+
+@Entity(tableName = "patterns",
+ indices = {@Index(name = "un_patterns_id", value = "id", unique = true)})
+public class TemplateHeader extends TemplateBase {
+ @PrimaryKey(autoGenerate = true)
+ @NonNull
+ private Long id;
+ @ColumnInfo(name = "name")
+ @NonNull
+ private String name;
+ @NonNull
+ @ColumnInfo(name = "regular_expression")
+ private String regularExpression;
+ @ColumnInfo(name = "test_text")
+ private String testText;
+ @ColumnInfo(name = "transaction_description")
+ private String transactionDescription;
+ @ColumnInfo(name = "transaction_description_match_group")
+ private Integer transactionDescriptionMatchGroup;
+ @ColumnInfo(name = "transaction_comment")
+ private String transactionComment;
+ @ColumnInfo(name = "transaction_comment_match_group")
+ private Integer transactionCommentMatchGroup;
+ @ColumnInfo(name = "date_year")
+ private Integer dateYear;
+ @ColumnInfo(name = "date_year_match_group")
+ private Integer dateYearMatchGroup;
+ @ColumnInfo(name = "date_month")
+ private Integer dateMonth;
+ @ColumnInfo(name = "date_month_match_group")
+ private Integer dateMonthMatchGroup;
+ @ColumnInfo(name = "date_day")
+ private Integer dateDay;
+ @ColumnInfo(name = "date_day_match_group")
+ private Integer dateDayMatchGroup;
+ public TemplateHeader(@NotNull Long id, @NonNull String name,
+ @NonNull String regularExpression) {
+ this.id = id;
+ this.name = name;
+ this.regularExpression = regularExpression;
+ }
+ public String getTestText() {
+ return testText;
+ }
+ public void setTestText(String testText) {
+ this.testText = testText;
+ }
+ public Integer getTransactionDescriptionMatchGroup() {
+ return transactionDescriptionMatchGroup;
+ }
+ public void setTransactionDescriptionMatchGroup(Integer transactionDescriptionMatchGroup) {
+ this.transactionDescriptionMatchGroup = transactionDescriptionMatchGroup;
+ }
+ public Integer getTransactionCommentMatchGroup() {
+ return transactionCommentMatchGroup;
+ }
+ public void setTransactionCommentMatchGroup(Integer transactionCommentMatchGroup) {
+ this.transactionCommentMatchGroup = transactionCommentMatchGroup;
+ }
+ public Integer getDateYear() {
+ return dateYear;
+ }
+ public void setDateYear(Integer dateYear) {
+ this.dateYear = dateYear;
+ }
+ public Integer getDateMonth() {
+ return dateMonth;
+ }
+ public void setDateMonth(Integer dateMonth) {
+ this.dateMonth = dateMonth;
+ }
+ public Integer getDateDay() {
+ return dateDay;
+ }
+ public void setDateDay(Integer dateDay) {
+ this.dateDay = dateDay;
+ }
+ @NonNull
+ public Long getId() {
+ return id;
+ }
+ public void setId(@NonNull Long id) {
+ this.id = id;
+ }
+ @NonNull
+ public String getName() {
+ return name;
+ }
+ public void setName(@NonNull String name) {
+ this.name = name;
+ }
+ @NonNull
+ public String getRegularExpression() {
+ return regularExpression;
+ }
+ public void setRegularExpression(@NonNull String regularExpression) {
+ this.regularExpression = regularExpression;
+ }
+ public String getTransactionDescription() {
+ return transactionDescription;
+ }
+ public void setTransactionDescription(String transactionDescription) {
+ this.transactionDescription = transactionDescription;
+ }
+ public String getTransactionComment() {
+ return transactionComment;
+ }
+ public void setTransactionComment(String transactionComment) {
+ this.transactionComment = transactionComment;
+ }
+ public Integer getDateYearMatchGroup() {
+ return dateYearMatchGroup;
+ }
+ public void setDateYearMatchGroup(Integer dateYearMatchGroup) {
+ this.dateYearMatchGroup = dateYearMatchGroup;
+ }
+ public Integer getDateMonthMatchGroup() {
+ return dateMonthMatchGroup;
+ }
+ public void setDateMonthMatchGroup(Integer dateMonthMatchGroup) {
+ this.dateMonthMatchGroup = dateMonthMatchGroup;
+ }
+ public Integer getDateDayMatchGroup() {
+ return dateDayMatchGroup;
+ }
+ public void setDateDayMatchGroup(Integer dateDayMatchGroup) {
+ this.dateDayMatchGroup = dateDayMatchGroup;
+ }
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (obj == null)
+ return false;
+ if (!(obj instanceof TemplateHeader))
+ return false;
+
+ TemplateHeader o = (TemplateHeader) obj;
+
+ return Misc.equalLongs(id, o.id) && Misc.equalStrings(name, o.name) &&
+ Misc.equalStrings(regularExpression, o.regularExpression) &&
+ Misc.equalStrings(transactionDescription, o.transactionDescription) &&
+ Misc.equalStrings(transactionComment, o.transactionComment) &&
+ Misc.equalIntegers(transactionDescriptionMatchGroup,
+ o.transactionDescriptionMatchGroup) &&
+ Misc.equalIntegers(transactionCommentMatchGroup, o.transactionCommentMatchGroup) &&
+ Misc.equalIntegers(dateDay, o.dateDay) &&
+ Misc.equalIntegers(dateDayMatchGroup, o.dateDayMatchGroup) &&
+ Misc.equalIntegers(dateMonth, o.dateMonth) &&
+ Misc.equalIntegers(dateMonthMatchGroup, o.dateMonthMatchGroup) &&
+ Misc.equalIntegers(dateYear, o.dateYear) &&
+ Misc.equalIntegers(dateYearMatchGroup, o.dateYearMatchGroup);
+ }
+}
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.model;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.DiffUtil;
-
-import java.io.Serializable;
-
-public class PatternDetailSource implements Serializable {
- public static final DiffUtil.ItemCallback<PatternDetailSource> DIFF_CALLBACK =
- new DiffUtil.ItemCallback<PatternDetailSource>() {
- @Override
- public boolean areItemsTheSame(@NonNull PatternDetailSource oldItem,
- @NonNull PatternDetailSource newItem) {
- return oldItem.groupNumber == newItem.groupNumber;
- }
- @Override
- public boolean areContentsTheSame(@NonNull PatternDetailSource oldItem,
- @NonNull PatternDetailSource newItem) {
- return oldItem.matchedText.equals(newItem.matchedText);
- }
- };
-
- private short groupNumber;
- private String matchedText;
- public PatternDetailSource() {
- }
- public PatternDetailSource(short groupNumber, String matchedText) {
- this.groupNumber = groupNumber;
- this.matchedText = matchedText;
- }
- public short getGroupNumber() {
- return groupNumber;
- }
- public void setGroupNumber(short groupNumber) {
- this.groupNumber = groupNumber;
- }
- public String getMatchedText() {
- return matchedText;
- }
- public void setMatchedText(String matchedText) {
- this.matchedText = matchedText;
- }
-}
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.model;
-
-import android.content.res.Resources;
-
-import androidx.annotation.NonNull;
-
-import net.ktnx.mobileledger.R;
-import net.ktnx.mobileledger.db.PatternAccount;
-import net.ktnx.mobileledger.db.PatternBase;
-import net.ktnx.mobileledger.db.PatternHeader;
-import net.ktnx.mobileledger.utils.Misc;
-
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-abstract public class PatternDetailsItem {
- private final Type type;
- protected Long id;
- protected Long position;
-
- protected PatternDetailsItem(Type type) {
- this.type = type;
- }
- @Contract(" -> new")
- public static @NotNull PatternDetailsItem.Header createHeader() {
- return new Header();
- }
- public static @NotNull PatternDetailsItem.Header createHeader(Header origin) {
- return new Header(origin);
- }
- @Contract("-> new")
- public static @NotNull PatternDetailsItem.AccountRow createAccountRow() {
- return new AccountRow();
- }
- public static PatternDetailsItem fromRoomObject(PatternBase p) {
- if (p instanceof PatternHeader) {
- PatternHeader ph = (PatternHeader) p;
- Header header = createHeader();
- header.setId(ph.getId());
- header.setName(ph.getName());
- header.setPattern(ph.getRegularExpression());
- header.setTestText(ph.getTestText());
-
- if (ph.getTransactionDescriptionMatchGroup() == null)
- header.setTransactionDescription(ph.getTransactionDescription());
- else
- header.setTransactionDescriptionMatchGroup(
- ph.getTransactionDescriptionMatchGroup());
-
- if (ph.getTransactionCommentMatchGroup() == null)
- header.setTransactionComment(ph.getTransactionComment());
- else
- header.setTransactionCommentMatchGroup(ph.getTransactionCommentMatchGroup());
-
- if (ph.getDateDayMatchGroup() == null)
- header.setDateDay(ph.getDateDay());
- else
- header.setDateDayMatchGroup(ph.getDateDayMatchGroup());
-
- if (ph.getDateMonthMatchGroup() == null)
- header.setDateMonth(ph.getDateMonth());
- else
- header.setDateMonthMatchGroup(ph.getDateMonthMatchGroup());
-
- if (ph.getDateYearMatchGroup() == null)
- header.setDateYear(ph.getDateYear());
- else
- header.setDateYearMatchGroup(ph.getDateYearMatchGroup());
-
- return header;
- }
- else if (p instanceof PatternAccount) {
- PatternAccount pa = (PatternAccount) p;
- AccountRow acc = createAccountRow();
- acc.setId(pa.getId());
-
- if (pa.getAccountNameMatchGroup() == null)
- acc.setAccountName(Misc.nullIsEmpty(pa.getAccountName()));
- else
- acc.setAccountNameMatchGroup(pa.getAccountNameMatchGroup());
-
- if (pa.getAccountCommentMatchGroup() == null)
- acc.setAccountComment(Misc.nullIsEmpty(pa.getAccountComment()));
- else
- acc.setAccountCommentMatchGroup(pa.getAccountCommentMatchGroup());
-
- if (pa.getCurrencyMatchGroup() == null) {
- final Integer currencyId = pa.getCurrency();
- if (currencyId != null && currencyId > 0)
- acc.setCurrency(Currency.loadById(currencyId));
- }
- else
- acc.setCurrencyMatchGroup(pa.getCurrencyMatchGroup());
-
- final Integer amountMatchGroup = pa.getAmountMatchGroup();
- if (amountMatchGroup != null && amountMatchGroup > 0) {
- acc.setAmountMatchGroup(amountMatchGroup);
- final Boolean negateAmount = pa.getNegateAmount();
- acc.setNegateAmount(negateAmount != null && negateAmount);
- }
- else
- acc.setAmount(pa.getAmount());
-
- return acc;
- }
- else {
- throw new IllegalStateException("Unexpected item class " + p.getClass());
- }
- }
- public Header asHeaderItem() {
- ensureType(Type.HEADER);
- return (Header) this;
- }
- public AccountRow asAccountRowItem() {
- ensureType(Type.ACCOUNT_ITEM);
- return (AccountRow) this;
- }
- private void ensureType(Type type) {
- if (this.type != type)
- throw new IllegalStateException(
- String.format("Type is %s, but %s is required", this.type.toString(),
- type.toString()));
- }
- void ensureTrue(boolean flag) {
- if (!flag)
- throw new IllegalStateException(
- "Literal value requested, but it is matched via a pattern group");
- }
- void ensureFalse(boolean flag) {
- if (flag)
- throw new IllegalStateException("Matching group requested, but the value is a literal");
- }
- public long getId() {
- return id;
- }
- public void setId(Long id) {
- this.id = id;
- }
- public void setId(int id) {
- this.id = (long) id;
- }
- public long getPosition() {
- return position;
- }
- public void setPosition(Long position) {
- this.position = position;
- }
- abstract public String getProblem(@NonNull Resources r, int patternGroupCount);
- public Type getType() {
- return type;
- }
- public enum Type {
- HEADER(TYPE.header), ACCOUNT_ITEM(TYPE.accountItem);
- final int index;
- Type(int i) {
- index = i;
- }
- public int toInt() {
- return index;
- }
- }
-
- static class PossiblyMatchedValue<T> {
- private boolean literalValue;
- private T value;
- private int matchGroup;
- public PossiblyMatchedValue() {
- literalValue = true;
- value = null;
- }
- public PossiblyMatchedValue(@NonNull PossiblyMatchedValue<T> origin) {
- literalValue = origin.literalValue;
- value = origin.value;
- matchGroup = origin.matchGroup;
- }
- @NonNull
- public static PossiblyMatchedValue<Integer> withLiteralInt(Integer initialValue) {
- PossiblyMatchedValue<Integer> result = new PossiblyMatchedValue<>();
- result.setValue(initialValue);
- return result;
- }
- @NonNull
- public static PossiblyMatchedValue<Float> withLiteralFloat(Float initialValue) {
- PossiblyMatchedValue<Float> result = new PossiblyMatchedValue<>();
- result.setValue(initialValue);
- return result;
- }
- public static PossiblyMatchedValue<Short> withLiteralShort(Short initialValue) {
- PossiblyMatchedValue<Short> result = new PossiblyMatchedValue<>();
- result.setValue(initialValue);
- return result;
- }
- @NonNull
- public static PossiblyMatchedValue<String> withLiteralString(String initialValue) {
- PossiblyMatchedValue<String> result = new PossiblyMatchedValue<>();
- result.setValue(initialValue);
- return result;
- }
- public T getValue() {
- if (!literalValue)
- throw new IllegalStateException("Value is not literal");
- return value;
- }
- public void setValue(T newValue) {
- value = newValue;
- literalValue = true;
- }
- public boolean hasLiteralValue() {
- return literalValue;
- }
- public int getMatchGroup() {
- if (literalValue)
- throw new IllegalStateException("Value is literal");
- return matchGroup;
- }
- public void setMatchGroup(int group) {
- this.matchGroup = group;
- literalValue = false;
- }
- public boolean equals(PossiblyMatchedValue<T> other) {
- if (!other.literalValue == literalValue)
- return false;
- if (literalValue) {
- if (value == null)
- return other.value == null;
- return value.equals(other.value);
- }
- else
- return matchGroup == other.matchGroup;
- }
- public void switchToLiteral() {
- literalValue = true;
- }
- public String toString() {
- if (literalValue)
- if (value == null)
- return "<null>";
- else
- return value.toString();
- if (matchGroup > 0)
- return "grp:" + matchGroup;
- return "<null>";
- }
- }
-
- public static class TYPE {
- public static final int header = 0;
- public static final int accountItem = 1;
- }
-
- public static class AccountRow extends PatternDetailsItem {
- private final PossiblyMatchedValue<String> accountName =
- PossiblyMatchedValue.withLiteralString("");
- private final PossiblyMatchedValue<String> accountComment =
- PossiblyMatchedValue.withLiteralString("");
- private final PossiblyMatchedValue<Float> amount =
- PossiblyMatchedValue.withLiteralFloat(0f);
- private final PossiblyMatchedValue<Currency> currency = new PossiblyMatchedValue<>();
- private boolean negateAmount;
- private AccountRow() {
- super(Type.ACCOUNT_ITEM);
- }
- public boolean isNegateAmount() {
- return negateAmount;
- }
- public void setNegateAmount(boolean negateAmount) {
- this.negateAmount = negateAmount;
- }
- public int getAccountCommentMatchGroup() {
- return accountComment.getMatchGroup();
- }
- public void setAccountCommentMatchGroup(int group) {
- accountComment.setMatchGroup(group);
- }
- public String getAccountComment() {
- return accountComment.getValue();
- }
- public void setAccountComment(String comment) {
- this.accountComment.setValue(comment);
- }
- public int getCurrencyMatchGroup() {
- return currency.getMatchGroup();
- }
- public void setCurrencyMatchGroup(int group) {
- currency.setMatchGroup(group);
- }
- public Currency getCurrency() {
- return currency.getValue();
- }
- public void setCurrency(Currency currency) {
- this.currency.setValue(currency);
- }
- public int getAccountNameMatchGroup() {
- return accountName.getMatchGroup();
- }
- public void setAccountNameMatchGroup(int group) {
- accountName.setMatchGroup(group);
- }
- public String getAccountName() {
- return accountName.getValue();
- }
- public void setAccountName(String accountName) {
- this.accountName.setValue(accountName);
- }
- public boolean hasLiteralAccountName() { return accountName.hasLiteralValue(); }
- public boolean hasLiteralAmount() {
- return amount.hasLiteralValue();
- }
- public int getAmountMatchGroup() {
- return amount.getMatchGroup();
- }
- public void setAmountMatchGroup(int group) {
- amount.setMatchGroup(group);
- }
- public Float getAmount() {
- return amount.getValue();
- }
- public void setAmount(Float amount) {
- this.amount.setValue(amount);
- }
- public String getProblem(@NonNull Resources r, int patternGroupCount) {
- if (Misc.emptyIsNull(accountName.getValue()) == null)
- return r.getString(R.string.account_name_is_empty);
- if (!amount.hasLiteralValue() &&
- (amount.getMatchGroup() < 1 || amount.getMatchGroup() > patternGroupCount))
- return r.getString(R.string.invalid_matching_group_number);
-
- return null;
- }
- public boolean hasLiteralAccountComment() {
- return accountComment.hasLiteralValue();
- }
- public boolean equalContents(AccountRow o) {
- return amount.equals(o.amount) && accountName.equals(o.accountName) &&
- accountComment.equals(o.accountComment) && negateAmount == o.negateAmount;
- }
- public void switchToLiteralAmount() {
- amount.switchToLiteral();
- }
- public void switchToLiteralAccountName() {
- accountName.switchToLiteral();
- }
- public void switchToLiteralAccountComment() {
- accountComment.switchToLiteral();
- }
- public PatternAccount toDBO(@NonNull Long patternId) {
- PatternAccount result = new PatternAccount(id, patternId, position);
-
- if (accountName.hasLiteralValue())
- result.setAccountName(accountName.getValue());
- else
- result.setAccountNameMatchGroup(accountName.getMatchGroup());
-
- if (accountComment.hasLiteralValue())
- result.setAccountComment(accountComment.getValue());
- else
- result.setAccountCommentMatchGroup(accountComment.getMatchGroup());
-
- if (amount.hasLiteralValue()) {
- result.setAmount(amount.getValue());
- result.setNegateAmount(null);
- }
- else {
- result.setAmountMatchGroup(amount.getMatchGroup());
- result.setNegateAmount(negateAmount ? true : null);
- }
-
- return result;
- }
- }
-
- public static class Header extends PatternDetailsItem {
- private String pattern = "";
- private String testText = "";
- private Pattern compiledPattern;
- private String patternError;
- private String name = "";
- private PossiblyMatchedValue<String> transactionDescription =
- PossiblyMatchedValue.withLiteralString("");
- private PossiblyMatchedValue<String> transactionComment =
- PossiblyMatchedValue.withLiteralString("");
- private PossiblyMatchedValue<Integer> dateYear = PossiblyMatchedValue.withLiteralInt(null);
- private PossiblyMatchedValue<Integer> dateMonth = PossiblyMatchedValue.withLiteralInt(null);
- private PossiblyMatchedValue<Integer> dateDay = PossiblyMatchedValue.withLiteralInt(null);
- private Header() {
- super(Type.HEADER);
- }
- public Header(Header origin) {
- this();
- id = origin.id;
- name = origin.name;
- testText = origin.testText;
- setPattern(origin.pattern);
-
- transactionDescription = new PossiblyMatchedValue<>(origin.transactionDescription);
- transactionComment = new PossiblyMatchedValue<>(origin.transactionComment);
-
- dateYear = new PossiblyMatchedValue<>(origin.dateYear);
- dateMonth = new PossiblyMatchedValue<>(origin.dateMonth);
- dateDay = new PossiblyMatchedValue<>(origin.dateDay);
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getPattern() {
- return pattern;
- }
- public void setPattern(String pattern) {
- this.pattern = pattern;
- if (pattern != null) {
- try {
- this.compiledPattern = Pattern.compile(pattern);
- this.patternError = null;
- }
- catch (PatternSyntaxException e) {
- this.compiledPattern = null;
- this.patternError = e.getMessage();
- }
- }
- else {
- patternError = "Missing pattern";
- }
- }
- @NonNull
- @Override
- public String toString() {
- return super.toString() +
- String.format(" name[%s] pat[%s] test[%s] tran[%s] com[%s]", name, pattern,
- testText, transactionDescription, transactionComment);
- }
- public String getTestText() {
- return testText;
- }
- public void setTestText(String testText) {
- this.testText = testText;
- }
- public String getTransactionDescription() {
- return transactionDescription.getValue();
- }
- public void setTransactionDescription(String transactionDescription) {
- this.transactionDescription.setValue(transactionDescription);
- }
- public String getTransactionComment() {
- return transactionComment.getValue();
- }
- public void setTransactionComment(String transactionComment) {
- this.transactionComment.setValue(transactionComment);
- }
- public Integer getDateYear() {
- return dateYear.getValue();
- }
- public void setDateYear(Integer dateYear) {
- this.dateYear.setValue(dateYear);
- }
- public Integer getDateMonth() {
- return dateMonth.getValue();
- }
- public void setDateMonth(Integer dateMonth) {
- this.dateMonth.setValue(dateMonth);
- }
- public Integer getDateDay() {
- return dateDay.getValue();
- }
- public void setDateDay(Integer dateDay) {
- this.dateDay.setValue(dateDay);
- }
- public int getDateYearMatchGroup() {
- return dateYear.getMatchGroup();
- }
- public void setDateYearMatchGroup(int dateYearMatchGroup) {
- this.dateYear.setMatchGroup(dateYearMatchGroup);
- }
- public int getDateMonthMatchGroup() {
- return dateMonth.getMatchGroup();
- }
- public void setDateMonthMatchGroup(int dateMonthMatchGroup) {
- this.dateMonth.setMatchGroup(dateMonthMatchGroup);
- }
- public int getDateDayMatchGroup() {
- return dateDay.getMatchGroup();
- }
- public void setDateDayMatchGroup(int dateDayMatchGroup) {
- this.dateDay.setMatchGroup(dateDayMatchGroup);
- }
- public boolean hasLiteralDateYear() {
- return dateYear.hasLiteralValue();
- }
- public boolean hasLiteralDateMonth() {
- return dateMonth.hasLiteralValue();
- }
- public boolean hasLiteralDateDay() {
- return dateDay.hasLiteralValue();
- }
- public boolean hasLiteralTransactionDescription() { return transactionDescription.hasLiteralValue(); }
- public boolean hasLiteralTransactionComment() { return transactionComment.hasLiteralValue(); }
- public String getProblem(@NonNull Resources r, int patternGroupCount) {
- if (patternError != null)
- return r.getString(R.string.pattern_has_errors) + ": " + patternError;
- if (Misc.emptyIsNull(pattern) == null)
- return r.getString(R.string.pattern_is_empty);
-
- if (!dateYear.hasLiteralValue() && compiledPattern != null &&
- (dateDay.getMatchGroup() < 1 || dateDay.getMatchGroup() > patternGroupCount))
- return r.getString(R.string.invalid_matching_group_number);
-
- if (!dateMonth.hasLiteralValue() && compiledPattern != null &&
- (dateMonth.getMatchGroup() < 1 || dateMonth.getMatchGroup() > patternGroupCount))
- return r.getString(R.string.invalid_matching_group_number);
-
- if (!dateDay.hasLiteralValue() && compiledPattern != null &&
- (dateDay.getMatchGroup() < 1 || dateDay.getMatchGroup() > patternGroupCount))
- return r.getString(R.string.invalid_matching_group_number);
-
- return null;
- }
-
- public boolean equalContents(Header o) {
- if (!dateDay.equals(o.dateDay))
- return false;
- if (!dateMonth.equals(o.dateMonth))
- return false;
- if (!dateYear.equals(o.dateYear))
- return false;
- if (!transactionDescription.equals(o.transactionDescription))
- return false;
- if (!transactionComment.equals(o.transactionComment))
- return true;
-
- return Misc.equalStrings(name, o.name) && Misc.equalStrings(pattern, o.pattern) &&
- Misc.equalStrings(testText, o.testText);
- }
- public String getMatchGroupText(int group) {
- if (compiledPattern != null && testText != null) {
- Matcher m = compiledPattern.matcher(testText);
- if (m.matches())
- return m.group(group);
- }
-
- return "ø";
- }
- public Pattern getCompiledPattern() {
- return compiledPattern;
- }
- public void switchToLiteralTransactionDescription() {
- transactionDescription.switchToLiteral();
- }
- public void switchToLiteralTransactionComment() {
- transactionComment.switchToLiteral();
- }
- public int getTransactionDescriptionMatchGroup() {
- return transactionDescription.getMatchGroup();
- }
- public void setTransactionDescriptionMatchGroup(int group) {
- transactionDescription.setMatchGroup(group);
- }
- public int getTransactionCommentMatchGroup() {
- return transactionComment.getMatchGroup();
- }
- public void setTransactionCommentMatchGroup(int group) {
- transactionComment.setMatchGroup(group);
- }
- public void switchToLiteralDateYear() {
- dateYear.switchToLiteral();
- }
- public void switchToLiteralDateMonth() {
- dateMonth.switchToLiteral();
- }
- public void switchToLiteralDateDay() { dateDay.switchToLiteral(); }
- public PatternHeader toDBO() {
- PatternHeader result = new PatternHeader(id, name, pattern);
-
- if (Misc.emptyIsNull(testText) != null)
- result.setTestText(testText);
-
- if (transactionDescription.hasLiteralValue())
- result.setTransactionDescription(transactionDescription.getValue());
- else
- result.setTransactionDescriptionMatchGroup(transactionDescription.getMatchGroup());
-
- if (transactionComment.hasLiteralValue())
- result.setTransactionComment(transactionComment.getValue());
- else
- result.setTransactionCommentMatchGroup(transactionComment.getMatchGroup());
-
- if (dateYear.hasLiteralValue())
- result.setDateYear(dateYear.getValue());
- else
- result.setDateYearMatchGroup(dateYear.getMatchGroup());
-
- if (dateMonth.hasLiteralValue())
- result.setDateMonth(dateMonth.getValue());
- else
- result.setDateMonthMatchGroup(dateMonth.getMatchGroup());
-
- if (dateDay.hasLiteralValue())
- result.setDateDay(dateDay.getValue());
- else
- result.setDateDayMatchGroup(dateDay.getMatchGroup());
-
- return result;
- }
- }
-}
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.model;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.DiffUtil;
+
+import java.io.Serializable;
+
+public class TemplateDetailSource implements Serializable {
+ public static final DiffUtil.ItemCallback<TemplateDetailSource> DIFF_CALLBACK =
+ new DiffUtil.ItemCallback<TemplateDetailSource>() {
+ @Override
+ public boolean areItemsTheSame(@NonNull TemplateDetailSource oldItem,
+ @NonNull TemplateDetailSource newItem) {
+ return oldItem.groupNumber == newItem.groupNumber;
+ }
+ @Override
+ public boolean areContentsTheSame(@NonNull TemplateDetailSource oldItem,
+ @NonNull TemplateDetailSource newItem) {
+ return oldItem.matchedText.equals(newItem.matchedText);
+ }
+ };
+
+ private short groupNumber;
+ private String matchedText;
+ public TemplateDetailSource() {
+ }
+ public TemplateDetailSource(short groupNumber, String matchedText) {
+ this.groupNumber = groupNumber;
+ this.matchedText = matchedText;
+ }
+ public short getGroupNumber() {
+ return groupNumber;
+ }
+ public void setGroupNumber(short groupNumber) {
+ this.groupNumber = groupNumber;
+ }
+ public String getMatchedText() {
+ return matchedText;
+ }
+ public void setMatchedText(String matchedText) {
+ this.matchedText = matchedText;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.model;
+
+import android.content.res.Resources;
+
+import androidx.annotation.NonNull;
+
+import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.db.TemplateAccount;
+import net.ktnx.mobileledger.db.TemplateBase;
+import net.ktnx.mobileledger.db.TemplateHeader;
+import net.ktnx.mobileledger.utils.Misc;
+
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+abstract public class TemplateDetailsItem {
+ private final Type type;
+ protected Long id;
+ protected Long position;
+
+ protected TemplateDetailsItem(Type type) {
+ this.type = type;
+ }
+ @Contract(" -> new")
+ public static @NotNull TemplateDetailsItem.Header createHeader() {
+ return new Header();
+ }
+ public static @NotNull TemplateDetailsItem.Header createHeader(Header origin) {
+ return new Header(origin);
+ }
+ @Contract("-> new")
+ public static @NotNull TemplateDetailsItem.AccountRow createAccountRow() {
+ return new AccountRow();
+ }
+ public static TemplateDetailsItem fromRoomObject(TemplateBase p) {
+ if (p instanceof TemplateHeader) {
+ TemplateHeader ph = (TemplateHeader) p;
+ Header header = createHeader();
+ header.setId(ph.getId());
+ header.setName(ph.getName());
+ header.setPattern(ph.getRegularExpression());
+ header.setTestText(ph.getTestText());
+
+ if (ph.getTransactionDescriptionMatchGroup() == null)
+ header.setTransactionDescription(ph.getTransactionDescription());
+ else
+ header.setTransactionDescriptionMatchGroup(
+ ph.getTransactionDescriptionMatchGroup());
+
+ if (ph.getTransactionCommentMatchGroup() == null)
+ header.setTransactionComment(ph.getTransactionComment());
+ else
+ header.setTransactionCommentMatchGroup(ph.getTransactionCommentMatchGroup());
+
+ if (ph.getDateDayMatchGroup() == null)
+ header.setDateDay(ph.getDateDay());
+ else
+ header.setDateDayMatchGroup(ph.getDateDayMatchGroup());
+
+ if (ph.getDateMonthMatchGroup() == null)
+ header.setDateMonth(ph.getDateMonth());
+ else
+ header.setDateMonthMatchGroup(ph.getDateMonthMatchGroup());
+
+ if (ph.getDateYearMatchGroup() == null)
+ header.setDateYear(ph.getDateYear());
+ else
+ header.setDateYearMatchGroup(ph.getDateYearMatchGroup());
+
+ return header;
+ }
+ else if (p instanceof TemplateAccount) {
+ TemplateAccount pa = (TemplateAccount) p;
+ AccountRow acc = createAccountRow();
+ acc.setId(pa.getId());
+
+ if (pa.getAccountNameMatchGroup() == null)
+ acc.setAccountName(Misc.nullIsEmpty(pa.getAccountName()));
+ else
+ acc.setAccountNameMatchGroup(pa.getAccountNameMatchGroup());
+
+ if (pa.getAccountCommentMatchGroup() == null)
+ acc.setAccountComment(Misc.nullIsEmpty(pa.getAccountComment()));
+ else
+ acc.setAccountCommentMatchGroup(pa.getAccountCommentMatchGroup());
+
+ if (pa.getCurrencyMatchGroup() == null) {
+ final Integer currencyId = pa.getCurrency();
+ if (currencyId != null && currencyId > 0)
+ acc.setCurrency(Currency.loadById(currencyId));
+ }
+ else
+ acc.setCurrencyMatchGroup(pa.getCurrencyMatchGroup());
+
+ final Integer amountMatchGroup = pa.getAmountMatchGroup();
+ if (amountMatchGroup != null && amountMatchGroup > 0) {
+ acc.setAmountMatchGroup(amountMatchGroup);
+ final Boolean negateAmount = pa.getNegateAmount();
+ acc.setNegateAmount(negateAmount != null && negateAmount);
+ }
+ else
+ acc.setAmount(pa.getAmount());
+
+ return acc;
+ }
+ else {
+ throw new IllegalStateException("Unexpected item class " + p.getClass());
+ }
+ }
+ public Header asHeaderItem() {
+ ensureType(Type.HEADER);
+ return (Header) this;
+ }
+ public AccountRow asAccountRowItem() {
+ ensureType(Type.ACCOUNT_ITEM);
+ return (AccountRow) this;
+ }
+ private void ensureType(Type type) {
+ if (this.type != type)
+ throw new IllegalStateException(
+ String.format("Type is %s, but %s is required", this.type.toString(),
+ type.toString()));
+ }
+ void ensureTrue(boolean flag) {
+ if (!flag)
+ throw new IllegalStateException(
+ "Literal value requested, but it is matched via a pattern group");
+ }
+ void ensureFalse(boolean flag) {
+ if (flag)
+ throw new IllegalStateException("Matching group requested, but the value is a literal");
+ }
+ public long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public void setId(int id) {
+ this.id = (long) id;
+ }
+ public long getPosition() {
+ return position;
+ }
+ public void setPosition(Long position) {
+ this.position = position;
+ }
+ abstract public String getProblem(@NonNull Resources r, int patternGroupCount);
+ public Type getType() {
+ return type;
+ }
+ public enum Type {
+ HEADER(TYPE.header), ACCOUNT_ITEM(TYPE.accountItem);
+ final int index;
+ Type(int i) {
+ index = i;
+ }
+ public int toInt() {
+ return index;
+ }
+ }
+
+ static class PossiblyMatchedValue<T> {
+ private boolean literalValue;
+ private T value;
+ private int matchGroup;
+ public PossiblyMatchedValue() {
+ literalValue = true;
+ value = null;
+ }
+ public PossiblyMatchedValue(@NonNull PossiblyMatchedValue<T> origin) {
+ literalValue = origin.literalValue;
+ value = origin.value;
+ matchGroup = origin.matchGroup;
+ }
+ @NonNull
+ public static PossiblyMatchedValue<Integer> withLiteralInt(Integer initialValue) {
+ PossiblyMatchedValue<Integer> result = new PossiblyMatchedValue<>();
+ result.setValue(initialValue);
+ return result;
+ }
+ @NonNull
+ public static PossiblyMatchedValue<Float> withLiteralFloat(Float initialValue) {
+ PossiblyMatchedValue<Float> result = new PossiblyMatchedValue<>();
+ result.setValue(initialValue);
+ return result;
+ }
+ public static PossiblyMatchedValue<Short> withLiteralShort(Short initialValue) {
+ PossiblyMatchedValue<Short> result = new PossiblyMatchedValue<>();
+ result.setValue(initialValue);
+ return result;
+ }
+ @NonNull
+ public static PossiblyMatchedValue<String> withLiteralString(String initialValue) {
+ PossiblyMatchedValue<String> result = new PossiblyMatchedValue<>();
+ result.setValue(initialValue);
+ return result;
+ }
+ public T getValue() {
+ if (!literalValue)
+ throw new IllegalStateException("Value is not literal");
+ return value;
+ }
+ public void setValue(T newValue) {
+ value = newValue;
+ literalValue = true;
+ }
+ public boolean hasLiteralValue() {
+ return literalValue;
+ }
+ public int getMatchGroup() {
+ if (literalValue)
+ throw new IllegalStateException("Value is literal");
+ return matchGroup;
+ }
+ public void setMatchGroup(int group) {
+ this.matchGroup = group;
+ literalValue = false;
+ }
+ public boolean equals(PossiblyMatchedValue<T> other) {
+ if (!other.literalValue == literalValue)
+ return false;
+ if (literalValue) {
+ if (value == null)
+ return other.value == null;
+ return value.equals(other.value);
+ }
+ else
+ return matchGroup == other.matchGroup;
+ }
+ public void switchToLiteral() {
+ literalValue = true;
+ }
+ public String toString() {
+ if (literalValue)
+ if (value == null)
+ return "<null>";
+ else
+ return value.toString();
+ if (matchGroup > 0)
+ return "grp:" + matchGroup;
+ return "<null>";
+ }
+ }
+
+ public static class TYPE {
+ public static final int header = 0;
+ public static final int accountItem = 1;
+ }
+
+ public static class AccountRow extends TemplateDetailsItem {
+ private final PossiblyMatchedValue<String> accountName =
+ PossiblyMatchedValue.withLiteralString("");
+ private final PossiblyMatchedValue<String> accountComment =
+ PossiblyMatchedValue.withLiteralString("");
+ private final PossiblyMatchedValue<Float> amount =
+ PossiblyMatchedValue.withLiteralFloat(0f);
+ private final PossiblyMatchedValue<Currency> currency = new PossiblyMatchedValue<>();
+ private boolean negateAmount;
+ private AccountRow() {
+ super(Type.ACCOUNT_ITEM);
+ }
+ public boolean isNegateAmount() {
+ return negateAmount;
+ }
+ public void setNegateAmount(boolean negateAmount) {
+ this.negateAmount = negateAmount;
+ }
+ public int getAccountCommentMatchGroup() {
+ return accountComment.getMatchGroup();
+ }
+ public void setAccountCommentMatchGroup(int group) {
+ accountComment.setMatchGroup(group);
+ }
+ public String getAccountComment() {
+ return accountComment.getValue();
+ }
+ public void setAccountComment(String comment) {
+ this.accountComment.setValue(comment);
+ }
+ public int getCurrencyMatchGroup() {
+ return currency.getMatchGroup();
+ }
+ public void setCurrencyMatchGroup(int group) {
+ currency.setMatchGroup(group);
+ }
+ public Currency getCurrency() {
+ return currency.getValue();
+ }
+ public void setCurrency(Currency currency) {
+ this.currency.setValue(currency);
+ }
+ public int getAccountNameMatchGroup() {
+ return accountName.getMatchGroup();
+ }
+ public void setAccountNameMatchGroup(int group) {
+ accountName.setMatchGroup(group);
+ }
+ public String getAccountName() {
+ return accountName.getValue();
+ }
+ public void setAccountName(String accountName) {
+ this.accountName.setValue(accountName);
+ }
+ public boolean hasLiteralAccountName() { return accountName.hasLiteralValue(); }
+ public boolean hasLiteralAmount() {
+ return amount.hasLiteralValue();
+ }
+ public int getAmountMatchGroup() {
+ return amount.getMatchGroup();
+ }
+ public void setAmountMatchGroup(int group) {
+ amount.setMatchGroup(group);
+ }
+ public Float getAmount() {
+ return amount.getValue();
+ }
+ public void setAmount(Float amount) {
+ this.amount.setValue(amount);
+ }
+ public String getProblem(@NonNull Resources r, int patternGroupCount) {
+ if (Misc.emptyIsNull(accountName.getValue()) == null)
+ return r.getString(R.string.account_name_is_empty);
+ if (!amount.hasLiteralValue() &&
+ (amount.getMatchGroup() < 1 || amount.getMatchGroup() > patternGroupCount))
+ return r.getString(R.string.invalid_matching_group_number);
+
+ return null;
+ }
+ public boolean hasLiteralAccountComment() {
+ return accountComment.hasLiteralValue();
+ }
+ public boolean equalContents(AccountRow o) {
+ return amount.equals(o.amount) && accountName.equals(o.accountName) &&
+ accountComment.equals(o.accountComment) && negateAmount == o.negateAmount;
+ }
+ public void switchToLiteralAmount() {
+ amount.switchToLiteral();
+ }
+ public void switchToLiteralAccountName() {
+ accountName.switchToLiteral();
+ }
+ public void switchToLiteralAccountComment() {
+ accountComment.switchToLiteral();
+ }
+ public TemplateAccount toDBO(@NonNull Long patternId) {
+ TemplateAccount result = new TemplateAccount(id, patternId, position);
+
+ if (accountName.hasLiteralValue())
+ result.setAccountName(accountName.getValue());
+ else
+ result.setAccountNameMatchGroup(accountName.getMatchGroup());
+
+ if (accountComment.hasLiteralValue())
+ result.setAccountComment(accountComment.getValue());
+ else
+ result.setAccountCommentMatchGroup(accountComment.getMatchGroup());
+
+ if (amount.hasLiteralValue()) {
+ result.setAmount(amount.getValue());
+ result.setNegateAmount(null);
+ }
+ else {
+ result.setAmountMatchGroup(amount.getMatchGroup());
+ result.setNegateAmount(negateAmount ? true : null);
+ }
+
+ return result;
+ }
+ }
+
+ public static class Header extends TemplateDetailsItem {
+ private String pattern = "";
+ private String testText = "";
+ private Pattern compiledPattern;
+ private String patternError;
+ private String name = "";
+ private PossiblyMatchedValue<String> transactionDescription =
+ PossiblyMatchedValue.withLiteralString("");
+ private PossiblyMatchedValue<String> transactionComment =
+ PossiblyMatchedValue.withLiteralString("");
+ private PossiblyMatchedValue<Integer> dateYear = PossiblyMatchedValue.withLiteralInt(null);
+ private PossiblyMatchedValue<Integer> dateMonth = PossiblyMatchedValue.withLiteralInt(null);
+ private PossiblyMatchedValue<Integer> dateDay = PossiblyMatchedValue.withLiteralInt(null);
+ private Header() {
+ super(Type.HEADER);
+ }
+ public Header(Header origin) {
+ this();
+ id = origin.id;
+ name = origin.name;
+ testText = origin.testText;
+ setPattern(origin.pattern);
+
+ transactionDescription = new PossiblyMatchedValue<>(origin.transactionDescription);
+ transactionComment = new PossiblyMatchedValue<>(origin.transactionComment);
+
+ dateYear = new PossiblyMatchedValue<>(origin.dateYear);
+ dateMonth = new PossiblyMatchedValue<>(origin.dateMonth);
+ dateDay = new PossiblyMatchedValue<>(origin.dateDay);
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getPattern() {
+ return pattern;
+ }
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ if (pattern != null) {
+ try {
+ this.compiledPattern = Pattern.compile(pattern);
+ this.patternError = null;
+ }
+ catch (PatternSyntaxException e) {
+ this.compiledPattern = null;
+ this.patternError = e.getMessage();
+ }
+ }
+ else {
+ patternError = "Missing pattern";
+ }
+ }
+ @NonNull
+ @Override
+ public String toString() {
+ return super.toString() +
+ String.format(" name[%s] pat[%s] test[%s] tran[%s] com[%s]", name, pattern,
+ testText, transactionDescription, transactionComment);
+ }
+ public String getTestText() {
+ return testText;
+ }
+ public void setTestText(String testText) {
+ this.testText = testText;
+ }
+ public String getTransactionDescription() {
+ return transactionDescription.getValue();
+ }
+ public void setTransactionDescription(String transactionDescription) {
+ this.transactionDescription.setValue(transactionDescription);
+ }
+ public String getTransactionComment() {
+ return transactionComment.getValue();
+ }
+ public void setTransactionComment(String transactionComment) {
+ this.transactionComment.setValue(transactionComment);
+ }
+ public Integer getDateYear() {
+ return dateYear.getValue();
+ }
+ public void setDateYear(Integer dateYear) {
+ this.dateYear.setValue(dateYear);
+ }
+ public Integer getDateMonth() {
+ return dateMonth.getValue();
+ }
+ public void setDateMonth(Integer dateMonth) {
+ this.dateMonth.setValue(dateMonth);
+ }
+ public Integer getDateDay() {
+ return dateDay.getValue();
+ }
+ public void setDateDay(Integer dateDay) {
+ this.dateDay.setValue(dateDay);
+ }
+ public int getDateYearMatchGroup() {
+ return dateYear.getMatchGroup();
+ }
+ public void setDateYearMatchGroup(int dateYearMatchGroup) {
+ this.dateYear.setMatchGroup(dateYearMatchGroup);
+ }
+ public int getDateMonthMatchGroup() {
+ return dateMonth.getMatchGroup();
+ }
+ public void setDateMonthMatchGroup(int dateMonthMatchGroup) {
+ this.dateMonth.setMatchGroup(dateMonthMatchGroup);
+ }
+ public int getDateDayMatchGroup() {
+ return dateDay.getMatchGroup();
+ }
+ public void setDateDayMatchGroup(int dateDayMatchGroup) {
+ this.dateDay.setMatchGroup(dateDayMatchGroup);
+ }
+ public boolean hasLiteralDateYear() {
+ return dateYear.hasLiteralValue();
+ }
+ public boolean hasLiteralDateMonth() {
+ return dateMonth.hasLiteralValue();
+ }
+ public boolean hasLiteralDateDay() {
+ return dateDay.hasLiteralValue();
+ }
+ public boolean hasLiteralTransactionDescription() { return transactionDescription.hasLiteralValue(); }
+ public boolean hasLiteralTransactionComment() { return transactionComment.hasLiteralValue(); }
+ public String getProblem(@NonNull Resources r, int patternGroupCount) {
+ if (patternError != null)
+ return r.getString(R.string.pattern_has_errors) + ": " + patternError;
+ if (Misc.emptyIsNull(pattern) == null)
+ return r.getString(R.string.pattern_is_empty);
+
+ if (!dateYear.hasLiteralValue() && compiledPattern != null &&
+ (dateDay.getMatchGroup() < 1 || dateDay.getMatchGroup() > patternGroupCount))
+ return r.getString(R.string.invalid_matching_group_number);
+
+ if (!dateMonth.hasLiteralValue() && compiledPattern != null &&
+ (dateMonth.getMatchGroup() < 1 || dateMonth.getMatchGroup() > patternGroupCount))
+ return r.getString(R.string.invalid_matching_group_number);
+
+ if (!dateDay.hasLiteralValue() && compiledPattern != null &&
+ (dateDay.getMatchGroup() < 1 || dateDay.getMatchGroup() > patternGroupCount))
+ return r.getString(R.string.invalid_matching_group_number);
+
+ return null;
+ }
+
+ public boolean equalContents(Header o) {
+ if (!dateDay.equals(o.dateDay))
+ return false;
+ if (!dateMonth.equals(o.dateMonth))
+ return false;
+ if (!dateYear.equals(o.dateYear))
+ return false;
+ if (!transactionDescription.equals(o.transactionDescription))
+ return false;
+ if (!transactionComment.equals(o.transactionComment))
+ return true;
+
+ return Misc.equalStrings(name, o.name) && Misc.equalStrings(pattern, o.pattern) &&
+ Misc.equalStrings(testText, o.testText);
+ }
+ public String getMatchGroupText(int group) {
+ if (compiledPattern != null && testText != null) {
+ Matcher m = compiledPattern.matcher(testText);
+ if (m.matches())
+ return m.group(group);
+ }
+
+ return "ø";
+ }
+ public Pattern getCompiledPattern() {
+ return compiledPattern;
+ }
+ public void switchToLiteralTransactionDescription() {
+ transactionDescription.switchToLiteral();
+ }
+ public void switchToLiteralTransactionComment() {
+ transactionComment.switchToLiteral();
+ }
+ public int getTransactionDescriptionMatchGroup() {
+ return transactionDescription.getMatchGroup();
+ }
+ public void setTransactionDescriptionMatchGroup(int group) {
+ transactionDescription.setMatchGroup(group);
+ }
+ public int getTransactionCommentMatchGroup() {
+ return transactionComment.getMatchGroup();
+ }
+ public void setTransactionCommentMatchGroup(int group) {
+ transactionComment.setMatchGroup(group);
+ }
+ public void switchToLiteralDateYear() {
+ dateYear.switchToLiteral();
+ }
+ public void switchToLiteralDateMonth() {
+ dateMonth.switchToLiteral();
+ }
+ public void switchToLiteralDateDay() { dateDay.switchToLiteral(); }
+ public TemplateHeader toDBO() {
+ TemplateHeader result = new TemplateHeader(id, name, pattern);
+
+ if (Misc.emptyIsNull(testText) != null)
+ result.setTestText(testText);
+
+ if (transactionDescription.hasLiteralValue())
+ result.setTransactionDescription(transactionDescription.getValue());
+ else
+ result.setTransactionDescriptionMatchGroup(transactionDescription.getMatchGroup());
+
+ if (transactionComment.hasLiteralValue())
+ result.setTransactionComment(transactionComment.getValue());
+ else
+ result.setTransactionCommentMatchGroup(transactionComment.getMatchGroup());
+
+ if (dateYear.hasLiteralValue())
+ result.setDateYear(dateYear.getValue());
+ else
+ result.setDateYearMatchGroup(dateYear.getMatchGroup());
+
+ if (dateMonth.hasLiteralValue())
+ result.setDateMonth(dateMonth.getValue());
+ else
+ result.setDateMonthMatchGroup(dateMonth.getMatchGroup());
+
+ if (dateDay.hasLiteralValue())
+ result.setDateDay(dateDay.getValue());
+ else
+ result.setDateDayMatchGroup(dateDay.getMatchGroup());
+
+ return result;
+ }
+ }
+}
+++ /dev/null
-/*
- * Copyright © 2019 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.appcompat.app.AppCompatDialogFragment;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import net.ktnx.mobileledger.R;
-import net.ktnx.mobileledger.databinding.FragmentPatternDetailSourceSelectorListBinding;
-import net.ktnx.mobileledger.model.PatternDetailSource;
-import net.ktnx.mobileledger.utils.Logger;
-import net.ktnx.mobileledger.utils.Misc;
-
-import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * A fragment representing a list of Items.
- * <p/>
- * Activities containing this fragment MUST implement the {@link OnSourceSelectedListener}
- * interface.
- */
-public class PatternDetailSourceSelectorFragment extends AppCompatDialogFragment
- implements OnSourceSelectedListener {
-
- public static final int DEFAULT_COLUMN_COUNT = 1;
- public static final String ARG_COLUMN_COUNT = "column-count";
- public static final String ARG_PATTERN = "pattern";
- public static final String ARG_TEST_TEXT = "test-text";
- private int mColumnCount = DEFAULT_COLUMN_COUNT;
- private ArrayList<PatternDetailSource> mSources;
- private PatternDetailSourceSelectorModel model;
- private OnSourceSelectedListener onSourceSelectedListener;
- private @StringRes
- int mPatternProblem;
-
- /**
- * Mandatory empty constructor for the fragment manager to instantiate the
- * fragment (e.g. upon screen orientation changes).
- */
- public PatternDetailSourceSelectorFragment() {
- }
- @SuppressWarnings("unused")
- public static PatternDetailSourceSelectorFragment newInstance() {
- return newInstance(DEFAULT_COLUMN_COUNT, null, null);
- }
- public static PatternDetailSourceSelectorFragment newInstance(int columnCount,
- @Nullable String pattern,
- @Nullable String testText) {
- PatternDetailSourceSelectorFragment fragment = new PatternDetailSourceSelectorFragment();
- Bundle args = new Bundle();
- args.putInt(ARG_COLUMN_COUNT, columnCount);
- if (pattern != null)
- args.putString(ARG_PATTERN, pattern);
- if (testText != null)
- args.putString(ARG_TEST_TEXT, testText);
- fragment.setArguments(args);
- return fragment;
- }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (getArguments() != null) {
- mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT, DEFAULT_COLUMN_COUNT);
- final String patternText = getArguments().getString(ARG_PATTERN);
- final String testText = getArguments().getString(ARG_TEST_TEXT);
- if (Misc.emptyIsNull(patternText) == null) {
- mPatternProblem = R.string.missing_pattern_error;
- }
- else {
- if (Misc.emptyIsNull(testText) == null) {
- mPatternProblem = R.string.missing_test_text;
- }
- else {
- Pattern pattern = Pattern.compile(patternText);
- Matcher matcher = pattern.matcher(testText);
- Logger.debug("patterns",
- String.format("Trying to match pattern '%s' against text '%s'",
- patternText, testText));
- if (matcher.matches()) {
- if (matcher.groupCount() >= 0) {
- ArrayList<PatternDetailSource> list = new ArrayList<>();
- for (short g = 1; g <= matcher.groupCount(); g++) {
- list.add(new PatternDetailSource(g, matcher.group(g)));
- }
- mSources = list;
- }
- else {
- mPatternProblem = R.string.pattern_without_groups;
- }
- }
- else {
- mPatternProblem = R.string.pattern_does_not_match;
- }
- }
- }
- }
- }
- @NonNull
- @Override
- public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
- Context context = requireContext();
- Dialog csd = new Dialog(context);
- FragmentPatternDetailSourceSelectorListBinding b =
- FragmentPatternDetailSourceSelectorListBinding.inflate(
- LayoutInflater.from(context));
- csd.setContentView(b.getRoot());
- csd.setTitle(R.string.choose_pattern_detail_source_label);
-
- if (mSources != null && !mSources.isEmpty()) {
- RecyclerView recyclerView = b.list;
-
- if (mColumnCount <= 1) {
- recyclerView.setLayoutManager(new LinearLayoutManager(context));
- }
- else {
- recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
- }
- model = new ViewModelProvider(this).get(PatternDetailSourceSelectorModel.class);
- if (onSourceSelectedListener != null)
- model.setOnSourceSelectedListener(onSourceSelectedListener);
- model.setSourcesList(mSources);
-
- PatternDetailSourceSelectorRecyclerViewAdapter adapter =
- new PatternDetailSourceSelectorRecyclerViewAdapter();
- model.groups.observe(this, adapter::submitList);
-
- recyclerView.setAdapter(adapter);
- adapter.setSourceSelectedListener(this);
- }
- else {
- b.list.setVisibility(View.GONE);
- b.patternError.setText(mPatternProblem);
- b.patternError.setVisibility(View.VISIBLE);
- }
-
- b.literalButton.setOnClickListener(v -> onSourceSelected(true, (short) -1));
-
- return csd;
- }
- public void setOnSourceSelectedListener(OnSourceSelectedListener listener) {
- onSourceSelectedListener = listener;
-
- if (model != null)
- model.setOnSourceSelectedListener(listener);
- }
- public void resetOnSourceSelectedListener() {
- model.resetOnSourceSelectedListener();
- }
- @Override
- public void onSourceSelected(boolean literal, short group) {
- if (model != null)
- model.triggerOnSourceSelectedListener(literal, group);
- if (onSourceSelectedListener != null)
- onSourceSelectedListener.onSourceSelected(literal, group);
-
- dismiss();
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright © 2020 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui;
-
-import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.ViewModel;
-
-import net.ktnx.mobileledger.model.PatternDetailSource;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PatternDetailSourceSelectorModel extends ViewModel {
- public final MutableLiveData<List<PatternDetailSource>> groups = new MutableLiveData<>();
- private OnSourceSelectedListener selectionListener;
- public PatternDetailSourceSelectorModel() {
- }
- void setOnSourceSelectedListener(OnSourceSelectedListener listener) {
- selectionListener = listener;
- }
- void resetOnSourceSelectedListener() {
- selectionListener = null;
- }
- void triggerOnSourceSelectedListener(boolean literal, short group) {
- if (selectionListener != null)
- selectionListener.onSourceSelected(literal, group);
- }
- public void setSourcesList(ArrayList<PatternDetailSource> mSources) {
- groups.setValue(mSources);
- }
-}
+++ /dev/null
-/*
- * Copyright © 2019 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui;
-
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.ListAdapter;
-import androidx.recyclerview.widget.RecyclerView;
-
-import net.ktnx.mobileledger.databinding.FragmentPatternDetailSourceSelectorBinding;
-import net.ktnx.mobileledger.model.PatternDetailSource;
-
-import org.jetbrains.annotations.NotNull;
-
-/**
- * {@link RecyclerView.Adapter} that can display a {@link PatternDetailSource} and makes a call
- * to the
- * specified {@link OnSourceSelectedListener}.
- */
-public class PatternDetailSourceSelectorRecyclerViewAdapter extends
- ListAdapter<PatternDetailSource,
- PatternDetailSourceSelectorRecyclerViewAdapter.ViewHolder> {
-
- private OnSourceSelectedListener sourceSelectedListener;
- public PatternDetailSourceSelectorRecyclerViewAdapter() {
- super(PatternDetailSource.DIFF_CALLBACK);
- }
- @NotNull
- @Override
- public ViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) {
- FragmentPatternDetailSourceSelectorBinding b =
- FragmentPatternDetailSourceSelectorBinding.inflate(
- LayoutInflater.from(parent.getContext()), parent, false);
- return new ViewHolder(b);
- }
-
- @Override
- public void onBindViewHolder(final ViewHolder holder, int position) {
- holder.bindTo(getItem(position));
- }
- public void setSourceSelectedListener(OnSourceSelectedListener listener) {
- this.sourceSelectedListener = listener;
- }
- public void resetSourceSelectedListener() {
- sourceSelectedListener = null;
- }
- public void notifySourceSelected(PatternDetailSource item) {
- if (null != sourceSelectedListener)
- sourceSelectedListener.onSourceSelected(false, item.getGroupNumber());
- }
- public void notifyLiteralSelected() {
- if (null != sourceSelectedListener)
- sourceSelectedListener.onSourceSelected(true, (short) -1);
- }
- public class ViewHolder extends RecyclerView.ViewHolder {
- private final FragmentPatternDetailSourceSelectorBinding b;
- private PatternDetailSource mItem;
-
- ViewHolder(FragmentPatternDetailSourceSelectorBinding binding) {
- super(binding.getRoot());
- b = binding;
-
- b.getRoot()
- .setOnClickListener(v -> notifySourceSelected(mItem));
- }
-
- @NotNull
- @Override
- public String toString() {
- return super.toString() + " " + b.groupNumber.getText() + ": '" +
- b.matchedText.getText() + "'";
- }
- void bindTo(PatternDetailSource item) {
- mItem = item;
- b.groupNumber.setText(String.valueOf(item.getGroupNumber()));
- b.matchedText.setText(item.getMatchedText());
- }
- }
-}
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.AppCompatDialogFragment;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.databinding.FragmentTemplateDetailSourceSelectorListBinding;
+import net.ktnx.mobileledger.model.TemplateDetailSource;
+import net.ktnx.mobileledger.utils.Logger;
+import net.ktnx.mobileledger.utils.Misc;
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A fragment representing a list of Items.
+ * <p/>
+ * Activities containing this fragment MUST implement the {@link OnSourceSelectedListener}
+ * interface.
+ */
+public class TemplateDetailSourceSelectorFragment extends AppCompatDialogFragment
+ implements OnSourceSelectedListener {
+
+ public static final int DEFAULT_COLUMN_COUNT = 1;
+ public static final String ARG_COLUMN_COUNT = "column-count";
+ public static final String ARG_PATTERN = "pattern";
+ public static final String ARG_TEST_TEXT = "test-text";
+ private int mColumnCount = DEFAULT_COLUMN_COUNT;
+ private ArrayList<TemplateDetailSource> mSources;
+ private TemplateDetailSourceSelectorModel model;
+ private OnSourceSelectedListener onSourceSelectedListener;
+ private @StringRes
+ int mPatternProblem;
+
+ /**
+ * Mandatory empty constructor for the fragment manager to instantiate the
+ * fragment (e.g. upon screen orientation changes).
+ */
+ public TemplateDetailSourceSelectorFragment() {
+ }
+ @SuppressWarnings("unused")
+ public static TemplateDetailSourceSelectorFragment newInstance() {
+ return newInstance(DEFAULT_COLUMN_COUNT, null, null);
+ }
+ public static TemplateDetailSourceSelectorFragment newInstance(int columnCount,
+ @Nullable String pattern,
+ @Nullable String testText) {
+ TemplateDetailSourceSelectorFragment fragment = new TemplateDetailSourceSelectorFragment();
+ Bundle args = new Bundle();
+ args.putInt(ARG_COLUMN_COUNT, columnCount);
+ if (pattern != null)
+ args.putString(ARG_PATTERN, pattern);
+ if (testText != null)
+ args.putString(ARG_TEST_TEXT, testText);
+ fragment.setArguments(args);
+ return fragment;
+ }
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getArguments() != null) {
+ mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT, DEFAULT_COLUMN_COUNT);
+ final String patternText = getArguments().getString(ARG_PATTERN);
+ final String testText = getArguments().getString(ARG_TEST_TEXT);
+ if (Misc.emptyIsNull(patternText) == null) {
+ mPatternProblem = R.string.missing_pattern_error;
+ }
+ else {
+ if (Misc.emptyIsNull(testText) == null) {
+ mPatternProblem = R.string.missing_test_text;
+ }
+ else {
+ Pattern pattern = Pattern.compile(patternText);
+ Matcher matcher = pattern.matcher(testText);
+ Logger.debug("templates",
+ String.format("Trying to match pattern '%s' against text '%s'",
+ patternText, testText));
+ if (matcher.matches()) {
+ if (matcher.groupCount() >= 0) {
+ ArrayList<TemplateDetailSource> list = new ArrayList<>();
+ for (short g = 1; g <= matcher.groupCount(); g++) {
+ list.add(new TemplateDetailSource(g, matcher.group(g)));
+ }
+ mSources = list;
+ }
+ else {
+ mPatternProblem = R.string.pattern_without_groups;
+ }
+ }
+ else {
+ mPatternProblem = R.string.pattern_does_not_match;
+ }
+ }
+ }
+ }
+ }
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Context context = requireContext();
+ Dialog csd = new Dialog(context);
+ FragmentTemplateDetailSourceSelectorListBinding b =
+ FragmentTemplateDetailSourceSelectorListBinding.inflate(
+ LayoutInflater.from(context));
+ csd.setContentView(b.getRoot());
+ csd.setTitle(R.string.choose_template_detail_source_label);
+
+ if (mSources != null && !mSources.isEmpty()) {
+ RecyclerView recyclerView = b.list;
+
+ if (mColumnCount <= 1) {
+ recyclerView.setLayoutManager(new LinearLayoutManager(context));
+ }
+ else {
+ recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
+ }
+ model = new ViewModelProvider(this).get(TemplateDetailSourceSelectorModel.class);
+ if (onSourceSelectedListener != null)
+ model.setOnSourceSelectedListener(onSourceSelectedListener);
+ model.setSourcesList(mSources);
+
+ TemplateDetailSourceSelectorRecyclerViewAdapter adapter =
+ new TemplateDetailSourceSelectorRecyclerViewAdapter();
+ model.groups.observe(this, adapter::submitList);
+
+ recyclerView.setAdapter(adapter);
+ adapter.setSourceSelectedListener(this);
+ }
+ else {
+ b.list.setVisibility(View.GONE);
+ b.templateError.setText(mPatternProblem);
+ b.templateError.setVisibility(View.VISIBLE);
+ }
+
+ b.literalButton.setOnClickListener(v -> onSourceSelected(true, (short) -1));
+
+ return csd;
+ }
+ public void setOnSourceSelectedListener(OnSourceSelectedListener listener) {
+ onSourceSelectedListener = listener;
+
+ if (model != null)
+ model.setOnSourceSelectedListener(listener);
+ }
+ public void resetOnSourceSelectedListener() {
+ model.resetOnSourceSelectedListener();
+ }
+ @Override
+ public void onSourceSelected(boolean literal, short group) {
+ if (model != null)
+ model.triggerOnSourceSelectedListener(literal, group);
+ if (onSourceSelectedListener != null)
+ onSourceSelectedListener.onSourceSelected(literal, group);
+
+ dismiss();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui;
+
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+import net.ktnx.mobileledger.model.TemplateDetailSource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TemplateDetailSourceSelectorModel extends ViewModel {
+ public final MutableLiveData<List<TemplateDetailSource>> groups = new MutableLiveData<>();
+ private OnSourceSelectedListener selectionListener;
+ public TemplateDetailSourceSelectorModel() {
+ }
+ void setOnSourceSelectedListener(OnSourceSelectedListener listener) {
+ selectionListener = listener;
+ }
+ void resetOnSourceSelectedListener() {
+ selectionListener = null;
+ }
+ void triggerOnSourceSelectedListener(boolean literal, short group) {
+ if (selectionListener != null)
+ selectionListener.onSourceSelected(literal, group);
+ }
+ public void setSourcesList(ArrayList<TemplateDetailSource> mSources) {
+ groups.setValue(mSources);
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.ListAdapter;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.ktnx.mobileledger.databinding.FragmentTemplateDetailSourceSelectorBinding;
+import net.ktnx.mobileledger.model.TemplateDetailSource;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * {@link RecyclerView.Adapter} that can display a {@link TemplateDetailSource} and makes a call
+ * to the
+ * specified {@link OnSourceSelectedListener}.
+ */
+public class TemplateDetailSourceSelectorRecyclerViewAdapter extends
+ ListAdapter<TemplateDetailSource,
+ TemplateDetailSourceSelectorRecyclerViewAdapter.ViewHolder> {
+
+ private OnSourceSelectedListener sourceSelectedListener;
+ public TemplateDetailSourceSelectorRecyclerViewAdapter() {
+ super(TemplateDetailSource.DIFF_CALLBACK);
+ }
+ @NotNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) {
+ FragmentTemplateDetailSourceSelectorBinding b =
+ FragmentTemplateDetailSourceSelectorBinding.inflate(
+ LayoutInflater.from(parent.getContext()), parent, false);
+ return new ViewHolder(b);
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, int position) {
+ holder.bindTo(getItem(position));
+ }
+ public void setSourceSelectedListener(OnSourceSelectedListener listener) {
+ this.sourceSelectedListener = listener;
+ }
+ public void resetSourceSelectedListener() {
+ sourceSelectedListener = null;
+ }
+ public void notifySourceSelected(TemplateDetailSource item) {
+ if (null != sourceSelectedListener)
+ sourceSelectedListener.onSourceSelected(false, item.getGroupNumber());
+ }
+ public void notifyLiteralSelected() {
+ if (null != sourceSelectedListener)
+ sourceSelectedListener.onSourceSelected(true, (short) -1);
+ }
+ public class ViewHolder extends RecyclerView.ViewHolder {
+ private final FragmentTemplateDetailSourceSelectorBinding b;
+ private TemplateDetailSource mItem;
+
+ ViewHolder(FragmentTemplateDetailSourceSelectorBinding binding) {
+ super(binding.getRoot());
+ b = binding;
+
+ b.getRoot()
+ .setOnClickListener(v -> notifySourceSelected(mItem));
+ }
+
+ @NotNull
+ @Override
+ public String toString() {
+ return super.toString() + " " + b.groupNumber.getText() + ": '" +
+ b.matchedText.getText() + "'";
+ }
+ void bindTo(TemplateDetailSource item) {
+ mItem = item;
+ b.groupNumber.setText(String.valueOf(item.getGroupNumber()));
+ b.matchedText.setText(item.getMatchedText());
+ }
+ }
+}
import net.ktnx.mobileledger.ui.MainModel;
import net.ktnx.mobileledger.ui.account_summary.AccountSummaryFragment;
import net.ktnx.mobileledger.ui.new_transaction.NewTransactionActivity;
-import net.ktnx.mobileledger.ui.patterns.PatternsActivity;
import net.ktnx.mobileledger.ui.profiles.ProfilesRecyclerViewAdapter;
+import net.ktnx.mobileledger.ui.templates.TemplatesActivity;
import net.ktnx.mobileledger.ui.transaction_list.TransactionListFragment;
import net.ktnx.mobileledger.utils.Colors;
import net.ktnx.mobileledger.utils.Logger;
b.navPatterns.setOnClickListener(this::onPatternsClick);
}
private void onPatternsClick(View view) {
- Intent intent = new Intent(this, PatternsActivity.class);
+ Intent intent = new Intent(this, TemplatesActivity.class);
startActivity(intent);
}
private void scheduleDataRetrievalIfStale(long lastUpdate) {
import net.ktnx.mobileledger.R;
import net.ktnx.mobileledger.db.DB;
-import net.ktnx.mobileledger.db.PatternAccount;
-import net.ktnx.mobileledger.db.PatternHeader;
+import net.ktnx.mobileledger.db.TemplateAccount;
+import net.ktnx.mobileledger.db.TemplateHeader;
import net.ktnx.mobileledger.json.API;
import net.ktnx.mobileledger.model.Data;
import net.ktnx.mobileledger.model.LedgerTransaction;
import net.ktnx.mobileledger.model.LedgerTransactionAccount;
import net.ktnx.mobileledger.model.MobileLedgerProfile;
import net.ktnx.mobileledger.ui.QRScanCapableFragment;
-import net.ktnx.mobileledger.ui.patterns.PatternsActivity;
+import net.ktnx.mobileledger.ui.templates.TemplatesActivity;
import net.ktnx.mobileledger.utils.Logger;
import net.ktnx.mobileledger.utils.Misc;
import net.ktnx.mobileledger.utils.SimpleDate;
setHasOptionsMenu(true);
}
private void startNewPatternActivity(String scanned) {
- Intent intent = new Intent(requireContext(), PatternsActivity.class);
+ Intent intent = new Intent(requireContext(), TemplatesActivity.class);
Bundle args = new Bundle();
- args.putString(PatternsActivity.ARG_ADD_PATTERN, scanned);
+ args.putString(TemplatesActivity.ARG_ADD_TEMPLATE, scanned);
requireContext().startActivity(intent, args);
}
private void alertNoPatternMatch(String scanned) {
if (Misc.emptyIsNull(text) == null)
return;
- LiveData<List<PatternHeader>> allPatterns = DB.get()
- .getPatternDAO()
- .getPatterns();
+ LiveData<List<TemplateHeader>> allPatterns = DB.get()
+ .getPatternDAO()
+ .getPatterns();
allPatterns.observe(getViewLifecycleOwner(), patternHeaders -> {
- ArrayList<PatternHeader> matchingPatterns = new ArrayList<>();
+ ArrayList<TemplateHeader> matchingPatterns = new ArrayList<>();
- for (PatternHeader ph : patternHeaders) {
+ for (TemplateHeader ph : patternHeaders) {
String patternSource = ph.getRegularExpression();
if (Misc.emptyIsNull(patternSource) == null)
continue;
choosePattern(matchingPatterns, text);
});
}
- private void choosePattern(ArrayList<PatternHeader> matchingPatterns, String matchedText) {
+ private void choosePattern(ArrayList<TemplateHeader> matchingPatterns, String matchedText) {
final String patternNameColumn = "name";
AbstractCursor cursor = new AbstractCursor() {
@Override
.create()
.show();
}
- private void applyPattern(PatternHeader patternHeader, String text) {
+ private void applyPattern(TemplateHeader patternHeader, String text) {
Pattern pattern = Pattern.compile(patternHeader.getRegularExpression());
Matcher m = pattern.matcher(text);
.observe(getViewLifecycleOwner(), entry -> {
int rowIndex = 0;
final boolean accountsInInitialState = viewModel.accountsInInitialState();
- for (PatternAccount acc : entry.accounts) {
+ for (TemplateAccount acc : entry.accounts) {
rowIndex++;
String accountName = extractStringFromMatches(m, acc.getAccountNameMatchGroup(),
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui.patterns;
-
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.recyclerview.widget.AsyncListDiffer;
-import androidx.recyclerview.widget.DiffUtil;
-import androidx.recyclerview.widget.RecyclerView;
-
-import net.ktnx.mobileledger.R;
-import net.ktnx.mobileledger.databinding.PatternDetailsAccountBinding;
-import net.ktnx.mobileledger.databinding.PatternDetailsHeaderBinding;
-import net.ktnx.mobileledger.db.PatternBase;
-import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.PatternDetailsItem;
-import net.ktnx.mobileledger.ui.PatternDetailSourceSelectorFragment;
-import net.ktnx.mobileledger.ui.QRScanCapableFragment;
-import net.ktnx.mobileledger.utils.Logger;
-import net.ktnx.mobileledger.utils.Misc;
-
-import org.jetbrains.annotations.NotNull;
-
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-class PatternDetailsAdapter extends RecyclerView.Adapter<PatternDetailsAdapter.ViewHolder> {
- private static final String D_PATTERN_UI = "pattern-ui";
- private final AsyncListDiffer<PatternDetailsItem> differ;
- public PatternDetailsAdapter() {
- super();
- setHasStableIds(true);
- differ = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<PatternDetailsItem>() {
- @Override
- public boolean areItemsTheSame(@NonNull PatternDetailsItem oldItem,
- @NonNull PatternDetailsItem newItem) {
- if (oldItem.getType() != newItem.getType())
- return false;
- if (oldItem.getType()
- .equals(PatternDetailsItem.Type.HEADER))
- return true; // only one header item, ever
- // the rest is comparing two account row items
- return oldItem.asAccountRowItem()
- .getId() == newItem.asAccountRowItem()
- .getId();
- }
- @Override
- public boolean areContentsTheSame(@NonNull PatternDetailsItem oldItem,
- @NonNull PatternDetailsItem newItem) {
- if (oldItem.getType()
- .equals(PatternDetailsItem.Type.HEADER))
- {
- PatternDetailsItem.Header oldHeader = oldItem.asHeaderItem();
- PatternDetailsItem.Header newHeader = newItem.asHeaderItem();
-
- return oldHeader.equalContents(newHeader);
- }
- else {
- PatternDetailsItem.AccountRow oldAcc = oldItem.asAccountRowItem();
- PatternDetailsItem.AccountRow newAcc = newItem.asAccountRowItem();
-
- return oldAcc.equalContents(newAcc);
- }
- }
- });
- }
- @Override
- public long getItemId(int position) {
- // header item is always first and IDs id may duplicate some of the account IDs
- if (position == 0)
- return -1;
- PatternDetailsItem.AccountRow accRow = differ.getCurrentList()
- .get(position)
- .asAccountRowItem();
- return accRow.getId();
- }
- @Override
- public int getItemViewType(int position) {
-
- return differ.getCurrentList()
- .get(position)
- .getType()
- .toInt();
- }
- @NonNull
- @Override
- public PatternDetailsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
- int viewType) {
- final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
- switch (viewType) {
- case PatternDetailsItem.TYPE.header:
- return new Header(PatternDetailsHeaderBinding.inflate(inflater, parent, false));
- case PatternDetailsItem.TYPE.accountItem:
- return new AccountRow(
- PatternDetailsAccountBinding.inflate(inflater, parent, false));
- default:
- throw new IllegalStateException("Unsupported view type " + viewType);
- }
- }
- @Override
- public void onBindViewHolder(@NonNull PatternDetailsAdapter.ViewHolder holder, int position) {
- PatternDetailsItem item = differ.getCurrentList()
- .get(position);
- holder.bind(item);
- }
- @Override
- public int getItemCount() {
- return differ.getCurrentList()
- .size();
- }
- public void setPatternItems(List<PatternBase> items) {
- ArrayList<PatternDetailsItem> list = new ArrayList<>();
- for (PatternBase p : items) {
- PatternDetailsItem item = PatternDetailsItem.fromRoomObject(p);
- list.add(item);
- }
- setItems(list);
- }
- public void setItems(List<PatternDetailsItem> items) {
- differ.submitList(items);
- }
- public String getMatchGroupText(int groupNumber) {
- PatternDetailsItem.Header header = getHeader();
- Pattern p = header.getCompiledPattern();
- if (p == null)
- return null;
-
- final String testText = Misc.nullIsEmpty(header.getTestText());
- Matcher m = p.matcher(testText);
- if (m.matches() && m.groupCount() >= groupNumber)
- return m.group(groupNumber);
- else
- return null;
- }
- protected PatternDetailsItem.Header getHeader() {
- return differ.getCurrentList()
- .get(0)
- .asHeaderItem();
- }
-
- private enum HeaderDetail {DESCRIPTION, COMMENT, DATE_YEAR, DATE_MONTH, DATE_DAY}
-
- private enum AccDetail {ACCOUNT, COMMENT, AMOUNT}
-
- public abstract static class ViewHolder extends RecyclerView.ViewHolder {
- ViewHolder(@NonNull View itemView) {
- super(itemView);
- }
- abstract void bind(PatternDetailsItem item);
- }
-
- public class Header extends ViewHolder {
- private final PatternDetailsHeaderBinding b;
- public Header(@NonNull PatternDetailsHeaderBinding binding) {
- super(binding.getRoot());
- b = binding;
-
- TextWatcher patternNameWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
- @Override
- public void afterTextChanged(Editable s) {
- final PatternDetailsItem.Header header = getItem();
- Logger.debug(D_PATTERN_UI,
- "Storing changed pattern name " + s + "; header=" + header);
- header.setName(String.valueOf(s));
- }
- };
- b.patternName.addTextChangedListener(patternNameWatcher);
- TextWatcher patternWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
- @Override
- public void afterTextChanged(Editable s) {
- final PatternDetailsItem.Header header = getItem();
- Logger.debug(D_PATTERN_UI,
- "Storing changed pattern " + s + "; header=" + header);
- header.setPattern(String.valueOf(s));
- }
- };
- b.pattern.addTextChangedListener(patternWatcher);
- TextWatcher testTextWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
- @Override
- public void afterTextChanged(Editable s) {
- final PatternDetailsItem.Header header = getItem();
- Logger.debug(D_PATTERN_UI,
- "Storing changed test text " + s + "; header=" + header);
- header.setTestText(String.valueOf(s));
- }
- };
- b.testText.addTextChangedListener(testTextWatcher);
- TextWatcher transactionDescriptionWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
-
- }
- @Override
- public void afterTextChanged(Editable s) {
- final PatternDetailsItem.Header header = getItem();
- Logger.debug(D_PATTERN_UI,
- "Storing changed transaction description " + s + "; header=" + header);
- header.setTransactionDescription(String.valueOf(s));
- }
- };
- b.transactionDescription.addTextChangedListener(transactionDescriptionWatcher);
- TextWatcher transactionCommentWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-
- }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
-
- }
- @Override
- public void afterTextChanged(Editable s) {
- final PatternDetailsItem.Header header = getItem();
- Logger.debug(D_PATTERN_UI,
- "Storing changed transaction description " + s + "; header=" + header);
- header.setTransactionComment(String.valueOf(s));
- }
- };
- b.transactionComment.addTextChangedListener(transactionCommentWatcher);
- }
- @NotNull
- private PatternDetailsItem.Header getItem() {
- int pos = getAdapterPosition();
- return differ.getCurrentList()
- .get(pos)
- .asHeaderItem();
- }
- private void selectHeaderDetailSource(View v, HeaderDetail detail) {
- PatternDetailsItem.Header header = getItem();
- Logger.debug(D_PATTERN_UI, "header is " + header);
- PatternDetailSourceSelectorFragment sel =
- PatternDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
- header.getTestText());
- sel.setOnSourceSelectedListener((literal, group) -> {
- if (literal) {
- switch (detail) {
- case DESCRIPTION:
- header.switchToLiteralTransactionDescription();
- break;
- case COMMENT:
- header.switchToLiteralTransactionComment();
- break;
- case DATE_YEAR:
- header.switchToLiteralDateYear();
- break;
- case DATE_MONTH:
- header.switchToLiteralDateMonth();
- break;
- case DATE_DAY:
- header.switchToLiteralDateDay();
- break;
- default:
- throw new IllegalStateException("Unexpected detail " + detail);
- }
- }
- else {
- switch (detail) {
- case DESCRIPTION:
- header.setTransactionDescriptionMatchGroup(group);
- break;
- case COMMENT:
- header.setTransactionCommentMatchGroup(group);
- break;
- case DATE_YEAR:
- header.setDateYearMatchGroup(group);
- break;
- case DATE_MONTH:
- header.setDateMonthMatchGroup(group);
- break;
- case DATE_DAY:
- header.setDateDayMatchGroup(group);
- break;
- default:
- throw new IllegalStateException("Unexpected detail " + detail);
- }
- }
-
- notifyItemChanged(getAdapterPosition());
- });
- final AppCompatActivity activity = (AppCompatActivity) v.getContext();
- sel.show(activity.getSupportFragmentManager(), "pattern-details-source-selector");
- }
- @Override
- void bind(PatternDetailsItem item) {
- PatternDetailsItem.Header header = item.asHeaderItem();
- Logger.debug(D_PATTERN_UI, "Binding to header " + header);
-
- b.patternName.setText(header.getName());
- b.pattern.setText(header.getPattern());
- b.testText.setText(header.getTestText());
-
- if (header.hasLiteralDateYear()) {
- b.patternDetailsYearSource.setText(R.string.pattern_details_source_literal);
- final Integer dateYear = header.getDateYear();
- b.patternDetailsDateYear.setText(
- (dateYear == null) ? null : String.valueOf(dateYear));
- b.patternDetailsDateYearLayout.setVisibility(View.VISIBLE);
- }
- else {
- b.patternDetailsDateYearLayout.setVisibility(View.GONE);
- b.patternDetailsYearSource.setText(
- String.format(Locale.US, "Group %d (%s)", header.getDateYearMatchGroup(),
- getMatchGroupText(header.getDateYearMatchGroup())));
- }
- b.patternDetailsYearSourceLabel.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
- b.patternDetailsYearSource.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
-
- if (header.hasLiteralDateMonth()) {
- b.patternDetailsMonthSource.setText(R.string.pattern_details_source_literal);
- final Integer dateMonth = header.getDateMonth();
- b.patternDetailsDateMonth.setText(
- (dateMonth == null) ? null : String.valueOf(dateMonth));
- b.patternDetailsDateMonthLayout.setVisibility(View.VISIBLE);
- }
- else {
- b.patternDetailsDateMonthLayout.setVisibility(View.GONE);
- b.patternDetailsMonthSource.setText(
- String.format(Locale.US, "Group %d (%s)", header.getDateMonthMatchGroup(),
- getMatchGroupText(header.getDateMonthMatchGroup())));
- }
- b.patternDetailsMonthSourceLabel.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
- b.patternDetailsMonthSource.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
-
- if (header.hasLiteralDateDay()) {
- b.patternDetailsDaySource.setText(R.string.pattern_details_source_literal);
- final Integer dateDay = header.getDateDay();
- b.patternDetailsDateDay.setText((dateDay == null) ? null : String.valueOf(dateDay));
- b.patternDetailsDateDayLayout.setVisibility(View.VISIBLE);
- }
- else {
- b.patternDetailsDateDayLayout.setVisibility(View.GONE);
- b.patternDetailsDaySource.setText(
- String.format(Locale.US, "Group %d (%s)", header.getDateDayMatchGroup(),
- getMatchGroupText(header.getDateDayMatchGroup())));
- }
- b.patternDetailsDaySourceLabel.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
- b.patternDetailsDaySource.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
-
- if (header.hasLiteralTransactionDescription()) {
- b.patternTransactionDescriptionSource.setText(
- R.string.pattern_details_source_literal);
- b.transactionDescription.setText(header.getTransactionDescription());
- b.transactionDescriptionLayout.setVisibility(View.VISIBLE);
- }
- else {
- b.transactionDescriptionLayout.setVisibility(View.GONE);
- b.patternTransactionDescriptionSource.setText(
- String.format(Locale.US, "Group %d (%s)",
- header.getTransactionDescriptionMatchGroup(),
- getMatchGroupText(header.getTransactionDescriptionMatchGroup())));
-
- }
- b.patternTransactionDescriptionSourceLabel.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
- b.patternTransactionDescriptionSource.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
-
- if (header.hasLiteralTransactionComment()) {
- b.patternTransactionCommentSource.setText(R.string.pattern_details_source_literal);
- b.transactionComment.setText(header.getTransactionComment());
- b.transactionCommentLayout.setVisibility(View.VISIBLE);
- }
- else {
- b.transactionCommentLayout.setVisibility(View.GONE);
- b.patternTransactionCommentSource.setText(String.format(Locale.US, "Group %d (%s)",
- header.getTransactionCommentMatchGroup(),
- getMatchGroupText(header.getTransactionCommentMatchGroup())));
-
- }
- b.patternTransactionCommentSourceLabel.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
- b.patternTransactionCommentSource.setOnClickListener(
- v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
-
- b.patternDetailsHeadScanQrButton.setOnClickListener(this::scanTestQR);
-
- }
- private void scanTestQR(View view) {
- QRScanCapableFragment.triggerQRScan();
- }
- }
-
- public class AccountRow extends ViewHolder {
- private final PatternDetailsAccountBinding b;
- public AccountRow(@NonNull PatternDetailsAccountBinding binding) {
- super(binding.getRoot());
- b = binding;
-
- TextWatcher accountNameWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
- @Override
- public void afterTextChanged(Editable s) {
- PatternDetailsItem.AccountRow accRow = getItem();
- Logger.debug(D_PATTERN_UI,
- "Storing changed account name " + s + "; accRow=" + accRow);
- accRow.setAccountName(String.valueOf(s));
- }
- };
- b.patternDetailsAccountName.addTextChangedListener(accountNameWatcher);
- TextWatcher accountCommentWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
- @Override
- public void afterTextChanged(Editable s) {
- PatternDetailsItem.AccountRow accRow = getItem();
- Logger.debug(D_PATTERN_UI,
- "Storing changed account comment " + s + "; accRow=" + accRow);
- accRow.setAccountComment(String.valueOf(s));
- }
- };
- b.patternDetailsAccountComment.addTextChangedListener(accountCommentWatcher);
-
- b.patternDetailsAccountAmount.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-
- }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
-
- }
- @Override
- public void afterTextChanged(Editable s) {
- PatternDetailsItem.AccountRow accRow = getItem();
-
- String str = String.valueOf(s);
- if (Misc.emptyIsNull(str) == null) {
- accRow.setAmount(null);
- }
- else {
- try {
- final float amount = Data.parseNumber(str);
- accRow.setAmount(amount);
- b.patternDetailsAccountAmountLayout.setError(null);
-
- Logger.debug(D_PATTERN_UI, String.format(Locale.US,
- "Storing changed account amount %s [%4.2f]; accRow=%s", s,
- amount, accRow));
- }
- catch (NumberFormatException | ParseException e) {
- b.patternDetailsAccountAmountLayout.setError("!");
- }
- }
- }
- });
- b.patternDetailsAccountAmount.setOnFocusChangeListener((v, hasFocus) -> {
- if (hasFocus)
- return;
-
- PatternDetailsItem.AccountRow accRow = getItem();
- if (!accRow.hasLiteralAmount())
- return;
- Float amt = accRow.getAmount();
- if (amt == null)
- return;
-
- b.patternDetailsAccountAmount.setText(Data.formatNumber(amt));
- });
-
- b.negateAmountSwitch.setOnCheckedChangeListener(
- (buttonView, isChecked) -> getItem().setNegateAmount(isChecked));
- }
- @Override
- void bind(PatternDetailsItem item) {
- PatternDetailsItem.AccountRow accRow = item.asAccountRowItem();
- if (accRow.hasLiteralAccountName()) {
- b.patternDetailsAccountNameLayout.setVisibility(View.VISIBLE);
- b.patternDetailsAccountName.setText(accRow.getAccountName());
- b.patternDetailsAccountNameSource.setText(R.string.pattern_details_source_literal);
- }
- else {
- b.patternDetailsAccountNameLayout.setVisibility(View.GONE);
- b.patternDetailsAccountNameSource.setText(
- String.format(Locale.US, "Group %d (%s)", accRow.getAccountNameMatchGroup(),
- getMatchGroupText(accRow.getAccountNameMatchGroup())));
- }
-
- if (accRow.hasLiteralAccountComment()) {
- b.patternDetailsAccountCommentLayout.setVisibility(View.VISIBLE);
- b.patternDetailsAccountComment.setText(accRow.getAccountComment());
- b.patternDetailsAccountCommentSource.setText(
- R.string.pattern_details_source_literal);
- }
- else {
- b.patternDetailsAccountCommentLayout.setVisibility(View.GONE);
- b.patternDetailsAccountCommentSource.setText(
- String.format(Locale.US, "Group %d (%s)",
- accRow.getAccountCommentMatchGroup(),
- getMatchGroupText(accRow.getAccountCommentMatchGroup())));
- }
-
- if (accRow.hasLiteralAmount()) {
- b.patternDetailsAccountAmountSource.setText(
- R.string.pattern_details_source_literal);
- b.patternDetailsAccountAmount.setVisibility(View.VISIBLE);
- Float amt = accRow.getAmount();
- b.patternDetailsAccountAmount.setText((amt == null) ? null : String.format(
- Data.locale.getValue(), "%,4.2f", (accRow.getAmount())));
- b.negateAmountSwitch.setVisibility(View.GONE);
- }
- else {
- b.patternDetailsAccountAmountSource.setText(
- String.format(Locale.US, "Group %d (%s)", accRow.getAmountMatchGroup(),
- getMatchGroupText(accRow.getAmountMatchGroup())));
- b.patternDetailsAccountAmountLayout.setVisibility(View.GONE);
- b.negateAmountSwitch.setVisibility(View.VISIBLE);
- b.negateAmountSwitch.setChecked(accRow.isNegateAmount());
- }
-
- b.patternAccountNameSourceLabel.setOnClickListener(
- v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
- b.patternDetailsAccountNameSource.setOnClickListener(
- v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
- b.patternAccountCommentSourceLabel.setOnClickListener(
- v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
- b.patternDetailsAccountCommentSource.setOnClickListener(
- v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
- b.patternAccountAmountSourceLabel.setOnClickListener(
- v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
- b.patternDetailsAccountAmountSource.setOnClickListener(
- v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
- }
- private @NotNull PatternDetailsItem.AccountRow getItem() {
- return differ.getCurrentList()
- .get(getAdapterPosition())
- .asAccountRowItem();
- }
- private void selectAccountRowDetailSource(View v, AccDetail detail) {
- PatternDetailsItem.AccountRow accRow = getItem();
- final PatternDetailsItem.Header header = getHeader();
- Logger.debug(D_PATTERN_UI, "header is " + header);
- PatternDetailSourceSelectorFragment sel =
- PatternDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
- header.getTestText());
- sel.setOnSourceSelectedListener((literal, group) -> {
- if (literal) {
- switch (detail) {
- case ACCOUNT:
- accRow.switchToLiteralAccountName();
- break;
- case COMMENT:
- accRow.switchToLiteralAccountComment();
- break;
- case AMOUNT:
- accRow.switchToLiteralAmount();
- break;
- default:
- throw new IllegalStateException("Unexpected detail " + detail);
- }
- }
- else {
- switch (detail) {
- case ACCOUNT:
- accRow.setAccountNameMatchGroup(group);
- break;
- case COMMENT:
- accRow.setAccountCommentMatchGroup(group);
- break;
- case AMOUNT:
- accRow.setAmountMatchGroup(group);
- break;
- default:
- throw new IllegalStateException("Unexpected detail " + detail);
- }
- }
-
- notifyItemChanged(getAdapterPosition());
- });
- final AppCompatActivity activity = (AppCompatActivity) v.getContext();
- sel.show(activity.getSupportFragmentManager(), "pattern-details-source-selector");
- }
- }
-}
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui.patterns;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.lifecycle.ViewModelStoreOwner;
-import androidx.navigation.NavController;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.LinearLayoutManager;
-
-import com.google.android.material.snackbar.Snackbar;
-
-import net.ktnx.mobileledger.R;
-import net.ktnx.mobileledger.databinding.PatternDetailsFragmentBinding;
-import net.ktnx.mobileledger.ui.QRScanCapableFragment;
-import net.ktnx.mobileledger.utils.Logger;
-
-public class PatternDetailsFragment extends QRScanCapableFragment {
- static final String ARG_PATTERN_ID = "pattern-id";
- private static final String ARG_COLUMN_COUNT = "column-count";
- PatternDetailsFragmentBinding b;
- private PatternDetailsViewModel mViewModel;
- private int mColumnCount = 1;
- private Long mPatternId;
- public PatternDetailsFragment() {
- }
- public static PatternDetailsFragment newInstance(int columnCount, int patternId) {
- final PatternDetailsFragment fragment = new PatternDetailsFragment();
- Bundle args = new Bundle();
- args.putInt(ARG_COLUMN_COUNT, columnCount);
- if (patternId > 0)
- args.putInt(ARG_PATTERN_ID, patternId);
- fragment.setArguments(args);
- return fragment;
- }
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final Bundle args = getArguments();
- if (args != null) {
- mColumnCount = args.getInt(ARG_COLUMN_COUNT, 1);
- mPatternId = args.getLong(ARG_PATTERN_ID, -1);
- if (mPatternId == -1)
- mPatternId = null;
- }
- }
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- NavController controller = ((PatternsActivity) requireActivity()).getNavController();
- final ViewModelStoreOwner viewModelStoreOwner =
- controller.getViewModelStoreOwner(R.id.pattern_list_navigation);
- mViewModel = new ViewModelProvider(viewModelStoreOwner).get(PatternDetailsViewModel.class);
- mViewModel.setDefaultPatternName(getString(R.string.unnamed_pattern));
- Logger.debug("flow", "PatternDetailsFragment.onCreateView(): model=" + mViewModel);
-
- b = PatternDetailsFragmentBinding.inflate(inflater);
- Context context = b.patternDetailsRecyclerView.getContext();
- if (mColumnCount <= 1) {
- b.patternDetailsRecyclerView.setLayoutManager(new LinearLayoutManager(context));
- }
- else {
- b.patternDetailsRecyclerView.setLayoutManager(
- new GridLayoutManager(context, mColumnCount));
- }
-
-
- PatternDetailsAdapter adapter = new PatternDetailsAdapter();
- b.patternDetailsRecyclerView.setAdapter(adapter);
- mViewModel.getItems(mPatternId)
- .observe(getViewLifecycleOwner(), adapter::setItems);
-
- return b.getRoot();
- }
- @Override
- protected void onQrScanned(String text) {
- Logger.debug("PatDet_fr", String.format("Got scanned text '%s'", text));
- if (text != null)
- mViewModel.setTestText(text);
- }
- public void onSavePattern() {
- mViewModel.onSavePattern();
- final Snackbar snackbar = Snackbar.make(b.getRoot(),
- "One Save pattern action coming up soon in a fragment near you",
- Snackbar.LENGTH_INDEFINITE);
-// snackbar.setAction("Action", v -> snackbar.dismiss());
- snackbar.show();
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui.patterns;
-
-import android.os.AsyncTask;
-
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.Observer;
-import androidx.lifecycle.ViewModel;
-
-import net.ktnx.mobileledger.dao.PatternAccountDAO;
-import net.ktnx.mobileledger.dao.PatternHeaderDAO;
-import net.ktnx.mobileledger.db.DB;
-import net.ktnx.mobileledger.db.PatternAccount;
-import net.ktnx.mobileledger.db.PatternHeader;
-import net.ktnx.mobileledger.db.PatternWithAccounts;
-import net.ktnx.mobileledger.model.PatternDetailsItem;
-import net.ktnx.mobileledger.utils.Logger;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-
-public class PatternDetailsViewModel extends ViewModel {
- private final MutableLiveData<List<PatternDetailsItem>> items =
- new MutableLiveData<>(Collections.emptyList());
- private Long mPatternId;
- private String mDefaultPatternName;
- public String getDefaultPatternName() {
- return mDefaultPatternName;
- }
- public void setDefaultPatternName(String name) {
- mDefaultPatternName = name;
- }
-
- public void resetItems() {
- ArrayList<PatternDetailsItem> newList = new ArrayList<>();
- final PatternDetailsItem.Header header = PatternDetailsItem.createHeader();
- header.setName(mDefaultPatternName);
- header.setId(0);
- newList.add(header);
-
- while (newList.size() < 3) {
- final PatternDetailsItem.AccountRow aRow = PatternDetailsItem.createAccountRow();
- aRow.setId(newList.size() + 1);
- newList.add(aRow);
- }
-
- items.setValue(newList);
- }
- private void checkItemConsistency() {
- ArrayList<PatternDetailsItem> newList = new ArrayList<>(items.getValue());
- boolean changes = false;
- if (newList.size() < 1) {
- final PatternDetailsItem.Header header = PatternDetailsItem.createHeader();
- header.setName(mDefaultPatternName);
- newList.add(header);
- changes = true;
- }
-
- while (newList.size() < 3) {
- newList.add(PatternDetailsItem.createAccountRow());
- changes = true;
- }
-
- if (changes)
- items.setValue(newList);
- }
- public LiveData<List<PatternDetailsItem>> getItems(Long patternId) {
- if (patternId != null && patternId <= 0)
- throw new IllegalArgumentException("Pattern ID " + patternId + " is invalid");
-
- mPatternId = patternId;
-
- if (mPatternId == null) {
- resetItems();
- return items;
- }
-
- DB db = DB.get();
- LiveData<PatternWithAccounts> dbList = db.getPatternDAO()
- .getPatternWithAccounts(mPatternId);
- Observer<PatternWithAccounts> observer = new Observer<PatternWithAccounts>() {
- @Override
- public void onChanged(PatternWithAccounts src) {
- ArrayList<PatternDetailsItem> l = new ArrayList<>();
-
- PatternDetailsItem header = PatternDetailsItem.fromRoomObject(src.header);
- l.add(header);
- for (PatternAccount acc : src.accounts) {
- l.add(PatternDetailsItem.fromRoomObject(acc));
- }
-
- for (PatternDetailsItem i : l) {
- Logger.debug("patterns-db", "Loaded pattern item " + i);
- }
- items.postValue(l);
-
- dbList.removeObserver(this);
- }
- };
- dbList.observeForever(observer);
-
- return items;
- }
- public void setTestText(String text) {
- List<PatternDetailsItem> list = new ArrayList<>(items.getValue());
- PatternDetailsItem.Header header = new PatternDetailsItem.Header(list.get(0)
- .asHeaderItem());
- header.setTestText(text);
- list.set(0, header);
-
- items.setValue(list);
- }
- public void onSavePattern() {
- Logger.debug("flow", "PatternDetailsViewModel.onSavePattern(); model=" + this);
- final List<PatternDetailsItem> list = Objects.requireNonNull(items.getValue());
-
- AsyncTask.execute(() -> {
- boolean newPattern = mPatternId == null || mPatternId <= 0;
-
- PatternDetailsItem.Header modelHeader = list.get(0)
- .asHeaderItem();
- PatternHeaderDAO headerDAO = DB.get()
- .getPatternDAO();
- PatternHeader dbHeader = modelHeader.toDBO();
- if (newPattern) {
- dbHeader.setId(null);
- dbHeader.setId(mPatternId = headerDAO.insert(dbHeader));
- }
- else
- headerDAO.update(dbHeader);
-
- Logger.debug("pattern-db",
- String.format(Locale.US, "Stored pattern header %d, item=%s", dbHeader.getId(),
- modelHeader));
-
-
- PatternAccountDAO paDAO = DB.get()
- .getPatternAccountDAO();
- for (int i = 1; i < list.size(); i++) {
- final PatternDetailsItem.AccountRow accRowItem = list.get(i)
- .asAccountRowItem();
- PatternAccount dbAccount = accRowItem.toDBO(dbHeader.getId());
- dbAccount.setPatternId(mPatternId);
- dbAccount.setPosition(i);
- if (newPattern) {
- dbAccount.setId(null);
- dbAccount.setId(paDAO.insert(dbAccount));
- }
- else
- paDAO.update(dbAccount);
-
- Logger.debug("pattern-db", String.format(Locale.US,
- "Stored pattern account %d, account=%s, comment=%s, neg=%s, item=%s",
- dbAccount.getId(), dbAccount.getAccountName(),
- dbAccount.getAccountComment(), dbAccount.getNegateAmount(), accRowItem));
- }
- });
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui.patterns;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleEventObserver;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LiveData;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import net.ktnx.mobileledger.dao.PatternHeaderDAO;
-import net.ktnx.mobileledger.databinding.FragmentPatternListBinding;
-import net.ktnx.mobileledger.db.DB;
-import net.ktnx.mobileledger.db.PatternHeader;
-import net.ktnx.mobileledger.utils.Logger;
-
-import org.jetbrains.annotations.NotNull;
-
-import java.util.List;
-
-/**
- * A simple {@link Fragment} subclass.
- * Use the {@link PatternListFragment#newInstance} factory method to
- * create an instance of this fragment.
- */
-public class PatternListFragment extends Fragment {
- private FragmentPatternListBinding b;
- private OnPatternListFragmentInteractionListener mListener;
-
- public PatternListFragment() {
- // Required empty public constructor
- }
- /**
- * Use this factory method to create a new instance of
- * this fragment using the provided parameters.
- *
- * @return A new instance of fragment PatternListFragment.
- */
- // TODO: Rename and change types and number of parameters
- public static PatternListFragment newInstance() {
- PatternListFragment fragment = new PatternListFragment();
- Bundle args = new Bundle();
- fragment.setArguments(args);
- return fragment;
- }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-// if (getArguments() != null) {
-// mParam1 = getArguments().getString(ARG_PARAM1);
-// mParam2 = getArguments().getString(ARG_PARAM2);
-// }
- }
-
- @Override
- public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- Logger.debug("flow", "PatternListFragment.onCreateView()");
- b = FragmentPatternListBinding.inflate(inflater);
-
- PatternsRecyclerViewAdapter modelAdapter = new PatternsRecyclerViewAdapter();
-
- b.patternList.setAdapter(modelAdapter);
- PatternHeaderDAO pDao = DB.get()
- .getPatternDAO();
- LiveData<List<PatternHeader>> patterns = pDao.getPatterns();
- patterns.observe(getViewLifecycleOwner(), modelAdapter::setPatterns);
- LinearLayoutManager llm = new LinearLayoutManager(getContext());
- llm.setOrientation(RecyclerView.VERTICAL);
- b.patternList.setLayoutManager(llm);
- return b.getRoot();
- }
- @Override
- public void onAttach(@NonNull Context context) {
- super.onAttach(context);
- if (context instanceof OnPatternListFragmentInteractionListener) {
- mListener = (OnPatternListFragmentInteractionListener) context;
- }
- else {
- throw new RuntimeException(
- context.toString() + " must implement OnFragmentInteractionListener");
- }
-
- final LifecycleEventObserver observer = new LifecycleEventObserver() {
- @Override
- public void onStateChanged(@NonNull LifecycleOwner source,
- @NonNull Lifecycle.Event event) {
- if (event.getTargetState() == Lifecycle.State.CREATED) {
-// getActivity().setActionBar(b.toolbar);
- getLifecycle().removeObserver(this);
- }
- }
- };
- getLifecycle().addObserver(observer);
- }
- /**
- * This interface must be implemented by activities that contain this
- * fragment to allow an interaction in this fragment to be communicated
- * to the activity and potentially other fragments contained in that
- * activity.
- * <p>
- * See the Android Training lesson <a href=
- * "http://developer.android.com/training/basics/fragments/communicating.html"
- * >Communicating with Other Fragments</a> for more information.
- */
- public interface OnPatternListFragmentInteractionListener {
- void onSavePattern();
-
- void onEditPattern(Long id);
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui.patterns;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import net.ktnx.mobileledger.databinding.PatternLayoutBinding;
-import net.ktnx.mobileledger.db.PatternHeader;
-
-class PatternViewHolder extends RecyclerView.ViewHolder {
- final PatternLayoutBinding b;
- public PatternViewHolder(@NonNull PatternLayoutBinding binding) {
- super(binding.getRoot());
- b = binding;
- }
- public void bindToItem(PatternHeader item) {
- b.patternName.setText(item.getName());
- b.editButton.setOnClickListener(v -> {
- ((PatternsActivity) v.getContext()).onEditPattern(item.getId());
- });
- }
-}
+++ /dev/null
-/*
- * Copyright © 2021 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.ui.patterns;
-
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.AsyncListDiffer;
-import androidx.recyclerview.widget.DiffUtil;
-import androidx.recyclerview.widget.RecyclerView;
-
-import net.ktnx.mobileledger.databinding.PatternLayoutBinding;
-import net.ktnx.mobileledger.db.PatternHeader;
-
-import org.jetbrains.annotations.NotNull;
-
-import java.util.List;
-
-public class PatternsRecyclerViewAdapter extends RecyclerView.Adapter<PatternViewHolder> {
- private final AsyncListDiffer<PatternHeader> listDiffer;
- public PatternsRecyclerViewAdapter() {
- listDiffer = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<PatternHeader>() {
- @Override
- public boolean areItemsTheSame(@NotNull PatternHeader oldItem,
- @NotNull PatternHeader newItem) {
- return oldItem.getId()
- .equals(newItem.getId());
- }
- @Override
- public boolean areContentsTheSame(@NotNull PatternHeader oldItem,
- @NotNull PatternHeader newItem) {
- return oldItem.equals(newItem);
- }
- });
- }
- @NonNull
- @Override
- public PatternViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- PatternLayoutBinding b =
- PatternLayoutBinding.inflate(LayoutInflater.from(parent.getContext()), parent,
- false);
-
- return new PatternViewHolder(b);
- }
- @Override
- public void onBindViewHolder(@NonNull PatternViewHolder holder, int position) {
- holder.bindToItem(listDiffer.getCurrentList()
- .get(position));
- }
- @Override
- public int getItemCount() {
- return listDiffer.getCurrentList()
- .size();
- }
- public void setPatterns(List<PatternHeader> newList) {
- listDiffer.submitList(newList);
- }
-}
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui.templates;
+
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.AsyncListDiffer;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.databinding.TemplateDetailsAccountBinding;
+import net.ktnx.mobileledger.databinding.TemplateDetailsHeaderBinding;
+import net.ktnx.mobileledger.db.TemplateBase;
+import net.ktnx.mobileledger.model.Data;
+import net.ktnx.mobileledger.model.TemplateDetailsItem;
+import net.ktnx.mobileledger.ui.QRScanCapableFragment;
+import net.ktnx.mobileledger.ui.TemplateDetailSourceSelectorFragment;
+import net.ktnx.mobileledger.utils.Logger;
+import net.ktnx.mobileledger.utils.Misc;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+class TemplateDetailsAdapter extends RecyclerView.Adapter<TemplateDetailsAdapter.ViewHolder> {
+ private static final String D_TEMPLATE_UI = "template-ui";
+ private final AsyncListDiffer<TemplateDetailsItem> differ;
+ public TemplateDetailsAdapter() {
+ super();
+ setHasStableIds(true);
+ differ = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<TemplateDetailsItem>() {
+ @Override
+ public boolean areItemsTheSame(@NonNull TemplateDetailsItem oldItem,
+ @NonNull TemplateDetailsItem newItem) {
+ if (oldItem.getType() != newItem.getType())
+ return false;
+ if (oldItem.getType()
+ .equals(TemplateDetailsItem.Type.HEADER))
+ return true; // only one header item, ever
+ // the rest is comparing two account row items
+ return oldItem.asAccountRowItem()
+ .getId() == newItem.asAccountRowItem()
+ .getId();
+ }
+ @Override
+ public boolean areContentsTheSame(@NonNull TemplateDetailsItem oldItem,
+ @NonNull TemplateDetailsItem newItem) {
+ if (oldItem.getType()
+ .equals(TemplateDetailsItem.Type.HEADER))
+ {
+ TemplateDetailsItem.Header oldHeader = oldItem.asHeaderItem();
+ TemplateDetailsItem.Header newHeader = newItem.asHeaderItem();
+
+ return oldHeader.equalContents(newHeader);
+ }
+ else {
+ TemplateDetailsItem.AccountRow oldAcc = oldItem.asAccountRowItem();
+ TemplateDetailsItem.AccountRow newAcc = newItem.asAccountRowItem();
+
+ return oldAcc.equalContents(newAcc);
+ }
+ }
+ });
+ }
+ @Override
+ public long getItemId(int position) {
+ // header item is always first and IDs id may duplicate some of the account IDs
+ if (position == 0)
+ return -1;
+ TemplateDetailsItem.AccountRow accRow = differ.getCurrentList()
+ .get(position)
+ .asAccountRowItem();
+ return accRow.getId();
+ }
+ @Override
+ public int getItemViewType(int position) {
+
+ return differ.getCurrentList()
+ .get(position)
+ .getType()
+ .toInt();
+ }
+ @NonNull
+ @Override
+ public TemplateDetailsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
+ int viewType) {
+ final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+ switch (viewType) {
+ case TemplateDetailsItem.TYPE.header:
+ return new Header(TemplateDetailsHeaderBinding.inflate(inflater, parent, false));
+ case TemplateDetailsItem.TYPE.accountItem:
+ return new AccountRow(
+ TemplateDetailsAccountBinding.inflate(inflater, parent, false));
+ default:
+ throw new IllegalStateException("Unsupported view type " + viewType);
+ }
+ }
+ @Override
+ public void onBindViewHolder(@NonNull TemplateDetailsAdapter.ViewHolder holder, int position) {
+ TemplateDetailsItem item = differ.getCurrentList()
+ .get(position);
+ holder.bind(item);
+ }
+ @Override
+ public int getItemCount() {
+ return differ.getCurrentList()
+ .size();
+ }
+ public void setTemplateItems(List<TemplateBase> items) {
+ ArrayList<TemplateDetailsItem> list = new ArrayList<>();
+ for (TemplateBase p : items) {
+ TemplateDetailsItem item = TemplateDetailsItem.fromRoomObject(p);
+ list.add(item);
+ }
+ setItems(list);
+ }
+ public void setItems(List<TemplateDetailsItem> items) {
+ differ.submitList(items);
+ }
+ public String getMatchGroupText(int groupNumber) {
+ TemplateDetailsItem.Header header = getHeader();
+ Pattern p = header.getCompiledPattern();
+ if (p == null)
+ return null;
+
+ final String testText = Misc.nullIsEmpty(header.getTestText());
+ Matcher m = p.matcher(testText);
+ if (m.matches() && m.groupCount() >= groupNumber)
+ return m.group(groupNumber);
+ else
+ return null;
+ }
+ protected TemplateDetailsItem.Header getHeader() {
+ return differ.getCurrentList()
+ .get(0)
+ .asHeaderItem();
+ }
+
+ private enum HeaderDetail {DESCRIPTION, COMMENT, DATE_YEAR, DATE_MONTH, DATE_DAY}
+
+ private enum AccDetail {ACCOUNT, COMMENT, AMOUNT}
+
+ public abstract static class ViewHolder extends RecyclerView.ViewHolder {
+ ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ }
+ abstract void bind(TemplateDetailsItem item);
+ }
+
+ public class Header extends ViewHolder {
+ private final TemplateDetailsHeaderBinding b;
+ public Header(@NonNull TemplateDetailsHeaderBinding binding) {
+ super(binding.getRoot());
+ b = binding;
+
+ TextWatcher templateNameWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+ @Override
+ public void afterTextChanged(Editable s) {
+ final TemplateDetailsItem.Header header = getItem();
+ Logger.debug(D_TEMPLATE_UI,
+ "Storing changed template name " + s + "; header=" + header);
+ header.setName(String.valueOf(s));
+ }
+ };
+ b.templateName.addTextChangedListener(templateNameWatcher);
+ TextWatcher patternWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+ @Override
+ public void afterTextChanged(Editable s) {
+ final TemplateDetailsItem.Header header = getItem();
+ Logger.debug(D_TEMPLATE_UI,
+ "Storing changed pattern " + s + "; header=" + header);
+ header.setPattern(String.valueOf(s));
+ }
+ };
+ b.pattern.addTextChangedListener(patternWatcher);
+ TextWatcher testTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+ @Override
+ public void afterTextChanged(Editable s) {
+ final TemplateDetailsItem.Header header = getItem();
+ Logger.debug(D_TEMPLATE_UI,
+ "Storing changed test text " + s + "; header=" + header);
+ header.setTestText(String.valueOf(s));
+ }
+ };
+ b.testText.addTextChangedListener(testTextWatcher);
+ TextWatcher transactionDescriptionWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+ @Override
+ public void afterTextChanged(Editable s) {
+ final TemplateDetailsItem.Header header = getItem();
+ Logger.debug(D_TEMPLATE_UI,
+ "Storing changed transaction description " + s + "; header=" + header);
+ header.setTransactionDescription(String.valueOf(s));
+ }
+ };
+ b.transactionDescription.addTextChangedListener(transactionDescriptionWatcher);
+ TextWatcher transactionCommentWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+ @Override
+ public void afterTextChanged(Editable s) {
+ final TemplateDetailsItem.Header header = getItem();
+ Logger.debug(D_TEMPLATE_UI,
+ "Storing changed transaction description " + s + "; header=" + header);
+ header.setTransactionComment(String.valueOf(s));
+ }
+ };
+ b.transactionComment.addTextChangedListener(transactionCommentWatcher);
+ }
+ @NotNull
+ private TemplateDetailsItem.Header getItem() {
+ int pos = getAdapterPosition();
+ return differ.getCurrentList()
+ .get(pos)
+ .asHeaderItem();
+ }
+ private void selectHeaderDetailSource(View v, HeaderDetail detail) {
+ TemplateDetailsItem.Header header = getItem();
+ Logger.debug(D_TEMPLATE_UI, "header is " + header);
+ TemplateDetailSourceSelectorFragment sel =
+ TemplateDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
+ header.getTestText());
+ sel.setOnSourceSelectedListener((literal, group) -> {
+ if (literal) {
+ switch (detail) {
+ case DESCRIPTION:
+ header.switchToLiteralTransactionDescription();
+ break;
+ case COMMENT:
+ header.switchToLiteralTransactionComment();
+ break;
+ case DATE_YEAR:
+ header.switchToLiteralDateYear();
+ break;
+ case DATE_MONTH:
+ header.switchToLiteralDateMonth();
+ break;
+ case DATE_DAY:
+ header.switchToLiteralDateDay();
+ break;
+ default:
+ throw new IllegalStateException("Unexpected detail " + detail);
+ }
+ }
+ else {
+ switch (detail) {
+ case DESCRIPTION:
+ header.setTransactionDescriptionMatchGroup(group);
+ break;
+ case COMMENT:
+ header.setTransactionCommentMatchGroup(group);
+ break;
+ case DATE_YEAR:
+ header.setDateYearMatchGroup(group);
+ break;
+ case DATE_MONTH:
+ header.setDateMonthMatchGroup(group);
+ break;
+ case DATE_DAY:
+ header.setDateDayMatchGroup(group);
+ break;
+ default:
+ throw new IllegalStateException("Unexpected detail " + detail);
+ }
+ }
+
+ notifyItemChanged(getAdapterPosition());
+ });
+ final AppCompatActivity activity = (AppCompatActivity) v.getContext();
+ sel.show(activity.getSupportFragmentManager(), "template-details-source-selector");
+ }
+ @Override
+ void bind(TemplateDetailsItem item) {
+ TemplateDetailsItem.Header header = item.asHeaderItem();
+ Logger.debug(D_TEMPLATE_UI, "Binding to header " + header);
+
+ b.templateName.setText(header.getName());
+ b.pattern.setText(header.getPattern());
+ b.testText.setText(header.getTestText());
+
+ if (header.hasLiteralDateYear()) {
+ b.templateDetailsYearSource.setText(R.string.template_details_source_literal);
+ final Integer dateYear = header.getDateYear();
+ b.templateDetailsDateYear.setText(
+ (dateYear == null) ? null : String.valueOf(dateYear));
+ b.templateDetailsDateYearLayout.setVisibility(View.VISIBLE);
+ }
+ else {
+ b.templateDetailsDateYearLayout.setVisibility(View.GONE);
+ b.templateDetailsYearSource.setText(
+ String.format(Locale.US, "Group %d (%s)", header.getDateYearMatchGroup(),
+ getMatchGroupText(header.getDateYearMatchGroup())));
+ }
+ b.templateDetailsYearSourceLabel.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
+ b.templateDetailsYearSource.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR));
+
+ if (header.hasLiteralDateMonth()) {
+ b.templateDetailsMonthSource.setText(R.string.template_details_source_literal);
+ final Integer dateMonth = header.getDateMonth();
+ b.templateDetailsDateMonth.setText(
+ (dateMonth == null) ? null : String.valueOf(dateMonth));
+ b.templateDetailsDateMonthLayout.setVisibility(View.VISIBLE);
+ }
+ else {
+ b.templateDetailsDateMonthLayout.setVisibility(View.GONE);
+ b.templateDetailsMonthSource.setText(
+ String.format(Locale.US, "Group %d (%s)", header.getDateMonthMatchGroup(),
+ getMatchGroupText(header.getDateMonthMatchGroup())));
+ }
+ b.templateDetailsMonthSourceLabel.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
+ b.templateDetailsMonthSource.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH));
+
+ if (header.hasLiteralDateDay()) {
+ b.templateDetailsDaySource.setText(R.string.template_details_source_literal);
+ final Integer dateDay = header.getDateDay();
+ b.templateDetailsDateDay.setText(
+ (dateDay == null) ? null : String.valueOf(dateDay));
+ b.templateDetailsDateDayLayout.setVisibility(View.VISIBLE);
+ }
+ else {
+ b.templateDetailsDateDayLayout.setVisibility(View.GONE);
+ b.templateDetailsDaySource.setText(
+ String.format(Locale.US, "Group %d (%s)", header.getDateDayMatchGroup(),
+ getMatchGroupText(header.getDateDayMatchGroup())));
+ }
+ b.templateDetailsDaySourceLabel.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
+ b.templateDetailsDaySource.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY));
+
+ if (header.hasLiteralTransactionDescription()) {
+ b.templateTransactionDescriptionSource.setText(
+ R.string.template_details_source_literal);
+ b.transactionDescription.setText(header.getTransactionDescription());
+ b.transactionDescriptionLayout.setVisibility(View.VISIBLE);
+ }
+ else {
+ b.transactionDescriptionLayout.setVisibility(View.GONE);
+ b.templateTransactionDescriptionSource.setText(
+ String.format(Locale.US, "Group %d (%s)",
+ header.getTransactionDescriptionMatchGroup(),
+ getMatchGroupText(header.getTransactionDescriptionMatchGroup())));
+
+ }
+ b.templateTransactionDescriptionSourceLabel.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
+ b.templateTransactionDescriptionSource.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION));
+
+ if (header.hasLiteralTransactionComment()) {
+ b.templateTransactionCommentSource.setText(
+ R.string.template_details_source_literal);
+ b.transactionComment.setText(header.getTransactionComment());
+ b.transactionCommentLayout.setVisibility(View.VISIBLE);
+ }
+ else {
+ b.transactionCommentLayout.setVisibility(View.GONE);
+ b.templateTransactionCommentSource.setText(String.format(Locale.US, "Group %d (%s)",
+ header.getTransactionCommentMatchGroup(),
+ getMatchGroupText(header.getTransactionCommentMatchGroup())));
+
+ }
+ b.templateTransactionCommentSourceLabel.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
+ b.templateTransactionCommentSource.setOnClickListener(
+ v -> selectHeaderDetailSource(v, HeaderDetail.COMMENT));
+
+ b.templateDetailsHeadScanQrButton.setOnClickListener(this::scanTestQR);
+
+ }
+ private void scanTestQR(View view) {
+ QRScanCapableFragment.triggerQRScan();
+ }
+ }
+
+ public class AccountRow extends ViewHolder {
+ private final TemplateDetailsAccountBinding b;
+ public AccountRow(@NonNull TemplateDetailsAccountBinding binding) {
+ super(binding.getRoot());
+ b = binding;
+
+ TextWatcher accountNameWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+ @Override
+ public void afterTextChanged(Editable s) {
+ TemplateDetailsItem.AccountRow accRow = getItem();
+ Logger.debug(D_TEMPLATE_UI,
+ "Storing changed account name " + s + "; accRow=" + accRow);
+ accRow.setAccountName(String.valueOf(s));
+ }
+ };
+ b.templateDetailsAccountName.addTextChangedListener(accountNameWatcher);
+ TextWatcher accountCommentWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+ @Override
+ public void afterTextChanged(Editable s) {
+ TemplateDetailsItem.AccountRow accRow = getItem();
+ Logger.debug(D_TEMPLATE_UI,
+ "Storing changed account comment " + s + "; accRow=" + accRow);
+ accRow.setAccountComment(String.valueOf(s));
+ }
+ };
+ b.templateDetailsAccountComment.addTextChangedListener(accountCommentWatcher);
+
+ b.templateDetailsAccountAmount.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+ @Override
+ public void afterTextChanged(Editable s) {
+ TemplateDetailsItem.AccountRow accRow = getItem();
+
+ String str = String.valueOf(s);
+ if (Misc.emptyIsNull(str) == null) {
+ accRow.setAmount(null);
+ }
+ else {
+ try {
+ final float amount = Data.parseNumber(str);
+ accRow.setAmount(amount);
+ b.templateDetailsAccountAmountLayout.setError(null);
+
+ Logger.debug(D_TEMPLATE_UI, String.format(Locale.US,
+ "Storing changed account amount %s [%4.2f]; accRow=%s", s,
+ amount, accRow));
+ }
+ catch (NumberFormatException | ParseException e) {
+ b.templateDetailsAccountAmountLayout.setError("!");
+ }
+ }
+ }
+ });
+ b.templateDetailsAccountAmount.setOnFocusChangeListener((v, hasFocus) -> {
+ if (hasFocus)
+ return;
+
+ TemplateDetailsItem.AccountRow accRow = getItem();
+ if (!accRow.hasLiteralAmount())
+ return;
+ Float amt = accRow.getAmount();
+ if (amt == null)
+ return;
+
+ b.templateDetailsAccountAmount.setText(Data.formatNumber(amt));
+ });
+
+ b.negateAmountSwitch.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> getItem().setNegateAmount(isChecked));
+ }
+ @Override
+ void bind(TemplateDetailsItem item) {
+ TemplateDetailsItem.AccountRow accRow = item.asAccountRowItem();
+ if (accRow.hasLiteralAccountName()) {
+ b.templateDetailsAccountNameLayout.setVisibility(View.VISIBLE);
+ b.templateDetailsAccountName.setText(accRow.getAccountName());
+ b.templateDetailsAccountNameSource.setText(
+ R.string.template_details_source_literal);
+ }
+ else {
+ b.templateDetailsAccountNameLayout.setVisibility(View.GONE);
+ b.templateDetailsAccountNameSource.setText(
+ String.format(Locale.US, "Group %d (%s)", accRow.getAccountNameMatchGroup(),
+ getMatchGroupText(accRow.getAccountNameMatchGroup())));
+ }
+
+ if (accRow.hasLiteralAccountComment()) {
+ b.templateDetailsAccountCommentLayout.setVisibility(View.VISIBLE);
+ b.templateDetailsAccountComment.setText(accRow.getAccountComment());
+ b.templateDetailsAccountCommentSource.setText(
+ R.string.template_details_source_literal);
+ }
+ else {
+ b.templateDetailsAccountCommentLayout.setVisibility(View.GONE);
+ b.templateDetailsAccountCommentSource.setText(
+ String.format(Locale.US, "Group %d (%s)",
+ accRow.getAccountCommentMatchGroup(),
+ getMatchGroupText(accRow.getAccountCommentMatchGroup())));
+ }
+
+ if (accRow.hasLiteralAmount()) {
+ b.templateDetailsAccountAmountSource.setText(
+ R.string.template_details_source_literal);
+ b.templateDetailsAccountAmount.setVisibility(View.VISIBLE);
+ Float amt = accRow.getAmount();
+ b.templateDetailsAccountAmount.setText((amt == null) ? null : String.format(
+ Data.locale.getValue(), "%,4.2f", (accRow.getAmount())));
+ b.negateAmountSwitch.setVisibility(View.GONE);
+ }
+ else {
+ b.templateDetailsAccountAmountSource.setText(
+ String.format(Locale.US, "Group %d (%s)", accRow.getAmountMatchGroup(),
+ getMatchGroupText(accRow.getAmountMatchGroup())));
+ b.templateDetailsAccountAmountLayout.setVisibility(View.GONE);
+ b.negateAmountSwitch.setVisibility(View.VISIBLE);
+ b.negateAmountSwitch.setChecked(accRow.isNegateAmount());
+ }
+
+ b.templateAccountNameSourceLabel.setOnClickListener(
+ v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
+ b.templateDetailsAccountNameSource.setOnClickListener(
+ v -> selectAccountRowDetailSource(v, AccDetail.ACCOUNT));
+ b.templateAccountCommentSourceLabel.setOnClickListener(
+ v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
+ b.templateDetailsAccountCommentSource.setOnClickListener(
+ v -> selectAccountRowDetailSource(v, AccDetail.COMMENT));
+ b.templateAccountAmountSourceLabel.setOnClickListener(
+ v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
+ b.templateDetailsAccountAmountSource.setOnClickListener(
+ v -> selectAccountRowDetailSource(v, AccDetail.AMOUNT));
+ }
+ private @NotNull TemplateDetailsItem.AccountRow getItem() {
+ return differ.getCurrentList()
+ .get(getAdapterPosition())
+ .asAccountRowItem();
+ }
+ private void selectAccountRowDetailSource(View v, AccDetail detail) {
+ TemplateDetailsItem.AccountRow accRow = getItem();
+ final TemplateDetailsItem.Header header = getHeader();
+ Logger.debug(D_TEMPLATE_UI, "header is " + header);
+ TemplateDetailSourceSelectorFragment sel =
+ TemplateDetailSourceSelectorFragment.newInstance(1, header.getPattern(),
+ header.getTestText());
+ sel.setOnSourceSelectedListener((literal, group) -> {
+ if (literal) {
+ switch (detail) {
+ case ACCOUNT:
+ accRow.switchToLiteralAccountName();
+ break;
+ case COMMENT:
+ accRow.switchToLiteralAccountComment();
+ break;
+ case AMOUNT:
+ accRow.switchToLiteralAmount();
+ break;
+ default:
+ throw new IllegalStateException("Unexpected detail " + detail);
+ }
+ }
+ else {
+ switch (detail) {
+ case ACCOUNT:
+ accRow.setAccountNameMatchGroup(group);
+ break;
+ case COMMENT:
+ accRow.setAccountCommentMatchGroup(group);
+ break;
+ case AMOUNT:
+ accRow.setAmountMatchGroup(group);
+ break;
+ default:
+ throw new IllegalStateException("Unexpected detail " + detail);
+ }
+ }
+
+ notifyItemChanged(getAdapterPosition());
+ });
+ final AppCompatActivity activity = (AppCompatActivity) v.getContext();
+ sel.show(activity.getSupportFragmentManager(), "template-details-source-selector");
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui.templates;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.lifecycle.ViewModelStoreOwner;
+import androidx.navigation.NavController;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.google.android.material.snackbar.Snackbar;
+
+import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.databinding.TemplateDetailsFragmentBinding;
+import net.ktnx.mobileledger.ui.QRScanCapableFragment;
+import net.ktnx.mobileledger.utils.Logger;
+
+public class TemplateDetailsFragment extends QRScanCapableFragment {
+ static final String ARG_TEMPLATE_ID = "pattern-id";
+ private static final String ARG_COLUMN_COUNT = "column-count";
+ TemplateDetailsFragmentBinding b;
+ private TemplateDetailsViewModel mViewModel;
+ private int mColumnCount = 1;
+ private Long mPatternId;
+ public TemplateDetailsFragment() {
+ }
+ public static TemplateDetailsFragment newInstance(int columnCount, int patternId) {
+ final TemplateDetailsFragment fragment = new TemplateDetailsFragment();
+ Bundle args = new Bundle();
+ args.putInt(ARG_COLUMN_COUNT, columnCount);
+ if (patternId > 0)
+ args.putInt(ARG_TEMPLATE_ID, patternId);
+ fragment.setArguments(args);
+ return fragment;
+ }
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Bundle args = getArguments();
+ if (args != null) {
+ mColumnCount = args.getInt(ARG_COLUMN_COUNT, 1);
+ mPatternId = args.getLong(ARG_TEMPLATE_ID, -1);
+ if (mPatternId == -1)
+ mPatternId = null;
+ }
+ }
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ NavController controller = ((TemplatesActivity) requireActivity()).getNavController();
+ final ViewModelStoreOwner viewModelStoreOwner =
+ controller.getViewModelStoreOwner(R.id.template_list_navigation);
+ mViewModel = new ViewModelProvider(viewModelStoreOwner).get(TemplateDetailsViewModel.class);
+ mViewModel.setDefaultPatternName(getString(R.string.unnamed_pattern));
+ Logger.debug("flow", "PatternDetailsFragment.onCreateView(): model=" + mViewModel);
+
+ b = TemplateDetailsFragmentBinding.inflate(inflater);
+ Context context = b.patternDetailsRecyclerView.getContext();
+ if (mColumnCount <= 1) {
+ b.patternDetailsRecyclerView.setLayoutManager(new LinearLayoutManager(context));
+ }
+ else {
+ b.patternDetailsRecyclerView.setLayoutManager(
+ new GridLayoutManager(context, mColumnCount));
+ }
+
+
+ TemplateDetailsAdapter adapter = new TemplateDetailsAdapter();
+ b.patternDetailsRecyclerView.setAdapter(adapter);
+ mViewModel.getItems(mPatternId)
+ .observe(getViewLifecycleOwner(), adapter::setItems);
+
+ return b.getRoot();
+ }
+ @Override
+ protected void onQrScanned(String text) {
+ Logger.debug("PatDet_fr", String.format("Got scanned text '%s'", text));
+ if (text != null)
+ mViewModel.setTestText(text);
+ }
+ public void onSavePattern() {
+ mViewModel.onSaveTemplate();
+ final Snackbar snackbar = Snackbar.make(b.getRoot(),
+ "One Save pattern action coming up soon in a fragment near you",
+ Snackbar.LENGTH_INDEFINITE);
+// snackbar.setAction("Action", v -> snackbar.dismiss());
+ snackbar.show();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui.templates;
+
+import android.os.AsyncTask;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Observer;
+import androidx.lifecycle.ViewModel;
+
+import net.ktnx.mobileledger.dao.PatternAccountDAO;
+import net.ktnx.mobileledger.dao.PatternHeaderDAO;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.PatternWithAccounts;
+import net.ktnx.mobileledger.db.TemplateAccount;
+import net.ktnx.mobileledger.db.TemplateHeader;
+import net.ktnx.mobileledger.model.TemplateDetailsItem;
+import net.ktnx.mobileledger.utils.Logger;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+
+public class TemplateDetailsViewModel extends ViewModel {
+ private final MutableLiveData<List<TemplateDetailsItem>> items =
+ new MutableLiveData<>(Collections.emptyList());
+ private Long mPatternId;
+ private String mDefaultPatternName;
+ public String getDefaultPatternName() {
+ return mDefaultPatternName;
+ }
+ public void setDefaultPatternName(String name) {
+ mDefaultPatternName = name;
+ }
+
+ public void resetItems() {
+ ArrayList<TemplateDetailsItem> newList = new ArrayList<>();
+ final TemplateDetailsItem.Header header = TemplateDetailsItem.createHeader();
+ header.setName(mDefaultPatternName);
+ header.setId(0);
+ newList.add(header);
+
+ while (newList.size() < 3) {
+ final TemplateDetailsItem.AccountRow aRow = TemplateDetailsItem.createAccountRow();
+ aRow.setId(newList.size() + 1);
+ newList.add(aRow);
+ }
+
+ items.setValue(newList);
+ }
+ private void checkItemConsistency() {
+ ArrayList<TemplateDetailsItem> newList = new ArrayList<>(items.getValue());
+ boolean changes = false;
+ if (newList.size() < 1) {
+ final TemplateDetailsItem.Header header = TemplateDetailsItem.createHeader();
+ header.setName(mDefaultPatternName);
+ newList.add(header);
+ changes = true;
+ }
+
+ while (newList.size() < 3) {
+ newList.add(TemplateDetailsItem.createAccountRow());
+ changes = true;
+ }
+
+ if (changes)
+ items.setValue(newList);
+ }
+ public LiveData<List<TemplateDetailsItem>> getItems(Long patternId) {
+ if (patternId != null && patternId <= 0)
+ throw new IllegalArgumentException("Pattern ID " + patternId + " is invalid");
+
+ mPatternId = patternId;
+
+ if (mPatternId == null) {
+ resetItems();
+ return items;
+ }
+
+ DB db = DB.get();
+ LiveData<PatternWithAccounts> dbList = db.getPatternDAO()
+ .getPatternWithAccounts(mPatternId);
+ Observer<PatternWithAccounts> observer = new Observer<PatternWithAccounts>() {
+ @Override
+ public void onChanged(PatternWithAccounts src) {
+ ArrayList<TemplateDetailsItem> l = new ArrayList<>();
+
+ TemplateDetailsItem header = TemplateDetailsItem.fromRoomObject(src.header);
+ l.add(header);
+ for (TemplateAccount acc : src.accounts) {
+ l.add(TemplateDetailsItem.fromRoomObject(acc));
+ }
+
+ for (TemplateDetailsItem i : l) {
+ Logger.debug("patterns-db", "Loaded pattern item " + i);
+ }
+ items.postValue(l);
+
+ dbList.removeObserver(this);
+ }
+ };
+ dbList.observeForever(observer);
+
+ return items;
+ }
+ public void setTestText(String text) {
+ List<TemplateDetailsItem> list = new ArrayList<>(items.getValue());
+ TemplateDetailsItem.Header header = new TemplateDetailsItem.Header(list.get(0)
+ .asHeaderItem());
+ header.setTestText(text);
+ list.set(0, header);
+
+ items.setValue(list);
+ }
+ public void onSaveTemplate() {
+ Logger.debug("flow", "PatternDetailsViewModel.onSavePattern(); model=" + this);
+ final List<TemplateDetailsItem> list = Objects.requireNonNull(items.getValue());
+
+ AsyncTask.execute(() -> {
+ boolean newPattern = mPatternId == null || mPatternId <= 0;
+
+ TemplateDetailsItem.Header modelHeader = list.get(0)
+ .asHeaderItem();
+ PatternHeaderDAO headerDAO = DB.get()
+ .getPatternDAO();
+ TemplateHeader dbHeader = modelHeader.toDBO();
+ if (newPattern) {
+ dbHeader.setId(null);
+ dbHeader.setId(mPatternId = headerDAO.insert(dbHeader));
+ }
+ else
+ headerDAO.update(dbHeader);
+
+ Logger.debug("pattern-db",
+ String.format(Locale.US, "Stored pattern header %d, item=%s", dbHeader.getId(),
+ modelHeader));
+
+
+ PatternAccountDAO paDAO = DB.get()
+ .getPatternAccountDAO();
+ for (int i = 1; i < list.size(); i++) {
+ final TemplateDetailsItem.AccountRow accRowItem = list.get(i)
+ .asAccountRowItem();
+ TemplateAccount dbAccount = accRowItem.toDBO(dbHeader.getId());
+ dbAccount.setPatternId(mPatternId);
+ dbAccount.setPosition(i);
+ if (newPattern) {
+ dbAccount.setId(null);
+ dbAccount.setId(paDAO.insert(dbAccount));
+ }
+ else
+ paDAO.update(dbAccount);
+
+ Logger.debug("pattern-db", String.format(Locale.US,
+ "Stored pattern account %d, account=%s, comment=%s, neg=%s, item=%s",
+ dbAccount.getId(), dbAccount.getAccountName(),
+ dbAccount.getAccountComment(), dbAccount.getNegateAmount(), accRowItem));
+ }
+ });
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui.templates;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LiveData;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.ktnx.mobileledger.dao.PatternHeaderDAO;
+import net.ktnx.mobileledger.databinding.FragmentTemplateListBinding;
+import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.TemplateHeader;
+import net.ktnx.mobileledger.utils.Logger;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Use the {@link TemplateListFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class TemplateListFragment extends Fragment {
+ private FragmentTemplateListBinding b;
+ private OnTemplateListFragmentInteractionListener mListener;
+
+ public TemplateListFragment() {
+ // Required empty public constructor
+ }
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ *
+ * @return A new instance of fragment TemplateListFragment.
+ */
+ // TODO: Rename and change types and number of parameters
+ public static TemplateListFragment newInstance() {
+ TemplateListFragment fragment = new TemplateListFragment();
+ Bundle args = new Bundle();
+ fragment.setArguments(args);
+ return fragment;
+ }
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+// if (getArguments() != null) {
+// mParam1 = getArguments().getString(ARG_PARAM1);
+// mParam2 = getArguments().getString(ARG_PARAM2);
+// }
+ }
+
+ @Override
+ public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ Logger.debug("flow", "PatternListFragment.onCreateView()");
+ b = FragmentTemplateListBinding.inflate(inflater);
+
+ TemplatesRecyclerViewAdapter modelAdapter = new TemplatesRecyclerViewAdapter();
+
+ b.templateList.setAdapter(modelAdapter);
+ PatternHeaderDAO pDao = DB.get()
+ .getPatternDAO();
+ LiveData<List<TemplateHeader>> templates = pDao.getPatterns();
+ templates.observe(getViewLifecycleOwner(), modelAdapter::setTemplates);
+ LinearLayoutManager llm = new LinearLayoutManager(getContext());
+ llm.setOrientation(RecyclerView.VERTICAL);
+ b.templateList.setLayoutManager(llm);
+ return b.getRoot();
+ }
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ if (context instanceof OnTemplateListFragmentInteractionListener) {
+ mListener = (OnTemplateListFragmentInteractionListener) context;
+ }
+ else {
+ throw new RuntimeException(
+ context.toString() + " must implement OnFragmentInteractionListener");
+ }
+
+ final LifecycleEventObserver observer = new LifecycleEventObserver() {
+ @Override
+ public void onStateChanged(@NonNull LifecycleOwner source,
+ @NonNull Lifecycle.Event event) {
+ if (event.getTargetState() == Lifecycle.State.CREATED) {
+// getActivity().setActionBar(b.toolbar);
+ getLifecycle().removeObserver(this);
+ }
+ }
+ };
+ getLifecycle().addObserver(observer);
+ }
+ /**
+ * This interface must be implemented by activities that contain this
+ * fragment to allow an interaction in this fragment to be communicated
+ * to the activity and potentially other fragments contained in that
+ * activity.
+ * <p>
+ * See the Android Training lesson <a href=
+ * "http://developer.android.com/training/basics/fragments/communicating.html"
+ * >Communicating with Other Fragments</a> for more information.
+ */
+ public interface OnTemplateListFragmentInteractionListener {
+ void onSaveTemplate();
+
+ void onEditTemplate(Long id);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui.templates;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.ktnx.mobileledger.databinding.TemplateListTemplateItemBinding;
+import net.ktnx.mobileledger.db.TemplateHeader;
+
+class TemplateViewHolder extends RecyclerView.ViewHolder {
+ final TemplateListTemplateItemBinding b;
+ public TemplateViewHolder(@NonNull TemplateListTemplateItemBinding binding) {
+ super(binding.getRoot());
+ b = binding;
+ }
+ public void bindToItem(TemplateHeader item) {
+ b.templateName.setText(item.getName());
+ b.editButton.setOnClickListener(v -> {
+ ((TemplatesActivity) v.getContext()).onEditTemplate(item.getId());
+ });
+ }
+}
* along with MoLe. If not, see <https://www.gnu.org/licenses/>.
*/
-package net.ktnx.mobileledger.ui.patterns;
+package net.ktnx.mobileledger.ui.templates;
import android.os.Bundle;
import android.view.Menu;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.navigation.NavController;
+import androidx.navigation.NavDestination;
import androidx.navigation.fragment.NavHostFragment;
import net.ktnx.mobileledger.R;
-import net.ktnx.mobileledger.databinding.ActivityPatternsBinding;
+import net.ktnx.mobileledger.databinding.ActivityTemplatesBinding;
import net.ktnx.mobileledger.ui.activity.CrashReportingActivity;
import net.ktnx.mobileledger.utils.Logger;
import java.util.Objects;
-public class PatternsActivity extends CrashReportingActivity
- implements PatternListFragment.OnPatternListFragmentInteractionListener {
- public static final String ARG_ADD_PATTERN = "add-pattern";
- private ActivityPatternsBinding b;
+public class TemplatesActivity extends CrashReportingActivity
+ implements TemplateListFragment.OnTemplateListFragmentInteractionListener {
+ public static final String ARG_ADD_TEMPLATE = "add-template";
+ private ActivityTemplatesBinding b;
private NavController navController;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.pattern_list_menu, menu);
+ getMenuInflater().inflate(R.menu.template_list_menu, menu);
return true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- b = ActivityPatternsBinding.inflate(getLayoutInflater());
+ b = ActivityTemplatesBinding.inflate(getLayoutInflater());
setContentView(b.getRoot());
setSupportActionBar(b.toolbar);
// Show the Up button in the action bar.
navController = navHostFragment.getNavController();
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
- if (destination.getId() == R.id.patternListFragment) {
+ if (destination.getId() == R.id.templateListFragment) {
b.fabAdd.show();
b.fabSave.hide();
- b.toolbarLayout.setTitle(getString(R.string.title_activity_patterns));
+ b.toolbarLayout.setTitle(getString(R.string.title_activity_templates));
}
- if (destination.getId() == R.id.patternDetailsFragment) {
+ if (destination.getId() == R.id.templateDetailsFragment) {
b.fabAdd.hide();
b.fabSave.show();
}
});
- b.toolbarLayout.setTitle(getString(R.string.title_activity_patterns));
+ b.toolbarLayout.setTitle(getString(R.string.title_activity_templates));
- b.fabAdd.setOnClickListener(v -> onEditPattern(null));
- b.fabSave.setOnClickListener(v -> onSavePattern());
+ b.fabAdd.setOnClickListener(v -> onEditTemplate(null));
+ b.fabSave.setOnClickListener(v -> onSaveTemplate());
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
final NavDestination currentDestination = navController.getCurrentDestination();
if (currentDestination != null &&
- currentDestination.getId() == R.id.patternDetailsFragment)
+ currentDestination.getId() == R.id.templateDetailsFragment)
navController.popBackStack();
else
finish();
}
@Override
- public void onEditPattern(Long id) {
+ public void onEditTemplate(Long id) {
if (id == null) {
- navController.navigate(R.id.action_patternListFragment_to_patternDetailsFragment);
- b.toolbarLayout.setTitle(getString(R.string.title_new_pattern));
+ navController.navigate(R.id.action_templateListFragment_to_templateDetailsFragment);
+ b.toolbarLayout.setTitle(getString(R.string.title_new_template));
}
else {
Bundle bundle = new Bundle();
- bundle.putLong(PatternDetailsFragment.ARG_PATTERN_ID, id);
- navController.navigate(R.id.action_patternListFragment_to_patternDetailsFragment,
+ bundle.putLong(TemplateDetailsFragment.ARG_TEMPLATE_ID, id);
+ navController.navigate(R.id.action_templateListFragment_to_templateDetailsFragment,
bundle);
- b.toolbarLayout.setTitle(getString(R.string.title_edit_pattern));
+ b.toolbarLayout.setTitle(getString(R.string.title_edit_template));
}
}
@Override
- public void onSavePattern() {
+ public void onSaveTemplate() {
final ViewModelStoreOwner viewModelStoreOwner =
- navController.getViewModelStoreOwner(R.id.pattern_list_navigation);
- PatternDetailsViewModel model =
- new ViewModelProvider(viewModelStoreOwner).get(PatternDetailsViewModel.class);
- Logger.debug("flow", "PatternsActivity.onSavePattern(): model=" + model);
- model.onSavePattern();
+ navController.getViewModelStoreOwner(R.id.template_list_navigation);
+ TemplateDetailsViewModel model =
+ new ViewModelProvider(viewModelStoreOwner).get(TemplateDetailsViewModel.class);
+ Logger.debug("flow", "TemplatesActivity.onSavePattern(): model=" + model);
+ model.onSaveTemplate();
navController.navigateUp();
}
public NavController getNavController() {
--- /dev/null
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui.templates;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.AsyncListDiffer;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.ktnx.mobileledger.databinding.TemplateListTemplateItemBinding;
+import net.ktnx.mobileledger.db.TemplateHeader;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class TemplatesRecyclerViewAdapter extends RecyclerView.Adapter<TemplateViewHolder> {
+ private final AsyncListDiffer<TemplateHeader> listDiffer;
+ public TemplatesRecyclerViewAdapter() {
+ listDiffer = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback<TemplateHeader>() {
+ @Override
+ public boolean areItemsTheSame(@NotNull TemplateHeader oldItem,
+ @NotNull TemplateHeader newItem) {
+ return oldItem.getId()
+ .equals(newItem.getId());
+ }
+ @Override
+ public boolean areContentsTheSame(@NotNull TemplateHeader oldItem,
+ @NotNull TemplateHeader newItem) {
+ return oldItem.equals(newItem);
+ }
+ });
+ }
+ @NonNull
+ @Override
+ public TemplateViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ TemplateListTemplateItemBinding b =
+ TemplateListTemplateItemBinding.inflate(LayoutInflater.from(parent.getContext()),
+ parent, false);
+
+ return new TemplateViewHolder(b);
+ }
+ @Override
+ public void onBindViewHolder(@NonNull TemplateViewHolder holder, int position) {
+ holder.bindToItem(listDiffer.getCurrentList()
+ .get(position));
+ }
+ @Override
+ public int getItemCount() {
+ return listDiffer.getCurrentList()
+ .size();
+ }
+ public void setTemplates(List<TemplateHeader> newList) {
+ listDiffer.submitList(newList);
+ }
+}
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2021 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".ui.patterns.PatternsActivity"
- >
-
- <com.google.android.material.appbar.AppBarLayout
- android:id="@+id/appbar"
- android:layout_width="match_parent"
- android:layout_height="@dimen/app_bar_height"
- android:fitsSystemWindows="true"
- android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
- >
- <com.google.android.material.appbar.CollapsingToolbarLayout
- android:id="@+id/toolbar_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true"
- app:contentScrim="?attr/colorPrimary"
- app:layout_scrollFlags="scroll|exitUntilCollapsed"
- app:toolbarId="@+id/toolbar"
- >
-
- <androidx.appcompat.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
- app:layout_collapseMode="pin"
- app:popupTheme="@style/ThemeOverlay.AppCompat.DayNight"
- />
- </com.google.android.material.appbar.CollapsingToolbarLayout>
- </com.google.android.material.appbar.AppBarLayout>
-
- <androidx.core.widget.NestedScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:layout_behavior="@string/appbar_scrolling_view_behavior"
- >
- <androidx.fragment.app.FragmentContainerView
- android:id="@+id/fragment_container"
- android:name="androidx.navigation.fragment.NavHostFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:defaultNavHost="true"
- app:navGraph="@navigation/pattern_list_navigation"
- />
- </androidx.core.widget.NestedScrollView>
-
- <com.google.android.material.floatingactionbutton.FloatingActionButton
- android:id="@+id/fabAdd"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|end"
- android:layout_margin="@dimen/fab_margin"
- app:srcCompat="@drawable/ic_add_white_24dp"
- android:contentDescription="@string/add_button_description"
- />
-
- <com.google.android.material.floatingactionbutton.FloatingActionButton
- android:id="@+id/fabSave"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|end"
- android:layout_margin="@dimen/fab_margin"
- app:srcCompat="@drawable/ic_save_white_24dp"
- android:contentDescription="@string/save_button_description"
- />
-
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".ui.templates.TemplatesActivity"
+ >
+
+ <com.google.android.material.appbar.AppBarLayout
+ android:id="@+id/appbar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/app_bar_height"
+ android:fitsSystemWindows="true"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ >
+ <com.google.android.material.appbar.CollapsingToolbarLayout
+ android:id="@+id/toolbar_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ app:contentScrim="?attr/colorPrimary"
+ app:layout_scrollFlags="scroll|exitUntilCollapsed"
+ app:toolbarId="@+id/toolbar"
+ >
+
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ app:layout_collapseMode="pin"
+ app:popupTheme="@style/ThemeOverlay.AppCompat.DayNight"
+ />
+ </com.google.android.material.appbar.CollapsingToolbarLayout>
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <androidx.core.widget.NestedScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"
+ >
+ <androidx.fragment.app.FragmentContainerView
+ android:id="@+id/fragment_container"
+ android:name="androidx.navigation.fragment.NavHostFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:defaultNavHost="true"
+ app:navGraph="@navigation/template_list_navigation"
+ />
+ </androidx.core.widget.NestedScrollView>
+
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/fabAdd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end"
+ android:layout_margin="@dimen/fab_margin"
+ app:srcCompat="@drawable/ic_add_white_24dp"
+ android:contentDescription="@string/add_button_description"
+ />
+
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/fabSave"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end"
+ android:layout_margin="@dimen/fab_margin"
+ app:srcCompat="@drawable/ic_save_white_24dp"
+ android:contentDescription="@string/save_button_description"
+ />
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
- tools:context=".ui.patterns.TestItemFragment"
- tools:listitem="@layout/pattern_details_header"
+ tools:context=".ui.templates.TestItemFragment"
+ tools:listitem="@layout/template_details_header"
/>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2019 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:longClickable="false"
- >
-
- <TextView
- android:id="@+id/group_number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="@dimen/text_margin"
- android:gravity="end"
- android:minWidth="20sp"
- android:text="1"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:ignore="HardcodedText"
- />
- <TextView
- android:id="@+id/colon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/text_margin"
- android:text=":"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintStart_toEndOf="@id/group_number"
- app:layout_constraintTop_toTopOf="parent"
- tools:ignore="HardcodedText"
- />
- <TextView
- android:id="@+id/matched_text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_margin="@dimen/text_margin"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@id/colon"
- app:layout_constraintTop_toTopOf="parent"
- />
-</androidx.constraintlayout.widget.ConstraintLayout>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2020 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:animateLayoutChanges="true"
- android:minWidth="60dp"
- android:padding="@dimen/text_margin"
- app:layout_constraintWidth_min="60dp"
- >
-
- <com.google.android.material.textview.MaterialTextView
- android:id="@+id/label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/text_margin"
- android:text="@string/choose_pattern_detail_source_label"
- android:textSize="18sp"
- app:layout_constraintBottom_toTopOf="@id/list"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- />
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/list"
- android:name="net.ktnx.mobileledger.ui.PatternDetailSourceSelectorFragment"
- android:layout_width="0dp"
- android:layout_height="0dp"
- app:layout_constraintWidth_min="50dp"
- app:layout_constraintHeight_min="150dp"
- android:layout_marginLeft="@dimen/activity_horizontal_margin"
- android:layout_marginRight="@dimen/activity_horizontal_margin"
- android:minHeight="100dp"
- app:layoutManager="LinearLayoutManager"
- app:layout_constraintBottom_toTopOf="@id/pattern_error"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/label"
- tools:context="net.ktnx.mobileledger.ui.CurrencySelectorFragment"
- tools:listitem="@layout/fragment_pattern_detail_source_selector"
- />
- <TextView
- android:id="@+id/pattern_error"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintBottom_toTopOf="@id/literal_button"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/list"
- />
- <com.google.android.material.button.MaterialButton
- android:id="@+id/literal_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/text_margin"
- android:text="@string/pattern_details_source_literal"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- />
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2021 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/pattern_list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".ui.patterns.PatternsActivity"
- />
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:longClickable="false"
+ >
+
+ <TextView
+ android:id="@+id/group_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/text_margin"
+ android:gravity="end"
+ android:minWidth="20sp"
+ android:text="1"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:ignore="HardcodedText"
+ />
+ <TextView
+ android:id="@+id/colon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/text_margin"
+ android:text=":"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintStart_toEndOf="@id/group_number"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:ignore="HardcodedText"
+ />
+ <TextView
+ android:id="@+id/matched_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/text_margin"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/colon"
+ app:layout_constraintTop_toTopOf="parent"
+ />
+</androidx.constraintlayout.widget.ConstraintLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:animateLayoutChanges="true"
+ android:minWidth="60dp"
+ android:padding="@dimen/text_margin"
+ app:layout_constraintWidth_min="60dp"
+ >
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/text_margin"
+ android:text="@string/choose_template_detail_source_label"
+ android:textSize="18sp"
+ app:layout_constraintBottom_toTopOf="@id/list"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/list"
+ android:name="net.ktnx.mobileledger.ui.PatternDetailSourceSelectorFragment"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintWidth_min="50dp"
+ app:layout_constraintHeight_min="150dp"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:minHeight="100dp"
+ app:layoutManager="LinearLayoutManager"
+ app:layout_constraintBottom_toTopOf="@id/template_error"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/label"
+ tools:context="net.ktnx.mobileledger.ui.CurrencySelectorFragment"
+ tools:listitem="@layout/fragment_template_detail_source_selector"
+ />
+ <TextView
+ android:id="@+id/template_error"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toTopOf="@id/literal_button"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/list"
+ />
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/literal_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/text_margin"
+ android:text="@string/template_details_source_literal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/template_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".ui.templates.TemplatesActivity"
+ />
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2021 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/pattern_details_item_account_row"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingHorizontal="@dimen/text_margin"
- >
- <TextView
- android:id="@+id/pattern_account_label"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="end"
- android:paddingTop="@dimen/text_margin"
- android:text="@string/pattern_details_account_row_label"
- app:drawableBottomCompat="@drawable/dashed_border_8dp"
- />
- <TextView
- android:id="@+id/pattern_account_name_source_label"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/text_margin"
- android:text="@string/account_name_source_label"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_account_label"
- />
- <TextView
- android:id="@+id/pattern_details_account_name_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minWidth="100dp"
- android:textAppearance="?attr/textAppearanceListItemSecondary"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_account_name_source_label"
- />
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/pattern_details_account_name_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/text_margin"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_details_account_name_source"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/pattern_details_account_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/pattern_details_account_name_label"
- android:inputType="text"
- />
- </com.google.android.material.textfield.TextInputLayout>
-
- <TextView
- android:id="@+id/pattern_account_comment_source_label"
- android:layout_width="0dp"
- android:layout_height="match_parent"
-
- android:paddingTop="@dimen/text_margin"
- android:text="@string/account_comment_source_label"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_details_account_name_layout"
- />
- <TextView
- android:id="@+id/pattern_details_account_comment_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minWidth="100dp"
- android:textAppearance="?attr/textAppearanceListItemSecondary"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_account_comment_source_label"
- />
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/pattern_details_account_comment_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/text_margin"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_details_account_comment_source"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/pattern_details_account_comment"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/pattern_details_account_comment_label"
- android:inputType="text"
- />
- </com.google.android.material.textfield.TextInputLayout>
-
- <TextView
- android:id="@+id/pattern_account_amount_source_label"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/text_margin"
- android:text="@string/account_amount_source_label"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toStartOf="@id/negate_amount_switch"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_details_account_comment_layout"
- />
- <TextView
- android:id="@+id/pattern_details_account_amount_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/textAppearanceListItemSecondary"
- app:layout_constraintEnd_toStartOf="@id/negate_amount_switch"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_account_amount_source_label"
- />
- <com.google.android.material.switchmaterial.SwitchMaterial
- android:id="@+id/negate_amount_switch"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintBottom_toBottomOf="@id/pattern_details_account_amount_source"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="@id/pattern_account_amount_source_label"
- />
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/pattern_details_account_amount_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/text_margin"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_details_account_amount_source"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/pattern_details_account_amount"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/pattern_details_account_amount_label"
- android:inputType="number|numberDecimal|numberSigned"
- />
- </com.google.android.material.textfield.TextInputLayout>
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2021 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/pattern_details_recycler_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".ui.patterns.PatternDetailsFragment"
- >
-</androidx.recyclerview.widget.RecyclerView>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2021 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/pattern_details_item_head"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingHorizontal="@dimen/text_margin"
- >
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/pattern_name_layout"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/pattern_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/pattern_name_label"
- android:inputType="text"
- />
- </com.google.android.material.textfield.TextInputLayout>
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/pattern_layout"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_name_layout"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/pattern"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/pattern_details_pattern_label"
- android:inputType="text"
- />
- </com.google.android.material.textfield.TextInputLayout>
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/test_text_layout"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toStartOf="@id/pattern_details_head_scan_qr_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_layout"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/test_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/pattern_details_test_text_label"
- android:inputType="text"
- />
- </com.google.android.material.textfield.TextInputLayout>
- <ImageButton
- android:id="@+id/pattern_details_head_scan_qr_button"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:background="@android:color/transparent"
- android:contentDescription="@string/scan_qr"
- android:minWidth="@dimen/thumb_row_height"
- app:layout_constraintBottom_toBottomOf="@id/test_text_layout"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="@id/test_text_layout"
- app:srcCompat="@drawable/ic_baseline_qr_code_scanner_24"
- app:tint="?colorPrimary"
- />
- <TextView
- android:id="@+id/transaction_parameters_label"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="end"
- android:paddingTop="@dimen/text_margin"
- android:text="@string/pattern_transaction_parameters_label"
- app:layout_constraintTop_toBottomOf="@id/test_text_layout"
- />
- <TextView
- android:id="@+id/pattern_transaction_date_label"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/pattern_details_date_label"
- app:layout_constraintTop_toBottomOf="@id/transaction_parameters_label"
- />
- <TextView
- android:id="@+id/pattern_details_year_source_label"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:text="@string/pattern_details_date_year_source_label"
- android:textAlignment="center"
- app:layout_constraintEnd_toStartOf="@id/pattern_details_month_source_label"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_transaction_date_label"
- />
- <TextView
- android:id="@+id/pattern_details_month_source_label"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:text="@string/pattern_details_date_month_source_label"
- android:textAlignment="center"
- app:layout_constraintEnd_toStartOf="@id/pattern_details_day_source_label"
- app:layout_constraintStart_toEndOf="@id/pattern_details_year_source_label"
- app:layout_constraintTop_toBottomOf="@id/pattern_transaction_date_label"
- />
- <TextView
- android:id="@+id/pattern_details_day_source_label"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:text="@string/pattern_details_date_day_source_label"
- android:textAlignment="center"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@id/pattern_details_month_source_label"
- app:layout_constraintTop_toBottomOf="@id/pattern_transaction_date_label"
- />
- <TextView
- android:id="@+id/pattern_details_year_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:text="@string/pattern_details_source_literal"
- android:textAlignment="center"
- app:layout_constraintEnd_toStartOf="@id/pattern_details_month_source"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_details_day_source_label"
- />
- <TextView
- android:id="@+id/pattern_details_month_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:text=""
- android:textAlignment="center"
- app:layout_constraintEnd_toStartOf="@id/pattern_details_day_source"
- app:layout_constraintStart_toEndOf="@id/pattern_details_year_source"
- app:layout_constraintTop_toBottomOf="@id/pattern_details_month_source_label"
- />
- <TextView
- android:id="@+id/pattern_details_day_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:text=""
- android:textAlignment="center"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@id/pattern_details_month_source"
- app:layout_constraintTop_toBottomOf="@id/pattern_details_day_source_label"
- />
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/barrier_before_date_inputs"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:barrierDirection="bottom"
- app:constraint_referenced_ids="pattern_details_year_source,pattern_details_month_source,pattern_details_day_source"
- />
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/pattern_details_date_year_layout"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- app:layout_constraintEnd_toEndOf="@id/pattern_details_year_source"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/barrier_before_date_inputs"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/pattern_details_date_year"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:hint="@string/date_year_hint"
- />
- </com.google.android.material.textfield.TextInputLayout>
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/pattern_details_date_month_layout"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- app:layout_constraintEnd_toEndOf="@id/pattern_details_month_source"
- app:layout_constraintStart_toStartOf="@id/pattern_details_month_source"
- app:layout_constraintTop_toBottomOf="@id/barrier_before_date_inputs"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/pattern_details_date_month"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:hint="@string/date_month_hint"
- />
- </com.google.android.material.textfield.TextInputLayout>
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/pattern_details_date_day_layout"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- app:layout_constraintBottom_toTopOf="@id/barrier_before_description"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@id/pattern_details_day_source"
- app:layout_constraintTop_toBottomOf="@id/barrier_before_date_inputs"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/pattern_details_date_day"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:hint="@string/date_day_hint"
- />
- </com.google.android.material.textfield.TextInputLayout>
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/barrier_before_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- app:barrierDirection="bottom"
- app:constraint_referenced_ids="pattern_details_date_day_layout,pattern_details_date_month_layout,pattern_details_date_year_layout"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- />
- <TextView
- android:id="@+id/pattern_transaction_description_source_label"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/text_margin"
- android:text="@string/transaction_description_source_label"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/barrier_before_description"
- />
- <TextView
- android:id="@+id/pattern_transaction_description_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minWidth="100dp"
- android:textAppearance="?attr/textAppearanceListItemSecondary"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_transaction_description_source_label"
- />
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/transaction_description_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/pattern_transaction_description_source"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/transaction_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/pattern_transaction_description_hint"
- />
- </com.google.android.material.textfield.TextInputLayout>
- <TextView
- android:id="@+id/pattern_transaction_comment_source_label"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/text_margin"
- android:text="@string/transaction_comment_source_label"
- android:textAppearance="?attr/textAppearanceListItem"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/transaction_description_layout"
- />
- <TextView
- android:id="@+id/pattern_transaction_comment_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minWidth="100dp"
- android:textAppearance="?attr/textAppearanceListItemSecondary"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pattern_transaction_comment_source_label"
- />
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/transaction_comment_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/pattern_transaction_comment_source"
- >
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/transaction_comment"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/pattern_transaction_comment_hint"
- />
- </com.google.android.material.textfield.TextInputLayout>
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2021 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- >
- <TextView
- android:id="@+id/pattern_name"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/text_margin"
- android:layout_marginEnd="@dimen/text_margin"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@id/edit_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- />
- <ImageButton
- android:id="@+id/edit_button"
- android:layout_width="@dimen/toolbar_height"
- android:layout_height="@dimen/toolbar_height"
- android:backgroundTint="?colorSurface"
- android:contentDescription="@string/edit_button_description"
- android:src="@drawable/ic_mode_edit_black_24dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- />
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/pattern_details_item_account_row"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingHorizontal="@dimen/text_margin"
+ >
+ <TextView
+ android:id="@+id/pattern_account_label"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="end"
+ android:paddingTop="@dimen/text_margin"
+ android:text="@string/pattern_details_account_row_label"
+ app:drawableBottomCompat="@drawable/dashed_border_8dp"
+ />
+ <TextView
+ android:id="@+id/template_account_name_source_label"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/text_margin"
+ android:text="@string/account_name_source_label"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/pattern_account_label"
+ />
+ <TextView
+ android:id="@+id/template_details_account_name_source"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:minWidth="100dp"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_account_name_source_label"
+ />
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/template_details_account_name_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/text_margin"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_details_account_name_source"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/template_details_account_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/pattern_details_account_name_label"
+ android:inputType="text"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <TextView
+ android:id="@+id/template_account_comment_source_label"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+
+ android:paddingTop="@dimen/text_margin"
+ android:text="@string/account_comment_source_label"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_details_account_name_layout"
+ />
+ <TextView
+ android:id="@+id/template_details_account_comment_source"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:minWidth="100dp"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_account_comment_source_label"
+ />
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/template_details_account_comment_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/text_margin"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_details_account_comment_source"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/template_details_account_comment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/pattern_details_account_comment_label"
+ android:inputType="text"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <TextView
+ android:id="@+id/template_account_amount_source_label"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/text_margin"
+ android:text="@string/account_amount_source_label"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toStartOf="@id/negate_amount_switch"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_details_account_comment_layout"
+ />
+ <TextView
+ android:id="@+id/template_details_account_amount_source"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ app:layout_constraintEnd_toStartOf="@id/negate_amount_switch"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_account_amount_source_label"
+ />
+ <com.google.android.material.switchmaterial.SwitchMaterial
+ android:id="@+id/negate_amount_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="@id/template_details_account_amount_source"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/template_account_amount_source_label"
+ />
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/template_details_account_amount_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/text_margin"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_details_account_amount_source"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/template_details_account_amount"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/pattern_details_account_amount_label"
+ android:inputType="number|numberDecimal|numberSigned"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/pattern_details_recycler_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".ui.templates.TemplateDetailsFragment"
+ >
+</androidx.recyclerview.widget.RecyclerView>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/pattern_details_item_head"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingHorizontal="@dimen/text_margin"
+ >
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/pattern_name_layout"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/template_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/pattern_name_label"
+ android:inputType="text"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/pattern_layout"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/pattern_name_layout"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/pattern"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/pattern_details_pattern_label"
+ android:inputType="text"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/test_text_layout"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toStartOf="@id/template_details_head_scan_qr_button"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/pattern_layout"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/test_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/pattern_details_test_text_label"
+ android:inputType="text"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+ <ImageButton
+ android:id="@+id/template_details_head_scan_qr_button"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:background="@android:color/transparent"
+ android:contentDescription="@string/scan_qr"
+ android:minWidth="@dimen/thumb_row_height"
+ app:layout_constraintBottom_toBottomOf="@id/test_text_layout"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/test_text_layout"
+ app:srcCompat="@drawable/ic_baseline_qr_code_scanner_24"
+ app:tint="?colorPrimary"
+ />
+ <TextView
+ android:id="@+id/transaction_parameters_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="end"
+ android:paddingTop="@dimen/text_margin"
+ android:text="@string/pattern_transaction_parameters_label"
+ app:layout_constraintTop_toBottomOf="@id/test_text_layout"
+ />
+ <TextView
+ android:id="@+id/pattern_transaction_date_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/pattern_details_date_label"
+ app:layout_constraintTop_toBottomOf="@id/transaction_parameters_label"
+ />
+ <TextView
+ android:id="@+id/template_details_year_source_label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/pattern_details_date_year_source_label"
+ android:textAlignment="center"
+ app:layout_constraintEnd_toStartOf="@id/template_details_month_source_label"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/pattern_transaction_date_label"
+ />
+ <TextView
+ android:id="@+id/template_details_month_source_label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/pattern_details_date_month_source_label"
+ android:textAlignment="center"
+ app:layout_constraintEnd_toStartOf="@id/template_details_day_source_label"
+ app:layout_constraintStart_toEndOf="@id/template_details_year_source_label"
+ app:layout_constraintTop_toBottomOf="@id/pattern_transaction_date_label"
+ />
+ <TextView
+ android:id="@+id/template_details_day_source_label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/pattern_details_date_day_source_label"
+ android:textAlignment="center"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/template_details_month_source_label"
+ app:layout_constraintTop_toBottomOf="@id/pattern_transaction_date_label"
+ />
+ <TextView
+ android:id="@+id/template_details_year_source"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/template_details_source_literal"
+ android:textAlignment="center"
+ app:layout_constraintEnd_toStartOf="@id/template_details_month_source"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_details_day_source_label"
+ />
+ <TextView
+ android:id="@+id/template_details_month_source"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAlignment="center"
+ app:layout_constraintEnd_toStartOf="@id/template_details_day_source"
+ app:layout_constraintStart_toEndOf="@id/template_details_year_source"
+ app:layout_constraintTop_toBottomOf="@id/template_details_month_source_label"
+ />
+ <TextView
+ android:id="@+id/template_details_day_source"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAlignment="center"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/template_details_month_source"
+ app:layout_constraintTop_toBottomOf="@id/template_details_day_source_label"
+ />
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/barrier_before_date_inputs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:barrierDirection="bottom"
+ app:constraint_referenced_ids="template_details_year_source,template_details_month_source,template_details_day_source"
+ />
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/template_details_date_year_layout"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintEnd_toEndOf="@id/template_details_year_source"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/barrier_before_date_inputs"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/template_details_date_year"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:hint="@string/date_year_hint"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/template_details_date_month_layout"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintEnd_toEndOf="@id/template_details_month_source"
+ app:layout_constraintStart_toStartOf="@id/template_details_month_source"
+ app:layout_constraintTop_toBottomOf="@id/barrier_before_date_inputs"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/template_details_date_month"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:hint="@string/date_month_hint"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/template_details_date_day_layout"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toTopOf="@id/barrier_before_description"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="@id/template_details_day_source"
+ app:layout_constraintTop_toBottomOf="@id/barrier_before_date_inputs"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/template_details_date_day"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:hint="@string/date_day_hint"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/barrier_before_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:barrierDirection="bottom"
+ app:constraint_referenced_ids="template_details_date_day_layout,template_details_date_month_layout,template_details_date_year_layout"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ />
+ <TextView
+ android:id="@+id/template_transaction_description_source_label"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/text_margin"
+ android:text="@string/transaction_description_source_label"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/barrier_before_description"
+ />
+ <TextView
+ android:id="@+id/template_transaction_description_source"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:minWidth="100dp"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_transaction_description_source_label"
+ />
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/transaction_description_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/template_transaction_description_source"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/transaction_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/template_transaction_description_hint"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+ <TextView
+ android:id="@+id/template_transaction_comment_source_label"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/text_margin"
+ android:text="@string/transaction_comment_source_label"
+ android:textAppearance="?attr/textAppearanceListItem"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/transaction_description_layout"
+ />
+ <TextView
+ android:id="@+id/template_transaction_comment_source"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:minWidth="100dp"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/template_transaction_comment_source_label"
+ />
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/transaction_comment_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/template_transaction_comment_source"
+ >
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/transaction_comment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/template_transaction_comment_hint"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <TextView
+ android:id="@+id/template_name"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/text_margin"
+ android:layout_marginEnd="@dimen/text_margin"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/edit_button"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ />
+ <ImageButton
+ android:id="@+id/edit_button"
+ android:layout_width="@dimen/toolbar_height"
+ android:layout_height="@dimen/toolbar_height"
+ android:backgroundTint="?colorSurface"
+ android:contentDescription="@string/edit_button_description"
+ android:src="@drawable/ic_mode_edit_black_24dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2021 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- >
-
- <item
- android:icon="@drawable/ic_baseline_help_24_white"
- android:title="@string/help_menu_item_title"
- app:showAsAction="ifRoom"
- />
-</menu>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ >
+
+ <item
+ android:icon="@drawable/ic_baseline_help_24_white"
+ android:title="@string/help_menu_item_title"
+ app:showAsAction="ifRoom"
+ />
+</menu>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2021 Damyan Ivanov.
- ~ This file is part of MoLe.
- ~ MoLe is free software: you can distribute it and/or modify it
- ~ under the term of the GNU General Public License as published by
- ~ the Free Software Foundation, either version 3 of the License, or
- ~ (at your opinion), any later version.
- ~
- ~ MoLe is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
- ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ~ GNU General Public License terms for details.
- ~
- ~ You should have received a copy of the GNU General Public License
- ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- -->
-
-<navigation xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/pattern_list_navigation"
- app:startDestination="@id/patternListFragment"
- >
-
- <fragment
- android:id="@+id/patternListFragment"
- android:name="net.ktnx.mobileledger.ui.patterns.PatternListFragment"
- android:label="PatternListFragment"
- android:tag="patternListFragment"
- >
- <action
- android:id="@+id/action_patternListFragment_to_patternDetailsFragment"
- app:destination="@id/patternDetailsFragment"
- app:enterAnim="@anim/slide_in_left"
- app:exitAnim="@anim/slide_out_left"
- />
- </fragment>
- <fragment
- android:id="@+id/patternDetailsFragment"
- android:name="net.ktnx.mobileledger.ui.patterns.PatternDetailsFragment"
- android:label="pattern_details_fragment"
- android:tag="patternDetailsFragment"
- tools:layout="@layout/pattern_details_fragment"
- >
- <action
- android:id="@+id/action_patternDetailsFragment_to_patternListFragment"
- app:destination="@id/patternListFragment"
- />
- </fragment>
-</navigation>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2021 Damyan Ivanov.
+ ~ This file is part of MoLe.
+ ~ MoLe is free software: you can distribute it and/or modify it
+ ~ under the term of the GNU General Public License as published by
+ ~ the Free Software Foundation, either version 3 of the License, or
+ ~ (at your opinion), any later version.
+ ~
+ ~ MoLe is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ~ GNU General Public License terms for details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ -->
+
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/template_list_navigation"
+ app:startDestination="@id/templateListFragment"
+ >
+
+ <fragment
+ android:id="@+id/templateListFragment"
+ android:name="net.ktnx.mobileledger.ui.templates.TemplateListFragment"
+ android:label="TemplateListFragment"
+ android:tag="templateListFragment"
+ >
+ <action
+ android:id="@+id/action_templateListFragment_to_templateDetailsFragment"
+ app:destination="@id/templateDetailsFragment"
+ app:enterAnim="@anim/slide_in_left"
+ app:exitAnim="@anim/slide_out_left"
+ />
+ </fragment>
+ <fragment
+ android:id="@+id/templateDetailsFragment"
+ android:name="net.ktnx.mobileledger.ui.templates.TemplateDetailsFragment"
+ android:label="pattern_details_fragment"
+ android:tag="patternDetailsFragment"
+ tools:layout="@layout/template_details_fragment"
+ >
+ <action
+ android:id="@+id/action_templateDetailsFragment_to_templateListFragment"
+ app:destination="@id/templateListFragment"
+ />
+ </fragment>
+</navigation>
\ No newline at end of file
<string name="err_json_send_error_unsupported">Възможно е програмния интерфейс на сървъра да не се поддържа от MoLe</string>
<string name="scan_qr">Сканиране на QR код</string>
<string name="nav_patterns">Макети</string>
- <string name="title_activity_patterns">Макети</string>
+ <string name="title_activity_templates">Макети</string>
<string name="pattern_regex_hint">Шаблон (regex)</string>
<string name="help_menu_item_title">Помощ</string>
<string name="edit_button_description">Бутон за промяна</string>
<string name="pattern_details_account_comment_label">Бележка към сметката</string>
<string name="pattern_details_account_amount_label">Сума</string>
- <string name="choose_pattern_detail_source_label">Прихващане от шаблона</string>
+ <string name="choose_template_detail_source_label">Прихващане от шаблона</string>
<string name="missing_pattern_error">Липсва шаблон</string>
<string name="missing_test_text">Липсва примерен текст</string>
<string name="pattern_without_groups">Шаблонът няма прихващания</string>
<string name="pattern_does_not_match">Шаблонът не съвпада с примерния текст</string>
<string name="pattern_transaction_parameters_label">Данни за движението</string>
- <string name="pattern_transaction_description_hint">Описание на движението</string>
- <string name="pattern_transaction_comment_hint">Бележка към движението</string>
+ <string name="template_transaction_description_hint">Описание на движението</string>
+ <string name="template_transaction_comment_hint">Бележка към движението</string>
<string name="transaction_description_source_label">Източник на описанието на движението</string>
<string name="transaction_comment_source_label">Източник на бележката към движението</string>
<string name="pattern_details_date_label">Дата на движението</string>
<string name="choose_pattern_to_apply">Избор на макет</string>
<string name="pattern_name">Име на макет</string>
<string name="implementation_pending">Функцията още не е готова</string>
- <string name="title_edit_pattern">Промяна на макет</string>
- <string name="title_new_pattern">Създаване на макет</string>
+ <string name="title_edit_template">Промяна на макет</string>
+ <string name="title_new_template">Създаване на макет</string>
<string name="pattern_has_errors">Шаблонът съдържа грешки</string>
<string name="account_name_is_empty">Липсва сметка</string>
<string name="pattern_is_empty">Липсва шаблон</string>
<string name="pattern_details_account_name_label">Сметка</string>
<string name="pattern_details_account_row_label">Данни за сметката</string>
<string name="account_name_source_label">Източник на името на сметката</string>
- <string name="pattern_details_source_literal">ръчно въвеждане</string>
+ <string name="template_details_source_literal">ръчно въвеждане</string>
<string name="account_comment_source_label">Източник на бележка към сметката</string>
<string name="account_amount_source_label">Източник на името на сметката</string>
</resources>
<string name="err_json_send_error_unsupported">Perhaps the API of the backend server is not supported by MoLe</string>
<string name="scan_qr">Scan QR code</string>
<string name="nav_patterns">Patterns</string>
- <string name="title_activity_patterns">Patterns</string>
+ <string name="title_activity_templates">Patterns</string>
<string name="pattern_regex_hint">Pattern (regular expression)</string>
<string name="help_menu_item_title">Help</string>
<string name="edit_button_description">Edit button</string>
<string name="pattern_details_account_name_label">Account name</string>
<string name="pattern_details_account_row_label">Transaction account details</string>
<string name="account_name_source_label">Account name source</string>
- <string name="pattern_details_source_literal">literal</string>
+ <string name="template_details_source_literal">literal</string>
<string name="account_comment_source_label">Account comment source</string>
<string name="account_amount_source_label">Amount source</string>
<string name="pattern_details_account_comment_label">Account comment</string>
<string name="pattern_details_account_amount_label">Amount</string>
- <string name="choose_pattern_detail_source_label">Pattern match group</string>
+ <string name="choose_template_detail_source_label">Pattern match group</string>
<string name="missing_pattern_error">Missing pattern</string>
<string name="missing_test_text">Missing test text</string>
<string name="pattern_without_groups">Pattern has no capturing groups</string>
<string name="pattern_does_not_match">Pattern doesn\'t match the test text</string>
<string name="pattern_transaction_parameters_label">Transaction parameters</string>
- <string name="pattern_transaction_description_hint">Transaction description</string>
- <string name="pattern_transaction_comment_hint">Transaction comment</string>
+ <string name="template_transaction_description_hint">Transaction description</string>
+ <string name="template_transaction_comment_hint">Transaction comment</string>
<string name="transaction_description_source_label">Transaction description source</string>
<string name="transaction_comment_source_label">Transaction comment source</string>
<string name="pattern_details_date_label">Transaction date</string>
<string name="choose_pattern_to_apply">Choose pattern to apply</string>
<string name="pattern_name">Pattern name</string>
<string name="implementation_pending">Not implemented yet</string>
- <string name="title_edit_pattern">Edit pattern</string>
- <string name="title_new_pattern">New pattern</string>
+ <string name="title_edit_template">Edit pattern</string>
+ <string name="title_new_template">New pattern</string>
</resources>