From: Damyan Ivanov Date: Tue, 2 Feb 2021 06:35:48 +0000 (+0200) Subject: rename patterns to templates X-Git-Tag: v0.17.0~171 X-Git-Url: https://git.ktnx.net/?p=mobile-ledger.git;a=commitdiff_plain;h=d95839304defead7c7d605cab2e612f1227cbfed rename patterns to templates "pattern" is the regular expression that is used to match templates --- diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 21a7e5ad..81183ea6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,8 +32,8 @@ android:supportsRtl="true" tools:ignore="GoogleAppIndexingWarning"> > getPatternAccounts(Long pattern_id); + LiveData> getPatternAccounts(Long pattern_id); @Query("SELECT * FROM pattern_accounts WHERE id = :id") - LiveData getPatternAccountById(Long id); + LiveData getPatternAccountById(Long id); // not useful for now // @Transaction diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/PatternHeaderDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/PatternHeaderDAO.java index c3f0535a..b4713d32 100644 --- a/app/src/main/java/net/ktnx/mobileledger/dao/PatternHeaderDAO.java +++ b/app/src/main/java/net/ktnx/mobileledger/dao/PatternHeaderDAO.java @@ -25,27 +25,27 @@ import androidx.room.Query; 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> getPatterns(); + LiveData> getPatterns(); @Query("SELECT * FROM patterns WHERE id = :id") - LiveData getPattern(Long id); + LiveData getPattern(Long id); @Transaction @Query("SELECT * FROM patterns WHERE id = :id") diff --git a/app/src/main/java/net/ktnx/mobileledger/db/DB.java b/app/src/main/java/net/ktnx/mobileledger/db/DB.java index 6bcae84f..80652ed1 100644 --- a/app/src/main/java/net/ktnx/mobileledger/db/DB.java +++ b/app/src/main/java/net/ktnx/mobileledger/db/DB.java @@ -30,7 +30,7 @@ import net.ktnx.mobileledger.dao.PatternAccountDAO; 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() { diff --git a/app/src/main/java/net/ktnx/mobileledger/db/PatternAccount.java b/app/src/main/java/net/ktnx/mobileledger/db/PatternAccount.java deleted file mode 100644 index 3ebd51da..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/db/PatternAccount.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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 . - */ - -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; - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/db/PatternBase.java b/app/src/main/java/net/ktnx/mobileledger/db/PatternBase.java deleted file mode 100644 index 5d048e84..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/db/PatternBase.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 . - */ - -package net.ktnx.mobileledger.db; - -public class PatternBase {} diff --git a/app/src/main/java/net/ktnx/mobileledger/db/PatternHeader.java b/app/src/main/java/net/ktnx/mobileledger/db/PatternHeader.java deleted file mode 100644 index c6223c4f..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/db/PatternHeader.java +++ /dev/null @@ -1,164 +0,0 @@ -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); - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/db/PatternWithAccounts.java b/app/src/main/java/net/ktnx/mobileledger/db/PatternWithAccounts.java index 8992fe18..9f171b1b 100644 --- a/app/src/main/java/net/ktnx/mobileledger/db/PatternWithAccounts.java +++ b/app/src/main/java/net/ktnx/mobileledger/db/PatternWithAccounts.java @@ -24,9 +24,9 @@ import java.util.List; public class PatternWithAccounts { @Embedded - public PatternHeader header; + public TemplateHeader header; @Relation(parentColumn = "id", entityColumn = "pattern_id") - public List accounts; + public List accounts; public Long getId() { return header.getId(); diff --git a/app/src/main/java/net/ktnx/mobileledger/db/TemplateAccount.java b/app/src/main/java/net/ktnx/mobileledger/db/TemplateAccount.java new file mode 100644 index 00000000..92b29080 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/db/TemplateAccount.java @@ -0,0 +1,148 @@ +/* + * 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 . + */ + +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; + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/db/TemplateBase.java b/app/src/main/java/net/ktnx/mobileledger/db/TemplateBase.java new file mode 100644 index 00000000..83a963ac --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/db/TemplateBase.java @@ -0,0 +1,20 @@ +/* + * 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 . + */ + +package net.ktnx.mobileledger.db; + +public class TemplateBase {} diff --git a/app/src/main/java/net/ktnx/mobileledger/db/TemplateHeader.java b/app/src/main/java/net/ktnx/mobileledger/db/TemplateHeader.java new file mode 100644 index 00000000..301b100c --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/db/TemplateHeader.java @@ -0,0 +1,181 @@ +/* + * 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 . + */ + +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); + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/model/PatternDetailSource.java b/app/src/main/java/net/ktnx/mobileledger/model/PatternDetailSource.java deleted file mode 100644 index 5b8f378d..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/model/PatternDetailSource.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 . - */ - -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 DIFF_CALLBACK = - new DiffUtil.ItemCallback() { - @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; - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/model/PatternDetailsItem.java b/app/src/main/java/net/ktnx/mobileledger/model/PatternDetailsItem.java deleted file mode 100644 index eeab37ae..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/model/PatternDetailsItem.java +++ /dev/null @@ -1,627 +0,0 @@ -/* - * 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 . - */ - -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 { - private boolean literalValue; - private T value; - private int matchGroup; - public PossiblyMatchedValue() { - literalValue = true; - value = null; - } - public PossiblyMatchedValue(@NonNull PossiblyMatchedValue origin) { - literalValue = origin.literalValue; - value = origin.value; - matchGroup = origin.matchGroup; - } - @NonNull - public static PossiblyMatchedValue withLiteralInt(Integer initialValue) { - PossiblyMatchedValue result = new PossiblyMatchedValue<>(); - result.setValue(initialValue); - return result; - } - @NonNull - public static PossiblyMatchedValue withLiteralFloat(Float initialValue) { - PossiblyMatchedValue result = new PossiblyMatchedValue<>(); - result.setValue(initialValue); - return result; - } - public static PossiblyMatchedValue withLiteralShort(Short initialValue) { - PossiblyMatchedValue result = new PossiblyMatchedValue<>(); - result.setValue(initialValue); - return result; - } - @NonNull - public static PossiblyMatchedValue withLiteralString(String initialValue) { - PossiblyMatchedValue 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 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 ""; - else - return value.toString(); - if (matchGroup > 0) - return "grp:" + matchGroup; - return ""; - } - } - - 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 accountName = - PossiblyMatchedValue.withLiteralString(""); - private final PossiblyMatchedValue accountComment = - PossiblyMatchedValue.withLiteralString(""); - private final PossiblyMatchedValue amount = - PossiblyMatchedValue.withLiteralFloat(0f); - private final PossiblyMatchedValue 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 transactionDescription = - PossiblyMatchedValue.withLiteralString(""); - private PossiblyMatchedValue transactionComment = - PossiblyMatchedValue.withLiteralString(""); - private PossiblyMatchedValue dateYear = PossiblyMatchedValue.withLiteralInt(null); - private PossiblyMatchedValue dateMonth = PossiblyMatchedValue.withLiteralInt(null); - private PossiblyMatchedValue 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; - } - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailSource.java b/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailSource.java new file mode 100644 index 00000000..611bcfca --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailSource.java @@ -0,0 +1,60 @@ +/* + * 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 . + */ + +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 DIFF_CALLBACK = + new DiffUtil.ItemCallback() { + @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; + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java b/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java new file mode 100644 index 00000000..71992276 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java @@ -0,0 +1,627 @@ +/* + * 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 . + */ + +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 { + private boolean literalValue; + private T value; + private int matchGroup; + public PossiblyMatchedValue() { + literalValue = true; + value = null; + } + public PossiblyMatchedValue(@NonNull PossiblyMatchedValue origin) { + literalValue = origin.literalValue; + value = origin.value; + matchGroup = origin.matchGroup; + } + @NonNull + public static PossiblyMatchedValue withLiteralInt(Integer initialValue) { + PossiblyMatchedValue result = new PossiblyMatchedValue<>(); + result.setValue(initialValue); + return result; + } + @NonNull + public static PossiblyMatchedValue withLiteralFloat(Float initialValue) { + PossiblyMatchedValue result = new PossiblyMatchedValue<>(); + result.setValue(initialValue); + return result; + } + public static PossiblyMatchedValue withLiteralShort(Short initialValue) { + PossiblyMatchedValue result = new PossiblyMatchedValue<>(); + result.setValue(initialValue); + return result; + } + @NonNull + public static PossiblyMatchedValue withLiteralString(String initialValue) { + PossiblyMatchedValue 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 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 ""; + else + return value.toString(); + if (matchGroup > 0) + return "grp:" + matchGroup; + return ""; + } + } + + 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 accountName = + PossiblyMatchedValue.withLiteralString(""); + private final PossiblyMatchedValue accountComment = + PossiblyMatchedValue.withLiteralString(""); + private final PossiblyMatchedValue amount = + PossiblyMatchedValue.withLiteralFloat(0f); + private final PossiblyMatchedValue 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 transactionDescription = + PossiblyMatchedValue.withLiteralString(""); + private PossiblyMatchedValue transactionComment = + PossiblyMatchedValue.withLiteralString(""); + private PossiblyMatchedValue dateYear = PossiblyMatchedValue.withLiteralInt(null); + private PossiblyMatchedValue dateMonth = PossiblyMatchedValue.withLiteralInt(null); + private PossiblyMatchedValue 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; + } + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/PatternDetailSourceSelectorFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/PatternDetailSourceSelectorFragment.java deleted file mode 100644 index 7a8a954d..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/ui/PatternDetailSourceSelectorFragment.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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 . - */ - -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. - *

- * 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 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 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 diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/PatternDetailSourceSelectorModel.java b/app/src/main/java/net/ktnx/mobileledger/ui/PatternDetailSourceSelectorModel.java deleted file mode 100644 index b88c6765..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/ui/PatternDetailSourceSelectorModel.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 . - */ - -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> 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 mSources) { - groups.setValue(mSources); - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/PatternDetailSourceSelectorRecyclerViewAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/PatternDetailSourceSelectorRecyclerViewAdapter.java deleted file mode 100644 index 91a3c4d7..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/ui/PatternDetailSourceSelectorRecyclerViewAdapter.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 . - */ - -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 { - - 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()); - } - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorFragment.java new file mode 100644 index 00000000..1bb48404 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorFragment.java @@ -0,0 +1,188 @@ +/* + * 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 . + */ + +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. + *

+ * 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 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 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 diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorModel.java b/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorModel.java new file mode 100644 index 00000000..46f58852 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorModel.java @@ -0,0 +1,46 @@ +/* + * 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 . + */ + +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> 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 mSources) { + groups.setValue(mSources); + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorRecyclerViewAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorRecyclerViewAdapter.java new file mode 100644 index 00000000..060bf138 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorRecyclerViewAdapter.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ + +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 { + + 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()); + } + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java b/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java index 3ed89291..291ad7c4 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java @@ -55,8 +55,8 @@ import net.ktnx.mobileledger.model.MobileLedgerProfile; 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; @@ -302,7 +302,7 @@ public class MainActivity extends ProfileThemedActivity { 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) { diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionFragment.java index 6be1bd37..e708c5fe 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionFragment.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionFragment.java @@ -49,15 +49,15 @@ import com.google.android.material.snackbar.Snackbar; 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; @@ -90,9 +90,9 @@ public class NewTransactionFragment extends QRScanCapableFragment { 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) { @@ -110,13 +110,13 @@ public class NewTransactionFragment extends QRScanCapableFragment { if (Misc.emptyIsNull(text) == null) return; - LiveData> allPatterns = DB.get() - .getPatternDAO() - .getPatterns(); + LiveData> allPatterns = DB.get() + .getPatternDAO() + .getPatterns(); allPatterns.observe(getViewLifecycleOwner(), patternHeaders -> { - ArrayList matchingPatterns = new ArrayList<>(); + ArrayList matchingPatterns = new ArrayList<>(); - for (PatternHeader ph : patternHeaders) { + for (TemplateHeader ph : patternHeaders) { String patternSource = ph.getRegularExpression(); if (Misc.emptyIsNull(patternSource) == null) continue; @@ -147,7 +147,7 @@ public class NewTransactionFragment extends QRScanCapableFragment { choosePattern(matchingPatterns, text); }); } - private void choosePattern(ArrayList matchingPatterns, String matchedText) { + private void choosePattern(ArrayList matchingPatterns, String matchedText) { final String patternNameColumn = "name"; AbstractCursor cursor = new AbstractCursor() { @Override @@ -207,7 +207,7 @@ public class NewTransactionFragment extends QRScanCapableFragment { .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); @@ -262,7 +262,7 @@ public class NewTransactionFragment extends QRScanCapableFragment { .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(), diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternDetailsAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternDetailsAdapter.java deleted file mode 100644 index 4fd35a52..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternDetailsAdapter.java +++ /dev/null @@ -1,624 +0,0 @@ -/* - * 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 . - */ - -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 { - private static final String D_PATTERN_UI = "pattern-ui"; - private final AsyncListDiffer differ; - public PatternDetailsAdapter() { - super(); - setHasStableIds(true); - differ = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback() { - @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 items) { - ArrayList list = new ArrayList<>(); - for (PatternBase p : items) { - PatternDetailsItem item = PatternDetailsItem.fromRoomObject(p); - list.add(item); - } - setItems(list); - } - public void setItems(List 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"); - } - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternDetailsFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternDetailsFragment.java deleted file mode 100644 index 63bbe9d9..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternDetailsFragment.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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 . - */ - -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 diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternDetailsViewModel.java b/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternDetailsViewModel.java deleted file mode 100644 index 982bb47c..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternDetailsViewModel.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * 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 . - */ - -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> 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 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 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> 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 dbList = db.getPatternDAO() - .getPatternWithAccounts(mPatternId); - Observer observer = new Observer() { - @Override - public void onChanged(PatternWithAccounts src) { - ArrayList 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 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 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 diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternListFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternListFragment.java deleted file mode 100644 index 3f8dbb13..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternListFragment.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 . - */ - -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> 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. - *

- * See the Android Training lesson Communicating with Other Fragments for more information. - */ - public interface OnPatternListFragmentInteractionListener { - void onSavePattern(); - - void onEditPattern(Long id); - } -} \ No newline at end of file diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternViewHolder.java b/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternViewHolder.java deleted file mode 100644 index 8dc8e32b..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternViewHolder.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 . - */ - -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()); - }); - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternsRecyclerViewAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternsRecyclerViewAdapter.java deleted file mode 100644 index 642a5ca6..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/ui/patterns/PatternsRecyclerViewAdapter.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 . - */ - -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 { - private final AsyncListDiffer listDiffer; - public PatternsRecyclerViewAdapter() { - listDiffer = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback() { - @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 newList) { - listDiffer.submitList(newList); - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java new file mode 100644 index 00000000..4f70f846 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java @@ -0,0 +1,627 @@ +/* + * 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 . + */ + +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 { + private static final String D_TEMPLATE_UI = "template-ui"; + private final AsyncListDiffer differ; + public TemplateDetailsAdapter() { + super(); + setHasStableIds(true); + differ = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback() { + @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 items) { + ArrayList list = new ArrayList<>(); + for (TemplateBase p : items) { + TemplateDetailsItem item = TemplateDetailsItem.fromRoomObject(p); + list.add(item); + } + setItems(list); + } + public void setItems(List 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"); + } + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsFragment.java new file mode 100644 index 00000000..94e6989e --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsFragment.java @@ -0,0 +1,113 @@ +/* + * 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 . + */ + +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 diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsViewModel.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsViewModel.java new file mode 100644 index 00000000..088b1b77 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsViewModel.java @@ -0,0 +1,179 @@ +/* + * 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 . + */ + +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> 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 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 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> 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 dbList = db.getPatternDAO() + .getPatternWithAccounts(mPatternId); + Observer observer = new Observer() { + @Override + public void onChanged(PatternWithAccounts src) { + ArrayList 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 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 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 diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateListFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateListFragment.java new file mode 100644 index 00000000..cc5c7988 --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateListFragment.java @@ -0,0 +1,135 @@ +/* + * 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 . + */ + +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> 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. + *

+ * See the Android Training lesson Communicating with Other Fragments for more information. + */ + public interface OnTemplateListFragmentInteractionListener { + void onSaveTemplate(); + + void onEditTemplate(Long id); + } +} \ No newline at end of file diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateViewHolder.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateViewHolder.java new file mode 100644 index 00000000..e9b9032d --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateViewHolder.java @@ -0,0 +1,38 @@ +/* + * 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 . + */ + +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()); + }); + } +} diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplatesActivity.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplatesActivity.java index 0f1d20f4..7bcece63 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplatesActivity.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplatesActivity.java @@ -15,7 +15,7 @@ * along with MoLe. If not, see . */ -package net.ktnx.mobileledger.ui.patterns; +package net.ktnx.mobileledger.ui.templates; import android.os.Bundle; import android.view.Menu; @@ -25,31 +25,32 @@ import androidx.appcompat.app.ActionBar; 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. @@ -63,28 +64,28 @@ public class PatternsActivity extends CrashReportingActivity 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(); @@ -95,27 +96,27 @@ public class PatternsActivity extends CrashReportingActivity } @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() { diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplatesRecyclerViewAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplatesRecyclerViewAdapter.java new file mode 100644 index 00000000..6c1632ea --- /dev/null +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplatesRecyclerViewAdapter.java @@ -0,0 +1,74 @@ +/* + * 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 . + */ + +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 { + private final AsyncListDiffer listDiffer; + public TemplatesRecyclerViewAdapter() { + listDiffer = new AsyncListDiffer<>(this, new DiffUtil.ItemCallback() { + @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 newList) { + listDiffer.submitList(newList); + } +} diff --git a/app/src/main/res/layout/activity_patterns.xml b/app/src/main/res/layout/activity_patterns.xml deleted file mode 100644 index 47ce9f1d..00000000 --- a/app/src/main/res/layout/activity_patterns.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_templates.xml b/app/src/main/res/layout/activity_templates.xml new file mode 100644 index 00000000..97416f9f --- /dev/null +++ b/app/src/main/res/layout/activity_templates.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_item_list.xml b/app/src/main/res/layout/fragment_item_list.xml index b2eda57d..9b6014d1 100644 --- a/app/src/main/res/layout/fragment_item_list.xml +++ b/app/src/main/res/layout/fragment_item_list.xml @@ -26,6 +26,6 @@ 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 diff --git a/app/src/main/res/layout/fragment_pattern_detail_source_selector.xml b/app/src/main/res/layout/fragment_pattern_detail_source_selector.xml deleted file mode 100644 index b36f101c..00000000 --- a/app/src/main/res/layout/fragment_pattern_detail_source_selector.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/fragment_pattern_detail_source_selector_list.xml b/app/src/main/res/layout/fragment_pattern_detail_source_selector_list.xml deleted file mode 100644 index bfdc5d3b..00000000 --- a/app/src/main/res/layout/fragment_pattern_detail_source_selector_list.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_pattern_list.xml b/app/src/main/res/layout/fragment_pattern_list.xml deleted file mode 100644 index f2302546..00000000 --- a/app/src/main/res/layout/fragment_pattern_list.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - diff --git a/app/src/main/res/layout/fragment_template_detail_source_selector.xml b/app/src/main/res/layout/fragment_template_detail_source_selector.xml new file mode 100644 index 00000000..d3af166e --- /dev/null +++ b/app/src/main/res/layout/fragment_template_detail_source_selector.xml @@ -0,0 +1,60 @@ + + + + + + + + diff --git a/app/src/main/res/layout/fragment_template_detail_source_selector_list.xml b/app/src/main/res/layout/fragment_template_detail_source_selector_list.xml new file mode 100644 index 00000000..f21a43d3 --- /dev/null +++ b/app/src/main/res/layout/fragment_template_detail_source_selector_list.xml @@ -0,0 +1,78 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_template_list.xml b/app/src/main/res/layout/fragment_template_list.xml new file mode 100644 index 00000000..a81e9fba --- /dev/null +++ b/app/src/main/res/layout/fragment_template_list.xml @@ -0,0 +1,24 @@ + + + diff --git a/app/src/main/res/layout/pattern_details_account.xml b/app/src/main/res/layout/pattern_details_account.xml deleted file mode 100644 index 49944789..00000000 --- a/app/src/main/res/layout/pattern_details_account.xml +++ /dev/null @@ -1,162 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/pattern_details_fragment.xml b/app/src/main/res/layout/pattern_details_fragment.xml deleted file mode 100644 index 01ddc4d5..00000000 --- a/app/src/main/res/layout/pattern_details_fragment.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/pattern_details_header.xml b/app/src/main/res/layout/pattern_details_header.xml deleted file mode 100644 index 245f7e0e..00000000 --- a/app/src/main/res/layout/pattern_details_header.xml +++ /dev/null @@ -1,299 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/pattern_layout.xml b/app/src/main/res/layout/pattern_layout.xml deleted file mode 100644 index 090870fc..00000000 --- a/app/src/main/res/layout/pattern_layout.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/template_details_account.xml b/app/src/main/res/layout/template_details_account.xml new file mode 100644 index 00000000..0eeb78b9 --- /dev/null +++ b/app/src/main/res/layout/template_details_account.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/template_details_fragment.xml b/app/src/main/res/layout/template_details_fragment.xml new file mode 100644 index 00000000..2b7d5759 --- /dev/null +++ b/app/src/main/res/layout/template_details_fragment.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/template_details_header.xml b/app/src/main/res/layout/template_details_header.xml new file mode 100644 index 00000000..bc5b851e --- /dev/null +++ b/app/src/main/res/layout/template_details_header.xml @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/template_list_template_item.xml b/app/src/main/res/layout/template_list_template_item.xml new file mode 100644 index 00000000..46d6410e --- /dev/null +++ b/app/src/main/res/layout/template_list_template_item.xml @@ -0,0 +1,45 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/pattern_list_menu.xml b/app/src/main/res/menu/pattern_list_menu.xml deleted file mode 100644 index a231f5fe..00000000 --- a/app/src/main/res/menu/pattern_list_menu.xml +++ /dev/null @@ -1,27 +0,0 @@ - - -

- - - \ No newline at end of file diff --git a/app/src/main/res/menu/template_list_menu.xml b/app/src/main/res/menu/template_list_menu.xml new file mode 100644 index 00000000..a231f5fe --- /dev/null +++ b/app/src/main/res/menu/template_list_menu.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/pattern_list_navigation.xml b/app/src/main/res/navigation/pattern_list_navigation.xml deleted file mode 100644 index 927ec201..00000000 --- a/app/src/main/res/navigation/pattern_list_navigation.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/template_list_navigation.xml b/app/src/main/res/navigation/template_list_navigation.xml new file mode 100644 index 00000000..cbd32a14 --- /dev/null +++ b/app/src/main/res/navigation/template_list_navigation.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 98c8b41c..0b97052c 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -169,20 +169,20 @@ Възможно е програмния интерфейс на сървъра да не се поддържа от MoLe Сканиране на QR код Макети - Макети + Макети Шаблон (regex) Помощ Бутон за промяна Бележка към сметката Сума - Прихващане от шаблона + Прихващане от шаблона Липсва шаблон Липсва примерен текст Шаблонът няма прихващания Шаблонът не съвпада с примерния текст Данни за движението - Описание на движението - Бележка към движението + Описание на движението + Бележка към движението Източник на описанието на движението Източник на бележката към движението Дата на движението @@ -199,8 +199,8 @@ Избор на макет Име на макет Функцията още не е готова - Промяна на макет - Създаване на макет + Промяна на макет + Създаване на макет Шаблонът съдържа грешки Липсва сметка Липсва шаблон @@ -212,7 +212,7 @@ Сметка Данни за сметката Източник на името на сметката - ръчно въвеждане + ръчно въвеждане Източник на бележка към сметката Източник на името на сметката diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ea647ba0..8b77d382 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -179,7 +179,7 @@ Perhaps the API of the backend server is not supported by MoLe Scan QR code Patterns - Patterns + Patterns Pattern (regular expression) Help Edit button @@ -194,19 +194,19 @@ Account name Transaction account details Account name source - literal + literal Account comment source Amount source Account comment Amount - Pattern match group + Pattern match group Missing pattern Missing test text Pattern has no capturing groups Pattern doesn\'t match the test text Transaction parameters - Transaction description - Transaction comment + Transaction description + Transaction comment Transaction description source Transaction comment source Transaction date @@ -223,6 +223,6 @@ Choose pattern to apply Pattern name Not implemented yet - Edit pattern - New pattern + Edit pattern + New pattern