Showing preview only (221K chars total). Download the full file or copy to clipboard to get everything.
Repository: concretesolutions/canarinho
Branch: master
Commit: 29858bb998d8
Files: 77
Total size: 196.4 KB
Directory structure:
gitextract_bmhm5g9q/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ └── android_master.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE.txt
├── PULL_REQUEST_TEMPLATE.md
├── README.md
├── build.gradle
├── canarinho/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── br/
│ └── com/
│ └── concrete/
│ └── canarinho/
│ ├── DigitoPara.java
│ ├── formatador/
│ │ ├── Formatador.java
│ │ ├── FormatadorBase.java
│ │ ├── FormatadorBoleto.java
│ │ ├── FormatadorCEP.java
│ │ ├── FormatadorCPFCNPJ.java
│ │ ├── FormatadorLinhaDigitavel.java
│ │ ├── FormatadorTelefone.java
│ │ └── FormatadorValor.java
│ ├── validator/
│ │ ├── Validador.java
│ │ ├── ValidadorBoleto.java
│ │ ├── ValidadorCEP.java
│ │ ├── ValidadorCNPJ.java
│ │ ├── ValidadorCPF.java
│ │ ├── ValidadorCPFCNPJ.java
│ │ └── ValidadorTelefone.java
│ └── watcher/
│ ├── BaseCanarinhoTextWatcher.java
│ ├── BoletoBancarioTextWatcher.java
│ ├── CEPTextWatcher.java
│ ├── CPFCNPJTextWatcher.java
│ ├── MascaraNumericaTextWatcher.java
│ ├── TelefoneTextWatcher.java
│ ├── ValorMonetarioWatcher.java
│ └── evento/
│ ├── EventoDeValidacao.java
│ └── EventoDeValidacaoDeBoleto.java
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── sample/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── br/
│ │ └── com/
│ │ └── concrete/
│ │ └── canarinho/
│ │ └── sample/
│ │ ├── BugOnApi28Test.java
│ │ └── DemoWatchersInstrumentationTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── br/
│ │ │ └── com/
│ │ │ └── concrete/
│ │ │ └── canarinho/
│ │ │ └── sample/
│ │ │ └── ui/
│ │ │ ├── activity/
│ │ │ │ └── MainActivity.java
│ │ │ ├── adapter/
│ │ │ │ └── WatchersPagerAdapter.java
│ │ │ ├── fragment/
│ │ │ │ ├── BaseWatcherFragment.java
│ │ │ │ ├── CanarinhoValorMonetarioWatcherFragment.java
│ │ │ │ └── WatcherFragment.java
│ │ │ └── model/
│ │ │ └── Watchers.java
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── fragment_canarinho_watcher.xml
│ │ │ ├── fragment_valor_monetario_watcher.xml
│ │ │ └── main_activity.xml
│ │ └── values/
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── br/
│ └── com/
│ └── concrete/
│ └── canarinho/
│ └── test/
│ ├── TesteFormatadorBOLETO.java
│ ├── TesteFormatadorCEP.java
│ ├── TesteFormatadorCNPJ.java
│ ├── TesteFormatadorCPF.java
│ ├── TesteFormatadorCPFCNPJ.java
│ ├── TesteFormatadorLinhaDigitavel.java
│ ├── TesteFormatadorTelefone.java
│ ├── TesteFormatadorValor.java
│ ├── TesteValidadores.java
│ └── watcher/
│ ├── BoletoTextWatcherTest.java
│ └── ValorMonetarioWatcherTest.java
├── settings.gradle
└── tools/
├── linters/
│ ├── checkstyle/
│ │ ├── checkstyle.xml
│ │ └── suppressions.xml
│ ├── linters.gradle
│ └── pmd/
│ └── pmd-ruleset.xml
└── publish.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/android_master.yml
================================================
name: Android Pull Request Master CI
on:
pull_request:
branches:
- 'master'
jobs:
Instrumented_Test:
runs-on: macOS-latest
strategy:
matrix:
api-level: [27, 29]
steps:
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Instrumented Tests
uses: reactivecircus/android-emulator-runner@v1
with:
api-level: ${{ matrix.api-level }}
script: ./gradlew connectedCheck
unitTests:
name: Unit Tests
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: set up JDK 1.9
uses: actions/setup-java@v1
with:
java-version: 1.9
- name: Unit tests
run: bash ./gradlew test
linters:
name: Linters
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: set up JDK 1.9
uses: actions/setup-java@v1
with:
java-version: 1.9
- name: Checkstyle
run: bash ./gradlew checkstyle
- name: PMD
run: bash ./gradlew pmd
================================================
FILE: .gitignore
================================================
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.idea/
*.iml
# Built application files
*.apk
*.ap_
# Files for the Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## 2.0.3
- Migração do bintray para Github Package Registry
## 2.0.2
- Atualização androidx
- Migração CI para Github Actions
- Migração jitpack.io
## 2.0.1
- Corrige bug no formatador da API 28
- Corrige bug do formatador numérico com símbolo
## 2.0.0
- Alterado o pacote da bilbioteca. Junto a reformulação do nome da própria Concrete (remoção de Solutions)
- Corrige issue #19. Obrigado a @luisfernandezbr pelo fix
- Atualiza sistema de build
### Quebras de API
- Corrige grafia do validador de telefone (TELFONE -> TELEFONE).
## 1.2.0
- Adicionada configuração de `ValorMonetarioWatcher`
- É possível deixar o símbolo de Real
- É possível manter os zeros quando o campo é apagado em lote
- Adicionado Builder para criação de watchers de valor
## 1.1.1:
- Corrigido bug de ArrayIndexOutOfBounds no `BoletoBancarioTextWatcher` após apagar em lote
- Adicionado teste de regressão para o caso acima na JVM que será executado no Travis
## 1.1.0:
- Refatorados testes instrumentados
- Adicionado construtor para máscara genérica
## 1.0.0:
- Removida necessidade de ter um validador/evento de validação na criação de Watchers
## 0.1.0:
- Ajustes no código antes da versão 1.0
## 0.0.9:
- Correção final para o formatador/validador de boleto. Tanto normal quanto tributo.
## 0.0.8:
- Adição de formatdor/validador de CEP
- Correção de formatdor monetário ao rotacionar a tela
## 0.0.7:
- Correção de validador de boleto quando setando o código de boleto inteiro (caso de uso: recebendo de um leitor de imagens)
## 0.0.6:
- Watcher de CPF/CNPJ simultâneos
## 0.0.5:
- Release no JCenter
## 0.0.4:
- Watcher para valor monetário no padão Real (vírgula para casas de milhar e ponto para casas decimais)
- Re-estruturação do projeto para gerar os binários na pasta correta do Bintray
## 0.0.3:
- Série de ajustes para Travis e JCenter (ainda não publicado)
## 0.0.2:
- Formatadores de telefone
## 0.0.1:
- Release inicial
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at bruno.silva@concrete.com.br. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
================================================
FILE: PULL_REQUEST_TEMPLATE.md
================================================
# Título para o PullRequest
O que acha de deixar uma descrição simples sobre o seu PR?
## Tipo de mudança
- [ ] Bug fix
- [ ] Nova Feature
- [ ] Melhoria
- [ ] Outra?
## Está relacionada a uma issue?
Cole o link da issue aqui.
## Quer adicionar outra informação?
Basta escrever aqui.
================================================
FILE: README.md
================================================
# Android Canarinho

Esta biblioteca é um conjunto de utilitários para trabalhar com padrões brasileiros no Android.
Inspirado em: https://github.com/caelum/caelum-stella.
O foco aqui é o Android. Portanto, não é compatível com aplicações Java puras.
Entre os padrões implementados temos:
- Formatador e validador de CPF
- Formatador e validador de CNPJ
- Formatador e validador de boleto bancário (e linha digitável)
- Formatador e validador de CEP
- Formatador de telefone
- [Formatador de valores financeiros](#formatador-de-valor-financeiro-no-padrão-real) (vírgula para milhares e ponto para decimais com duas casas)
Estes são utilizados para implementar `TextWatcher`s que formatam e validam a digitação do usuário.
## Exemplo de uso:
### Validar um CPF
```java
if (Validador.CPF.ehValido(cpf))
Toast.makeText(context, "Válido!", Toast.LENGTH_SHORT).show();
else
Toast.makeText(context, "Inválido!", Toast.LENGTH_SHORT).show();
```
### Formatar um CPF
```java
String cpfFormatado = Formatador.CPF.formata(usuario.getCpf());
```
### Formatar um EditText para CPF sem validação
```java
cpfEditText.addTextChangedListener(new MascaraNumericaTextWatcher("###.###.###-##"));
```
### Formatar um EditText para CPF com validação
```java
cpfEditText.addTextChangedListener(new MascaraNumericaTextWatcher.Builder()
.paraMascara("###.###.###-##")
.comCallbackDeValidacao(new SampleEventoDeValidacao(context))
.comValidador(Validador.CPF)
.build());
```
## Formatador de valor financeiro no padrão Real
Para deixar um usuário digitar valores monetários no padrão Real, basta adicionar um `ValorMonetarioWatcher` e alguns atributos ao `EditText`
```java
// Padrão sem símbolo de Real
editText.addTextChangedListener(new ValorMonetarioWatcher());
editText.append("1234567890");
assertThat(editText.getText().toString(), is("12.345.678,90"));
// Customizado com símbolo e mantendo zeros ao apagar em lote
editText.addTextChangedListener(new ValorMonetarioWatcher.Builder()
.comSimboloReal()
.comMantemZerosAoLimpar()
.build());
editText.append("1234567890");
assertThat(editText.getText().toString(), is("R$ 12.345.678,90"));
editText.getText().clear();
assertThat(editText.getText().toString(), is("R$ 0,00"));
```
Exemplo de declaração no layout:
```xml
<!--
O inputType abre o teclado númerico e permite caracteres de
formatação. O text inicia com o valor zerado, porém não é
obrigatório.
-->
<android.support.design.widget.TextInputEditText
android:id="@+id/amount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_value"
android:inputType="number"
android:maxLength="13"
android:text="0,00" />
```
## Callback de validação
Os `TextWatcher`s possuem a possibilidade de avisar o usuário conforme ele está digitando sobre algum erro de campo.
Para isso, usamos um `EventoDeValidacao` que possui os seguintes callbacks:
- `void invalido(String valorAtual, String mensagem)`: chamado quando o valor está inválido
- `void parcialmenteValido(String valorAtual)`: chamado quando o valor ainda não está completo e também não está inválido
- `void totalmenteValido(String valorAtual)`: chamado quando o valor está completo e válido
Um exemplo de implementação:
```java
public class SampleEventoDeValidacao implements EventoDeValidacao {
private final TextInputLayout textInputLayout;
public SampleEventoDeValidacao(TextInputLayout textInputLayout) {
this.textInputLayout = textInputLayout;
}
@Override
public void invalido(String valorAtual, String mensagem) {
textInputLayout.setError(mensagem);
}
@Override
public void parcialmenteValido(String valorAtual) {
textInputLayout.setErrorEnabled(false);
textInputLayout.setError(null);
}
@Override
public void totalmenteValido(String valorAtual) {
new AlertDialog.Builder(textInputLayout.getContext())
.setTitle("Campo válido!")
.setMessage(valorAtual)
.show();
}
}
```
Veja exemplos de implementação no sample.
## Changelog
Ver [CHANGELOG.md](CHANGELOG.md)
## Arquitetura
- `Formatador`: Formata, desformata e verifica se um valor está formatado e se pode ser formatado. Opera com valores completos.
- `Validador`: Valida de duas formas: absoluta (true ou false) e atualizando um objeto de validação (`ResultadoParcial`).
- Watchers: implementações de `TextWatcher`s para formatação e validação contínua (conforme a digitação do usuário).
Para exemplos, verifique os testes na pasta sample.
## Gradle
`allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}`
`implementation 'br.com.concrete:canarinho:{latest version}'`
## ATENÇÃO
Este projeto é desenvolvido de boa vontade e com o intuito de ajudar. No entanto, todo o desenvolvimento é feito SEM GARANTIAS.
## LICENÇA
Este projeto é disponibilizado sob a licença Apache vesão 2.0. Ver declaração no arquivo LICENSE.txt
================================================
FILE: build.gradle
================================================
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
================================================
FILE: canarinho/.gitignore
================================================
/build
================================================
FILE: canarinho/build.gradle
================================================
apply plugin: 'com.android.library'
apply from: "$rootDir/tools/linters/linters.gradle"
apply from: "$rootDir/tools/publish.gradle"
ext {
publishedGroupId = 'br.com.concrete'
libraryName = 'Android Canarinho'
artifact = 'canarinho'
libraryDescription = 'Utilitários Android para padrões Brasileiros'
libraryVersion = '2.0.3'
licenseName = 'The Apache Software License, Version 2.0'
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
allLicenses = ["Apache-2.0"]
}
android {
compileSdkVersion 31
defaultConfig {
minSdkVersion 21
targetSdkVersion 31
}
}
================================================
FILE: canarinho/src/main/AndroidManifest.xml
================================================
<manifest package="br.com.concrete.canarinho" />
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/DigitoPara.java
================================================
package br.com.concrete.canarinho;
import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Uma fluent interface para o cálculo de dígitos, que é usado em diversos boletos e
* documentos.
* <p>
* Para exemplificar, o dígito do trecho 0000039104766 para os multiplicadores indo de
* 2 a 7 e usando módulo 11 é a seguinte:
* </p>
* <pre>
* 0 0 0 0 0 3 9 1 0 4 7 6 6 (trecho numérico)
* 2 7 6 5 4 3 2 7 6 5 4 3 2 (multiplicadores, da direita para a esquerda e ciclando)
* ----------------------------------------- multiplicações algarismo a algarismo
* 0 0 0 0 0 9 18 7 0 20 28 18 12 -- soma = 112
* </pre>
* <p>
* Tira-se o módulo dessa soma e, então, calcula-se o complementar do módulo e, se o número
* for 0, 10 ou 11, o dígito passa a ser 1.
* </p>
* <pre>
* soma = 112
* soma % 11 = 2
* 11 - (soma % 11) = 9
* </pre>
* <p>
* NOTE: Esta é uma versão otimizada para Android inspirada em
* https://github.com/caelum/caelum-stella/blob/master/stella-core/src/main/java/br/com/caelum/stella/DigitoPara.java
* </p>
*/
public final class DigitoPara {
private final List<Integer> numero = new LinkedList<>();
private final List<Integer> multiplicadores;
private final boolean complementar;
private final int modulo;
private final boolean somarIndividual;
private final SparseArray<String> substituicoes;
private DigitoPara(Builder builder) {
multiplicadores = builder.multiplicadores;
complementar = builder.complementar;
modulo = builder.modulo;
somarIndividual = builder.somarIndividual;
substituicoes = builder.substituicoes;
}
/**
* Faz a soma geral das multiplicações dos algarismos pelos multiplicadores, tira o
* módulo e devolve seu complementar.
*
* @param trecho Bloco para calcular o dígito
* @return String o dígito vindo do módulo com o número passado e configurações extra.
*/
public final String calcula(String trecho) {
numero.clear();
final char[] digitos = trecho.toCharArray();
for (char digito : digitos) {
numero.add(Character.getNumericValue(digito));
}
Collections.reverse(numero);
int soma = 0;
int multiplicadorDaVez = 0;
for (int i = 0; i < numero.size(); i++) {
final int multiplicador = multiplicadores.get(multiplicadorDaVez);
final int total = numero.get(i) * multiplicador;
soma += somarIndividual ? somaDigitos(total) : total;
multiplicadorDaVez = proximoMultiplicador(multiplicadorDaVez);
}
int resultado = soma % modulo;
if (complementar) {
resultado = modulo - resultado;
}
if (substituicoes.get(resultado) != null) {
return substituicoes.get(resultado);
}
return String.valueOf(resultado);
}
/*
* soma os dígitos do número (até 2)
*
* Ex: 18 => 9 (1+8), 12 => 3 (1+2)
*/
private int somaDigitos(int total) {
return (total / 10) + (total % 10);
}
/*
* Devolve o próximo multiplicador a ser usado, isto é, a próxima posição da lista de
* multiplicadores ou, se chegar ao fim da lista, a primeira posição, novamente.
*/
private int proximoMultiplicador(int multiplicadorDaVez) {
int multiplicador = multiplicadorDaVez + 1;
if (multiplicador == multiplicadores.size()) {
multiplicador = 0;
}
return multiplicador;
}
/**
* Builder com interface fluente para criação de instâncias configuradas de
* {@link DigitoPara}.
*/
public static final class Builder {
private List<Integer> multiplicadores = new ArrayList<>();
private boolean complementar;
private int modulo;
private boolean somarIndividual;
private final SparseArray<String> substituicoes = new SparseArray<>();
/**
* TODO Javadoc pendente.
*
* @param modulo Inteiro pelo qual o resto será tirado e também seu complementar.
* O valor padrão é 11.
* @return this
*/
public final Builder mod(int modulo) {
this.modulo = modulo;
return this;
}
/**
* Para multiplicadores (ou pesos) sequenciais e em ordem crescente, esse método permite
* criar a lista de multiplicadores que será usada ciclicamente, caso o número base seja
* maior do que a sequência de multiplicadores. Por padrão os multiplicadores são iniciados
* de 2 a 9. No momento em que você inserir outro valor este default será sobrescrito.
*
* @param inicio Primeiro número do intervalo sequencial de multiplicadores
* @param fim Último número do intervalo sequencial de multiplicadores
* @return this
*/
public final Builder comMultiplicadoresDeAte(int inicio, int fim) {
this.multiplicadores.clear();
for (int i = inicio; i <= fim; i++) {
multiplicadores.add(i);
}
return this;
}
/**
* <p>
* Indica se, ao calcular o módulo, a soma dos resultados da multiplicação deve ser
* considerado digito a dígito.
* </p>
* Ex: 2 X 9 = 18, irá somar 9 (1 + 8) invés de 18 ao total.
*
* @return this
*/
public final Builder somandoIndividualmente() {
this.somarIndividual = true;
return this;
}
/**
* É comum que os geradores de dígito precisem do complementar do módulo em vez
* do módulo em sí. Então, a chamada desse método habilita a flag que é usada
* no método mod para decidir se o resultado devolvido é o módulo puro ou seu
* complementar.
*
* @return this
*/
public final Builder complementarAoModulo() {
this.complementar = true;
return this;
}
/**
* Troca por uma String caso encontre qualquer dos inteiros passados como argumento.
*
* @param substituto String para substituir
* @param i varargs de inteiros a serem substituídos
* @return this
*/
public final Builder trocandoPorSeEncontrar(String substituto, Integer... i) {
substituicoes.clear();
for (Integer integer : i) {
substituicoes.put(integer, substituto);
}
return this;
}
/**
* Há documentos em que os multiplicadores não usam todos os números de um intervalo
* ou alteram sua ordem. Nesses casos, a lista de multiplicadores pode ser passada
* através de varargs.
*
* @param multiplicadoresEmOrdem Sequência de inteiros com os multiplicadores em ordem
* @return this
*/
public final Builder comMultiplicadores(Integer... multiplicadoresEmOrdem) {
this.multiplicadores.clear();
this.multiplicadores.addAll(Arrays.asList(multiplicadoresEmOrdem));
return this;
}
/**
* Método responsável por criar o DigitoPara.
* Este método inicializará os seguintes valores padrões:
* <ul>
* <li>multiplicadores: 2 a 9</li>
* <li>módulo: 11</li>
* </ul>
*
* @return A instância imutável de DigitoPara
*/
public final DigitoPara build() {
if (multiplicadores.size() == 0) {
comMultiplicadoresDeAte(2, 9);
}
if (modulo == 0) {
mod(11);
}
return new DigitoPara(this);
}
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/Formatador.java
================================================
package br.com.concrete.canarinho.formatador;
import java.util.regex.Pattern;
/**
* Interface de formatação. Formata valores completos. Útil caso receba o valor
* desformatado de uma API.
*/
public interface Formatador {
// Formatadores
/**
* Singleton de formatação de CEP.
*/
Formatador CEP = new FormatadorBase(
Padroes.CEP_FORMATADO,
"$1-$2",
Padroes.CEP_DESFORMATADO,
"$1$2"
);
/**
* Singleton de formatação de CPF.
*/
Formatador CPF = new FormatadorBase(
Padroes.CPF_FORMATADO,
"$1.$2.$3-$4",
Padroes.CPF_DESFORMATADO,
"$1$2$3$4"
);
/**
* Singleton de formatação de CNPJ.
*/
Formatador CNPJ = new FormatadorBase(
Padroes.CNPJ_FORMATADO,
"$1.$2.$3/$4-$5",
Padroes.CNPJ_DESFORMATADO,
"$1$2$3$4$5"
);
/**
* Singleton de formatação de CPF e CNPJ.
*/
Formatador CPF_CNPJ = FormatadorCPFCNPJ.getInstance();
/**
* Singleton de formatação de valores monetários.
*/
FormatadorValor VALOR = FormatadorValor.getInstance(false);
/**
* Singleton de formatação de valores monetários com símbolo do Real.
*/
FormatadorValor VALOR_COM_SIMBOLO = FormatadorValor.getInstance(true);
/**
* Singleton de formatação de boletos bancários.
*/
Formatador BOLETO = FormatadorBoleto.getInstance();
/**
* Singleton de formatação de telefones (DDD) número.
*/
Formatador TELEFONE = FormatadorTelefone.getInstance();
/**
* Singleton de formatação de linha digitável. Transforma números do código de barras em linha
* digitável.
*/
Formatador LINHA_DIGITAVEL = FormatadorLinhaDigitavel.getInstance();
/**
* Formata um valor COMPLETO. Deve falhar caso o valor não esteja completo.
*
* @param value valor a formatar
* @return REsultado da formatação
*/
String formata(String value);
/**
* Desformata um valor.
*
* @param value Valor a desformatar
* @return Resultado da desformatação
*/
String desformata(String value);
/**
* Verifica se um parâmetro está formatado.
*
* @param value Valor para verificar
* @return True se estiver formatado, falso caso contrário.
*/
boolean estaFormatado(String value);
/**
* Verifica se um parâmetro pode ser formatado.
*
* @param value Valor para verificar
* @return True se puder ser formatado, falso caso contrário.
*/
boolean podeSerFormatado(String value);
/**
* Classe para guardar os padrões de experssões regulares usados no framework.
*/
abstract class Padroes {
public static final Pattern PADRAO_SOMENTE_NUMEROS = Pattern.compile("[^0-9]");
public static final Pattern CEP_FORMATADO = Pattern.compile("(\\d{5})-(\\d{3})");
public static final Pattern CEP_DESFORMATADO = Pattern.compile("(\\d{5})(\\d{3})");
public static final Pattern CNPJ_FORMATADO = Pattern.compile(
"(\\d{2})[.](\\d{3})[.](\\d{3})/(\\d{4})-(\\d{2})"
);
public static final Pattern CNPJ_DESFORMATADO = Pattern.compile(
"(\\d{2})(\\d{3})(\\d{3})(\\d{4})(\\d{2})"
);
public static final Pattern CPF_FORMATADO = Pattern.compile(
"(\\d{3})[.](\\d{3})[.](\\d{3})-(\\d{2})"
);
public static final Pattern CPF_DESFORMATADO = Pattern.compile(
"(\\d{3})(\\d{3})(\\d{3})(\\d{2})"
);
private Padroes() {
}
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorBase.java
================================================
package br.com.concrete.canarinho.formatador;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Classe utilitária para a implementação de todos os formatadores que precisam apenas da aplicação
* de {@link Pattern}s.
*/
final class FormatadorBase implements Formatador {
private final Pattern formatted;
private final String formattedReplacement;
private final Pattern unformatted;
private final String unformattedReplacement;
/**
* Constrói um formatador que recebe a configuração de formatação e substituição.
*
* @param formatted Pattern de regex para formatar o conteúdo
* @param formattedReplacement String com as posições de substituição dos grupos encontrados por regex
* @param unformatted Pattern de regex para DESformatar o conteúdo
* @param unformattedReplacement String com as posições de substituição dos grupos encontrados por regex
*/
FormatadorBase(
Pattern formatted,
String formattedReplacement,
Pattern unformatted,
String unformattedReplacement) {
this.formatted = formatted;
this.formattedReplacement = formattedReplacement;
this.unformatted = unformatted;
this.unformattedReplacement = unformattedReplacement;
}
@Override
public final String formata(String value) throws IllegalArgumentException {
if (value == null) {
throw new IllegalArgumentException("Value may not be null.");
}
if (formatted.matcher(value).matches()) {
return value;
}
return matchAndReplace(unformatted.matcher(value), formattedReplacement);
}
@Override
public final String desformata(String value) throws IllegalArgumentException {
if (value == null) {
throw new IllegalArgumentException("Value may not be null.");
}
if (unformatted.matcher(value).matches()) {
return value;
}
final Matcher matcher = formatted.matcher(value);
return matchAndReplace(matcher, unformattedReplacement);
}
@Override
public final boolean estaFormatado(String value) {
if (value == null) {
throw new IllegalArgumentException("value must not be null");
}
return formatted.matcher(value).matches();
}
@Override
public final boolean podeSerFormatado(String value) {
return value != null && unformatted.matcher(value).matches();
}
private String matchAndReplace(Matcher matcher, String replacement) {
if (matcher.matches()) {
return matcher.replaceAll(replacement);
}
throw new IllegalArgumentException("Valor não está formatado propriamente.");
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorBoleto.java
================================================
package br.com.concrete.canarinho.formatador;
import java.util.regex.Pattern;
/**
* Formatador especializado para linha digitável de boletos bancários. Ele detecta automaticamente
* se o boleto é do tipo tributos ou de conveniados. Caso o boleto comece com o número '8', então
* é aplicada a formatação: 888888888888 888888888888 888888888888 888888888888. Caso contrário,
* usa-se o padrão 99999.99999 99999.999999 99999.999999 9 99999999999999.
*/
public final class FormatadorBoleto implements Formatador {
private static final Pattern TRIBUTO_FORMATADO =
Pattern.compile("(\\d{12})\\s(\\d{12})\\s(\\d{12})\\s(\\d{12})");
private static final Pattern TRIBUTO_DESFORMATADO =
Pattern.compile("(\\d{12})(\\d{12})(\\d{12})(\\d{12})");
private static final FormatadorBase FORMATADOR_TRIBUTOS = new FormatadorBase(
TRIBUTO_FORMATADO,
"$1 $2 $3 $4",
TRIBUTO_DESFORMATADO,
"$1$2$3$4"
);
private static final Pattern NORMAL_FORMATADO = Pattern.compile(
"(\\d{5})[.](\\d{5})\\s(\\d{5})[.](\\d{6})\\s(\\d{5})[.](\\d{6})\\s(\\d)\\s(\\d{14})"
);
private static final Pattern NORMAL_DESFORMATADO = Pattern.compile(
"(\\d{5})(\\d{5})(\\d{5})(\\d{6})(\\d{5})(\\d{6})(\\d{1})(\\d{14})"
);
private static final FormatadorBase FORMATADOR_NORMAL = new FormatadorBase(
NORMAL_FORMATADO,
"$1.$2 $3.$4 $5.$6 $7 $8",
NORMAL_DESFORMATADO,
"$1$2$3$4$5$6$7$8"
);
// No instance creation
private FormatadorBoleto() {
}
@Override
public String formata(String value) {
if (ehTributo(value)) {
return FORMATADOR_TRIBUTOS.formata(value);
}
return FORMATADOR_NORMAL.formata(value);
}
@Override
public String desformata(String value) {
if (ehTributo(value)) {
return FORMATADOR_TRIBUTOS.desformata(value);
}
return FORMATADOR_NORMAL.desformata(value);
}
@Override
public boolean estaFormatado(String value) {
if (ehTributo(value)) {
return FORMATADOR_TRIBUTOS.estaFormatado(value);
}
return FORMATADOR_NORMAL.estaFormatado(value);
}
@Override
public boolean podeSerFormatado(String value) {
if (ehTributo(value)) {
return FORMATADOR_TRIBUTOS.podeSerFormatado(value);
}
return FORMATADOR_NORMAL.podeSerFormatado(value);
}
private boolean ehTributo(String value) {
if (value == null) {
throw new IllegalArgumentException("Valor não pode ser nulo");
}
return value.charAt(0) == '8';
}
static FormatadorBoleto getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final FormatadorBoleto INSTANCE = new FormatadorBoleto();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorCEP.java
================================================
package br.com.concrete.canarinho.formatador;
/**
* Formatador para CEP. Segue o padrão 99999-999.
*/
public final class FormatadorCEP implements Formatador {
private FormatadorCEP() {
}
static FormatadorCEP getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public String formata(final String value) {
return Formatador.CEP.formata(value);
}
@Override
public String desformata(final String value) {
return Formatador.CEP.desformata(value);
}
@Override
public boolean estaFormatado(final String value) {
return Formatador.CEP.estaFormatado(value);
}
@Override
public boolean podeSerFormatado(final String value) {
if (value == null) {
return false;
}
return Formatador.CEP.podeSerFormatado(value);
}
private static class SingletonHolder {
private static final FormatadorCEP INSTANCE = new FormatadorCEP();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorCPFCNPJ.java
================================================
package br.com.concrete.canarinho.formatador;
/**
* Formatador para CPF e CNPJ no mesmo campo. Formata como CPF até 11 dígitos numéricos. Depois
* formata como CNPJ.
*/
public final class FormatadorCPFCNPJ implements Formatador {
private FormatadorCPFCNPJ() {
}
static FormatadorCPFCNPJ getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public String formata(final String value) {
if (ehCpf(value)) {
return Formatador.CPF.formata(value);
}
return Formatador.CNPJ.formata(value);
}
@Override
public String desformata(final String value) {
if (ehCpf(value)) {
return Formatador.CPF.desformata(value);
}
return Formatador.CNPJ.desformata(value);
}
@Override
public boolean estaFormatado(final String value) {
if (ehCpf(value)) {
return Formatador.CPF.estaFormatado(value);
}
return Formatador.CNPJ.estaFormatado(value);
}
@Override
public boolean podeSerFormatado(final String value) {
if (value == null) {
return false;
}
if (ehCpf(value)) {
return Formatador.CPF.podeSerFormatado(value);
}
return Formatador.CNPJ.podeSerFormatado(value);
}
private boolean ehCpf(String value) {
if (value == null) {
throw new IllegalArgumentException("Valor não pode ser nulo");
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(value)
.replaceAll("");
return desformatado.length() < 12;
}
private static class SingletonHolder {
private static final FormatadorCPFCNPJ INSTANCE = new FormatadorCPFCNPJ();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorLinhaDigitavel.java
================================================
package br.com.concrete.canarinho.formatador;
import br.com.concrete.canarinho.DigitoPara;
import br.com.concrete.canarinho.validator.ValidadorBoleto;
/**
* Transforma a linha digitável de um boleto em um código de boleto e vice-versa. Use o metodo
* {@link #formata(String)} para transformar a linha digitavel em boleto e
* {@link #desformata(String)}.
* Para verificar se um valor esta em linha digitável ou em boleto, usar os métodos:
* <ul>
* <li>{@link #estaFormatado(String)}: indicará se está em formata de boleto</li>
* <li>{@link #podeSerFormatado(String)}: indicará se é uma linha digitável</li>
* </ul>
*/
public final class FormatadorLinhaDigitavel implements Formatador {
private FormatadorLinhaDigitavel() {
}
static FormatadorLinhaDigitavel getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public String formata(String value) {
if (value == null || value.length() != 44) {
throw new IllegalArgumentException("Linha digitável deve conter 44 caracteres. "
+ "Valor possui " + value + " caracteres");
}
if (value.startsWith("8")) {
final String primeiroBloco = value.substring(0, 11);
final String segundoBloco = value.substring(11, 22);
final String terceiroBloco = value.substring(22, 33);
final String quartoBloco = value.substring(33, 44);
// o terceiro dígito é o de valor real que define se será mod 10 ou mod 11
final boolean ehMod10 = value.charAt(2) == '6' || value.charAt(2) == '7';
final DigitoPara mod = ehMod10 ? ValidadorBoleto.MOD_10 : ValidadorBoleto.MOD_11;
final String primeiroDigito = mod.calcula(primeiroBloco);
final String segundoDigito = mod.calcula(segundoBloco);
final String terceiroDigito = mod.calcula(terceiroBloco);
final String quartoDigito = mod.calcula(quartoBloco);
return primeiroBloco + primeiroDigito + segundoBloco + segundoDigito
+ terceiroBloco + terceiroDigito + quartoBloco + quartoDigito;
}
String primeiroBloco = value.substring(0, 4); // 4
String segundoBloco = value.substring(4, 19); // 15
String terceiroBloco = value.substring(19, 24); // 5
String quartoBloco = value.substring(24, 34); // 10
String quintoBloco = value.substring(34, 44); // 10
// 1 - 3 - 4 - 5 - 2
final StringBuilder codigoOrdenado = new StringBuilder(primeiroBloco)
.append(terceiroBloco)
.append(quartoBloco)
.append(quintoBloco)
.append(segundoBloco);
primeiroBloco = codigoOrdenado.substring(0, 9);
segundoBloco = codigoOrdenado.substring(9, 19);
terceiroBloco = codigoOrdenado.substring(19, 29);
quartoBloco = codigoOrdenado.substring(29);
final String primeiroDigito = ValidadorBoleto.MOD_10.calcula(primeiroBloco);
final String segundoDigito = ValidadorBoleto.MOD_10.calcula(segundoBloco);
final String terceiroDigito = ValidadorBoleto.MOD_10.calcula(terceiroBloco);
return primeiroBloco + primeiroDigito + segundoBloco + segundoDigito
+ terceiroBloco + terceiroDigito + quartoBloco;
}
@Override
public String desformata(String valor) {
if (valor == null || "".equals(valor)) {
throw new IllegalArgumentException("Valor não pode estar nulo.");
}
String valorDesformatadao = Padroes.PADRAO_SOMENTE_NUMEROS.matcher(valor)
.replaceAll("");
if (valorDesformatadao.charAt(0) == '8') {
if (valorDesformatadao.length() != 48) {
throw new IllegalArgumentException(
"Valor para boletos que iniciam com 8 deve conter 48 dígitos"
);
}
final StringBuilder builder = new StringBuilder(valorDesformatadao);
final String primeiroBloco = builder.substring(0, 11);
final String segundoBloco = builder.substring(12, 23);
final String terceiroBloco = builder.substring(24, 35);
final String quartoBloco = builder.substring(36, 47);
return "" + primeiroBloco + segundoBloco + terceiroBloco + quartoBloco;
}
if (valorDesformatadao.length() != 47) {
throw new IllegalArgumentException("Valor para boletos deve conter 47 digitos");
}
String primeiroBloco = valorDesformatadao.substring(0, 9);
String segundoBloco = valorDesformatadao.substring(10, 20);
String terceiroBloco = valorDesformatadao.substring(21, 31);
String quartoBloco = valorDesformatadao.substring(32, valorDesformatadao.length());
final StringBuilder boletoOrdenado = new StringBuilder(primeiroBloco)
.append(segundoBloco)
.append(terceiroBloco)
.append(quartoBloco);
// 1 - 3 - 4 - 5 - 2
primeiroBloco = boletoOrdenado.substring(0, 4); // 4
segundoBloco = boletoOrdenado.substring(29, 44); // 15
terceiroBloco = boletoOrdenado.substring(4, 9); // 5
quartoBloco = boletoOrdenado.substring(9, 19); // 10
final String quintoBloco = boletoOrdenado.substring(19, 29); // 10
return "" + primeiroBloco + segundoBloco + terceiroBloco + quartoBloco + quintoBloco;
}
@Override
public boolean estaFormatado(String value) {
return Formatador.BOLETO.estaFormatado(value);
}
@Override
public boolean podeSerFormatado(String value) {
return Padroes.PADRAO_SOMENTE_NUMEROS.matcher(value)
.replaceAll("")
.length() == 44;
}
private static class SingletonHolder {
private static final FormatadorLinhaDigitavel INSTANCE = new FormatadorLinhaDigitavel();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorTelefone.java
================================================
package br.com.concrete.canarinho.formatador;
import java.util.regex.Pattern;
/**
* Formata no padrão de telefone brasileiro: (99) 99999-9999 ou (99) 9999-9999.
*/
public final class FormatadorTelefone implements Formatador {
private static final Pattern NOVE_DIGITOS_FORMATADO = Pattern.compile(
"\\((\\d{2})\\)\\s(\\d{5})-(\\d{4})"
);
private static final Pattern NOVE_DIGITOS_DESFORMATADO = Pattern.compile(
"(\\d{2})(\\d{5})(\\d{4})"
);
private static final Pattern OITO_DIGITOS_FORMATADO = Pattern.compile(
"\\((\\d{2})\\)\\s(\\d{4})-(\\d{4})"
);
private static final Pattern OITO_DIGITOS_DESFORMATADO = Pattern.compile(
"(\\d{2})(\\d{4})(\\d{4})"
);
private static final FormatadorBase FORMATADOR_NOVE_DIGITOS = new FormatadorBase(
NOVE_DIGITOS_FORMATADO,
"($1) $2-$3",
NOVE_DIGITOS_DESFORMATADO,
"$1$2$3"
);
private static final FormatadorBase FORMATADOR_OITO_DIGITOS = new FormatadorBase(
OITO_DIGITOS_FORMATADO,
"($1) $2-$3",
OITO_DIGITOS_DESFORMATADO,
"$1$2$3"
);
private FormatadorTelefone() {
}
static FormatadorTelefone getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public String formata(String value) {
if (ehNoveDigitos(value)) {
return FORMATADOR_NOVE_DIGITOS.formata(value);
}
return FORMATADOR_OITO_DIGITOS.formata(value);
}
@Override
public String desformata(String value) {
if (ehNoveDigitos(value)) {
return FORMATADOR_NOVE_DIGITOS.desformata(value);
}
return FORMATADOR_OITO_DIGITOS.desformata(value);
}
@Override
public boolean estaFormatado(String value) {
if (ehNoveDigitos(value)) {
return FORMATADOR_NOVE_DIGITOS.estaFormatado(value);
}
return FORMATADOR_OITO_DIGITOS.estaFormatado(value);
}
@Override
public boolean podeSerFormatado(String value) {
if (ehNoveDigitos(value)) {
return FORMATADOR_NOVE_DIGITOS.podeSerFormatado(value);
}
return FORMATADOR_OITO_DIGITOS.podeSerFormatado(value);
}
private boolean ehNoveDigitos(String value) {
if (value == null) {
throw new IllegalArgumentException("Valor não pode ser nulo");
}
return Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(value)
.replaceAll("")
.length() > 10;
}
private static class SingletonHolder {
private static final FormatadorTelefone INSTANCE = new FormatadorTelefone();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorValor.java
================================================
package br.com.concrete.canarinho.formatador;
import android.os.Build;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.Locale;
import java.util.regex.Pattern;
/**
* Formatador de valores monetários. Possui duas versões:
* <ul>
* <li>Com símbolo do Real {@link Formatador#VALOR_COM_SIMBOLO}</li>
* <li>Sem símbolo do Real {@link Formatador#VALOR}</li>
* </ul>
*/
public final class FormatadorValor implements Formatador {
private static final DecimalFormat FORMATADOR_MOEDA = (DecimalFormat)
NumberFormat.getCurrencyInstance(new Locale("pt", "BR"));
private static final Pattern PADRAO_DECIMAL = Pattern
.compile("^\\d+(\\.\\d{1,2})?$");
private static final Pattern PADRAO_MOEDA = Pattern
.compile("\\d{1,3}(\\.\\d{3})*(,\\d{2})?");
private static final String SIMBOLO_REAL = "R$ ";
static {
final DecimalFormatSymbols decimalSymbols = FORMATADOR_MOEDA.getDecimalFormatSymbols();
decimalSymbols.setCurrencySymbol("");
FORMATADOR_MOEDA.setMinimumFractionDigits(2);
FORMATADOR_MOEDA.setDecimalFormatSymbols(decimalSymbols);
FORMATADOR_MOEDA.setNegativePrefix("-");
FORMATADOR_MOEDA.setNegativeSuffix("");
FORMATADOR_MOEDA.setPositivePrefix("");
FORMATADOR_MOEDA.setParseBigDecimal(true);
}
private final boolean adicionaSimboloReal;
// No instance creation
private FormatadorValor(boolean comSimboloReal) {
adicionaSimboloReal = comSimboloReal;
}
/**
* Busca uma instância do formatador com símbolo ou sem.
*
* @param comSimboloReal Flag para saber qual instância buscar.
* @return FormatadorValor de acordo com a flag
*/
static FormatadorValor getInstance(boolean comSimboloReal) {
return comSimboloReal
? SingletonHolder.INSTANCE_COM_SIMBOLO
: SingletonHolder.INSTANCE_SEM_SIMBOLO;
}
@Override
public String formata(String value) {
final String resultado = FORMATADOR_MOEDA.format(new BigDecimal(value));
return adicionaSimboloReal ? SIMBOLO_REAL + resultado : resultado;
}
@Override
public String desformata(String value) {
String realValue = value;
if (value.startsWith(SIMBOLO_REAL)) {
int offset = value.indexOf(SIMBOLO_REAL) + SIMBOLO_REAL.length();
realValue = value.substring(offset);
}
final BigDecimal valor;
if (Build.VERSION.SDK_INT < 28) {
valor = (BigDecimal) FORMATADOR_MOEDA.parse(realValue,
new ParsePosition(0));
} else {
// Implementando o parse manual devido a um bug na API 28
valor = new BigDecimal(realValue.replaceAll("\\.", "")
.replace(",", "."));
}
return valor.toPlainString();
}
@Override
public boolean estaFormatado(String value) {
if (value == null) {
throw new IllegalArgumentException("Valor não pode ser nulo");
}
return PADRAO_MOEDA.matcher(value).matches();
}
@Override
public boolean podeSerFormatado(String value) {
if (value == null) {
throw new IllegalArgumentException("Valor não pode ser nulo");
}
return PADRAO_DECIMAL.matcher(value).matches();
}
private static class SingletonHolder {
private static final FormatadorValor INSTANCE_SEM_SIMBOLO = new FormatadorValor(false);
private static final FormatadorValor INSTANCE_COM_SIMBOLO = new FormatadorValor(true);
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/Validador.java
================================================
package br.com.concrete.canarinho.validator;
import android.text.Editable;
/**
* Interface de validação de campos. Há basicamente duas formas de validação:
* <ul>
* <li>Uma {@link String} completa</li>
* <li>Um {@link Editable} e um {@link br.com.concrete.canarinho.validator.Validador.ResultadoParcial}</li>
* </ul>
* No primeiro caso o retorno será: true ou false. No segundo caso, o resultado será sempre
* atualizado no objeto {@link br.com.concrete.canarinho.validator.Validador.ResultadoParcial} passado.
*/
public interface Validador {
/**
* Referência para o singleton de validação de CPF.
*/
Validador CPF = ValidadorCPF.getInstance();
/**
* Referência para o singleton de validação de CNPJ.
*/
Validador CNPJ = ValidadorCNPJ.getInstance();
/**
* Referência para o singleton de validação de boleto.
*/
Validador BOLETO = ValidadorBoleto.getInstance();
/**
* Referência para o singleton de validação de telefone.
*/
Validador TELEFONE = ValidadorTelefone.getInstance();
/**
* Referência para o singleton de validação de CEP.
*/
Validador CEP = ValidadorCEP.getInstance();
/**
* Valida uma {@link String} completa.
*
* @param valor Valor a ser validado
* @return true se estiver válida e false caso contrário
*/
boolean ehValido(String valor);
/**
* Valida um {@link Editable} retornando o
* {@link br.com.concrete.canarinho.validator.Validador.ResultadoParcial}.
*
* @param valor Editable
* @param resultadoParcial Objeto com o estado da validação
* @return Objeto com o estado da validação atualizado
*/
ResultadoParcial ehValido(Editable valor, ResultadoParcial resultadoParcial);
/**
* Value Object com o estado da validação.
*/
class ResultadoParcial {
private boolean valido;
private boolean parcialmenteValido = true;
private String mensagem;
public boolean isValido() {
return valido;
}
public boolean isParcialmenteValido() {
return parcialmenteValido;
}
public String getMensagem() {
return mensagem;
}
/**
* Ajusta a validação com o valor de "totalmente válido".
*
* @param valido Flag totalmenteValido
* @return Fluent Interface "this"
*/
public ResultadoParcial totalmenteValido(boolean valido) {
this.valido = valido;
return this;
}
/**
* Ajusta a validação com o valor de "totalmente válido".
*
* @param parcialmenteValido Flag parcialmenteValido
* @return Fluent Interface "this"
*/
public ResultadoParcial parcialmenteValido(boolean parcialmenteValido) {
this.parcialmenteValido = parcialmenteValido;
return this;
}
/**
* Ajusta a mensagem de erro.
*
* @param mensagem Mensagem usada na apresentação do erro.
* @return Fluent Interface "this"
*/
public ResultadoParcial mensagem(String mensagem) {
this.mensagem = mensagem;
return this;
}
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorBoleto.java
================================================
package br.com.concrete.canarinho.validator;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import br.com.concrete.canarinho.DigitoPara;
import br.com.concrete.canarinho.formatador.Formatador;
import java.util.regex.Pattern;
/**
* Implementação de @{link Validador} para boleto.
*
* @see Validador
*/
public final class ValidadorBoleto implements Validador {
/**
* Instância de módulo 10 para cálculo de digito verificador de boleto.
*/
public static final DigitoPara MOD_10 = new DigitoPara.Builder()
.mod(10)
.comMultiplicadores(2, 1)
.somandoIndividualmente()
.trocandoPorSeEncontrar("0", 10)
.complementarAoModulo()
.build();
/**
* Instância de módulo 11 para cálculo de digito verificador de boleto.
*/
public static final DigitoPara MOD_11 = new DigitoPara.Builder()
.trocandoPorSeEncontrar("0", 10, 11)
.complementarAoModulo()
.build();
private static final Pattern PADRAO_PARA_LIMPAR = Pattern.compile("[\\s.]");
private static final Pattern PADRAO_APENAS_NUMEROS = Pattern.compile("[\\d]*");
// No instance creation
private ValidadorBoleto() {
}
public static ValidadorBoleto getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public boolean ehValido(String valor) {
if (valor == null) {
throw new IllegalArgumentException("Campos não podem ser nulos");
}
String valorSemFormatacao = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(valor).replaceAll("");
return ehValido(new SpannableStringBuilder(valorSemFormatacao), new ResultadoParcial()).isValido();
}
@Override
public ResultadoParcial ehValido(Editable valor, ResultadoParcial resultadoParcial) {
if (resultadoParcial == null || valor == null) {
throw new IllegalArgumentException("Campos não podem ser nulos");
}
final String valorDesformatado = PADRAO_PARA_LIMPAR.matcher(valor).replaceAll("");
if (!PADRAO_APENAS_NUMEROS.matcher(valorDesformatado).matches()) {
throw new IllegalArgumentException("Apenas números, '.' e espaços são válidos");
}
resultadoParcial.totalmenteValido(false);
if (valorDesformatado.length() == 0) {
return resultadoParcial
.parcialmenteValido(true)
.mensagem("");
}
return ehTributo(valorDesformatado)
? validaTributo(valorDesformatado, resultadoParcial)
: validaNormal(valorDesformatado, resultadoParcial);
}
private ResultadoParcial validaNormal(String valor, ResultadoParcial resultadoParcial) {
if (!validaBloco(valor, resultadoParcial, MOD_10, 10, 0, "Primeiro")) {
return resultadoParcial;
}
if (!validaBloco(valor, resultadoParcial, MOD_10, 21, 10, "Segundo")) {
return resultadoParcial;
}
if (!validaBloco(valor, resultadoParcial, MOD_10, 32, 21, "Terceiro")) {
return resultadoParcial;
}
if (valor.length() < 47) {
return resultadoParcial;
}
return resultadoParcial.parcialmenteValido(true).totalmenteValido(true);
}
private ResultadoParcial validaTributo(String valor, ResultadoParcial resultadoParcial) {
if (valor.length() < 3) {
return resultadoParcial.parcialmenteValido(true);
}
// A validação precisa levar em conta o terceiro dígito
final boolean ehMod10 = valor.charAt(2) == '6' || valor.charAt(2) == '7';
final DigitoPara digitoPara = ehMod10 ? MOD_10 : MOD_11;
if (!validaBloco(valor, resultadoParcial, digitoPara, 12, 0, "Primeiro")) {
return resultadoParcial;
}
if (!validaBloco(valor, resultadoParcial, digitoPara, 24, 12, "Segundo")) {
return resultadoParcial;
}
if (!validaBloco(valor, resultadoParcial, digitoPara, 36, 24, "Terceiro")) {
return resultadoParcial;
}
if (!validaBloco(valor, resultadoParcial, digitoPara, 48, 36, "Quarto")) {
return resultadoParcial;
}
// Retorna bloco válido
return resultadoParcial.parcialmenteValido(true).totalmenteValido(true);
}
private boolean ehTributo(CharSequence valor) {
return valor.charAt(0) == '8';
}
private boolean validaBloco(String valor, ResultadoParcial resultadoParcial, DigitoPara mod,
int tamanhoMinimo, int st, String mensagem) {
if (valor.length() < tamanhoMinimo) {
resultadoParcial.parcialmenteValido(true);
return false;
}
final int end = tamanhoMinimo - 1;
// Valida primeiro bloco
final char digito = mod.calcula(valor.subSequence(st, end).toString()).charAt(0);
if (digito != valor.charAt(end)) {
return resultadoParcial
.mensagem(mensagem + " bloco inválido")
.parcialmenteValido(false)
.isParcialmenteValido();
}
return true;
}
private static class SingletonHolder {
private static final ValidadorBoleto INSTANCE = new ValidadorBoleto();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCEP.java
================================================
package br.com.concrete.canarinho.validator;
import android.text.Editable;
import br.com.concrete.canarinho.formatador.Formatador;
/**
* Implementação de @{link Validador} para CEP (Código de endereçamento Postal).
*
* @see Validador
*/
public final class ValidadorCEP implements Validador {
// No instance creation
private ValidadorCEP() {
}
public static ValidadorCEP getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public boolean ehValido(String valor) {
if (valor == null || valor.length() < 8) {
return false;
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(valor).replaceAll("");
return desformatado.length() == 8;
}
@Override
public ResultadoParcial ehValido(Editable valor, ResultadoParcial resultadoParcial) {
if (resultadoParcial == null || valor == null) {
throw new IllegalArgumentException("Valores não podem ser nulos");
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(valor).replaceAll("");
if (!ehValido(desformatado)) {
return resultadoParcial
.parcialmenteValido(desformatado.length() < 8)
.mensagem("CEP inválido")
.totalmenteValido(false);
}
return resultadoParcial
.parcialmenteValido(true)
.totalmenteValido(true);
}
private static class SingletonHolder {
private static final ValidadorCEP INSTANCE = new ValidadorCEP();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCNPJ.java
================================================
package br.com.concrete.canarinho.validator;
import android.text.Editable;
import br.com.concrete.canarinho.DigitoPara;
import br.com.concrete.canarinho.formatador.Formatador;
/**
* Implementação de @{link Validador} para CNPJ.
*
* @see Validador
*/
public final class ValidadorCNPJ implements Validador {
private static final DigitoPara DIGITO_PARA_CNPJ = new DigitoPara.Builder()
.complementarAoModulo()
.trocandoPorSeEncontrar("0", 10, 11)
.build();
// No instance creation
private ValidadorCNPJ() {
}
public static ValidadorCNPJ getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public boolean ehValido(String value) {
if (value == null || value.length() < 14) {
return false;
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(value).replaceAll("");
if (desformatado.length() != 14) {
return false;
}
final String cnpjSemDigitos = desformatado.substring(0, desformatado.length() - 2);
final String digitos = desformatado.substring(desformatado.length() - 2);
final String dig1 = DIGITO_PARA_CNPJ.calcula(cnpjSemDigitos);
final String dig2 = DIGITO_PARA_CNPJ.calcula(cnpjSemDigitos + dig1);
return (dig1 + dig2).equals(digitos);
}
@Override
public ResultadoParcial ehValido(Editable valor, ResultadoParcial resultadoParcial) {
if (resultadoParcial == null || valor == null) {
throw new IllegalArgumentException("Valores não podem ser nulos");
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(valor).replaceAll("");
if (!ehValido(desformatado)) {
return resultadoParcial
.parcialmenteValido(desformatado.length() < 14)
.mensagem("CNPJ inválido")
.totalmenteValido(false);
}
return resultadoParcial
.parcialmenteValido(true)
.totalmenteValido(true);
}
private static class SingletonHolder {
private static final ValidadorCNPJ INSTANCE = new ValidadorCNPJ();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCPF.java
================================================
package br.com.concrete.canarinho.validator;
import android.text.Editable;
import br.com.concrete.canarinho.DigitoPara;
import br.com.concrete.canarinho.formatador.Formatador;
/**
* Implementação de @{link Validador} para CPF.
*
* @see Validador
*/
public final class ValidadorCPF implements Validador {
private static final DigitoPara DIGITO_PARA_CPF = new DigitoPara.Builder()
.comMultiplicadoresDeAte(2, 11)
.complementarAoModulo()
.trocandoPorSeEncontrar("0", 10, 11)
.build();
// No instance creation
private ValidadorCPF() {
}
static ValidadorCPF getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public boolean ehValido(String value) {
if (value == null || value.length() < 11) {
return false;
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(value).replaceAll("");
if (desformatado.length() != 11) {
return false;
}
if (estaNaListaNegra(desformatado)) {
return false;
}
final String cpfSemDigito = desformatado.substring(0, desformatado.length() - 2);
final String digitos = desformatado.substring(desformatado.length() - 2);
final String dig1 = DIGITO_PARA_CPF.calcula(cpfSemDigito);
final String dig2 = DIGITO_PARA_CPF.calcula(cpfSemDigito + dig1);
return (dig1 + dig2).equals(digitos);
}
@Override
public ResultadoParcial ehValido(Editable valor, ResultadoParcial resultadoParcial) {
if (resultadoParcial == null || valor == null) {
throw new IllegalArgumentException("Valores não podem ser nulos");
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(valor).replaceAll("");
if (!ehValido(desformatado)) {
return resultadoParcial
.parcialmenteValido(desformatado.length() < 11)
.mensagem("CPF inválido")
.totalmenteValido(false);
}
return resultadoParcial
.parcialmenteValido(true)
.totalmenteValido(true);
}
// De acordo ao cálculo dos digitos verificadores, os CPFs abaixo são válidos, entretanto os mesmo
// são considerados inválidos pela Receita Federal
// 00000000000, 11111111111, 22222222222, 33333333333, 44444444444, 55555555555,
// 66666666666, 77777777777, 88888888888, 99999999999, 12345678909
private boolean estaNaListaNegra(String valor) {
boolean igual = true;
for (int i = 1; i < 11 && igual; i++) {
if (valor.charAt(i) != valor.charAt(0)) {
igual = false;
}
}
return igual || valor.equals("12345678909");
}
private static class SingletonHolder {
private static final ValidadorCPF INSTANCE = new ValidadorCPF();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCPFCNPJ.java
================================================
package br.com.concrete.canarinho.validator;
import android.text.Editable;
import br.com.concrete.canarinho.formatador.Formatador;
public final class ValidadorCPFCNPJ implements Validador {
// No instance creation
private ValidadorCPFCNPJ() {
}
public static ValidadorCPFCNPJ getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public boolean ehValido(final String valor) {
if (valor == null || (valor.length() != 11 && valor.length() != 14)) {
return false;
}
if (ehCpf(valor)) {
return ValidadorCPF.getInstance().ehValido(valor);
}
return ValidadorCNPJ.getInstance().ehValido(valor);
}
@Override
public ResultadoParcial ehValido(final Editable valor, final ResultadoParcial resultadoParcial) {
if (resultadoParcial == null || valor == null) {
throw new IllegalArgumentException("Valores não podem ser nulos");
}
if (ehCpf(valor.toString())) {
return ValidadorCPF.getInstance().ehValido(valor, resultadoParcial);
}
return ValidadorCNPJ.getInstance().ehValido(valor, resultadoParcial);
}
private boolean ehCpf(String valor) {
if (valor == null) {
throw new IllegalArgumentException("Valor não pode ser nulo");
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(valor).replaceAll("");
return desformatado.length() < 12;
}
private static class SingletonHolder {
private static final ValidadorCPFCNPJ INSTANCE = new ValidadorCPFCNPJ();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorTelefone.java
================================================
package br.com.concrete.canarinho.validator;
import android.text.Editable;
import br.com.concrete.canarinho.formatador.Formatador;
public final class ValidadorTelefone implements Validador {
// No instance creation
private ValidadorTelefone() {
}
public static ValidadorTelefone getInstance() {
return SingletonHolder.INSTANCE;
}
@Override
public boolean ehValido(String valor) {
if (valor == null || valor.length() < 10) {
return false;
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(valor).replaceAll("");
return desformatado.length() == 10 || desformatado.length() == 11;
}
@Override
public ResultadoParcial ehValido(Editable valor, ResultadoParcial resultadoParcial) {
if (resultadoParcial == null || valor == null) {
throw new IllegalArgumentException("Valores não podem ser nulos");
}
final String desformatado = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(valor).replaceAll("");
if (!ehValido(desformatado)) {
return resultadoParcial
.parcialmenteValido(desformatado.length() < 11)
.mensagem("Telefone inválido")
.totalmenteValido(false);
}
return resultadoParcial
.parcialmenteValido(true)
.totalmenteValido(true);
}
private static class SingletonHolder {
private static final ValidadorTelefone INSTANCE = new ValidadorTelefone();
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/BaseCanarinhoTextWatcher.java
================================================
package br.com.concrete.canarinho.watcher;
import android.text.Editable;
import android.text.Selection;
import android.text.TextWatcher;
import br.com.concrete.canarinho.formatador.Formatador;
import br.com.concrete.canarinho.validator.Validador;
import br.com.concrete.canarinho.watcher.evento.EventoDeValidacao;
/**
* Classe base para Watchers que possuem máscara e efetuam validação.
*
* @see Validador
*/
public abstract class BaseCanarinhoTextWatcher implements TextWatcher {
private boolean mudancaInterna = false;
private int tamanhoAnterior = 0;
private EventoDeValidacao eventoDeValidacao;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Não faz nada aqui
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Não faz nada aqui
}
public boolean isMudancaInterna() {
return mudancaInterna;
}
@SuppressWarnings("unchecked")
public <T extends EventoDeValidacao> T getEventoDeValidacao() {
return (T) eventoDeValidacao;
}
public void setEventoDeValidacao(EventoDeValidacao eventoDeValidacao) {
this.eventoDeValidacao = eventoDeValidacao;
}
/**
* Utilitário para implementações de Watcher customizadas.
* Verifica se a ação foi de apagar um caracter
*
* @param s o Editable em uso
* @return True case a ação foi uma deleção e false caso contrário
*/
protected boolean isApagouCaracter(Editable s) {
return tamanhoAnterior > s.length();
}
/**
* Utilitário para implementações de Watcher customizadas.
* Utilitário para atualizar o Editable com flags de atualização.
*
* @param validador Validador utilizado para verificar o input
* @param resultadoParcial Objeto de validação
* @param s Editable em uso
* @param builder Valor atual da string
*/
// Usa o Editable para atualizar o Editable
// O cursor SEMPRE sera posicionado no final do conteúdo
protected void atualizaTexto(Validador validador, Validador.ResultadoParcial resultadoParcial,
Editable s, StringBuilder builder) {
tamanhoAnterior = builder.length();
mudancaInterna = true;
s.replace(0, s.length(), builder, 0, builder.length());
if (builder.toString().equals(s.toString())) {
// TODO: estudar implantar a manutenção da posição do cursor
Selection.setSelection(s, builder.length());
}
efetuaValidacao(validador, resultadoParcial, s);
mudancaInterna = false;
}
/**
* Método que efetua a validação em si.
*
* @param validador Validador utilizado para verificar o input
* @param resultadoParcial Objeto de validação
* @param s Editable em uso
*/
// CUIDADO AO ATUALIZAR O Editable AQUI!!!
protected void efetuaValidacao(Validador validador, Validador.ResultadoParcial resultadoParcial, Editable s) {
if (validador == null) {
return;
}
if (eventoDeValidacao == null) {
validador.ehValido(s.toString());
return;
}
validador.ehValido(s, resultadoParcial);
if (!resultadoParcial.isParcialmenteValido()) {
eventoDeValidacao.invalido(s.toString(), resultadoParcial.getMensagem());
} else if (!resultadoParcial.isValido()) {
eventoDeValidacao.parcialmenteValido(s.toString());
} else {
eventoDeValidacao.totalmenteValido(s.toString());
}
}
/**
* Implementação genérica para adição ou remoção de caracter.
*
* @param s Editable em uso
* @param mascara máscara do Watcher
* @return Builder com o valor final
*/
protected StringBuilder trataAdicaoRemocaoDeCaracter(Editable s, char[] mascara) {
return isApagouCaracter(s)
? trataRemocaoDeCaracter(s, mascara)
: trataAdicaoDeCaracter(s, mascara);
}
private StringBuilder trataAdicaoDeCaracter(Editable s, char[] mascara) {
return carregarMascara(s.toString(), mascara);
}
// Só é chamado após uma deleção, portanto, é seguro chamar mascara[s.length()]
private StringBuilder trataRemocaoDeCaracter(Editable s, char[] mascara) {
final StringBuilder builder = new StringBuilder(s);
// Obtém a posição do último caracter excluído
final int posicaoUltimoCaracter = mascara.length > s.length() ? s.length() : mascara.length - 1;
// Verifica se o último caracter que foi excluído fazia parte da máscara
final boolean ultimoCaracterEraMascara = mascara[posicaoUltimoCaracter] != '#';
// Se o último caracter excluído fazia parte da máscara,
// deve excluir até o primeiro caracter que não faz parte da máscara
if (ultimoCaracterEraMascara) {
boolean encontrouCaracterValido = false;
while (builder.length() > 0 && !encontrouCaracterValido) {
encontrouCaracterValido = mascara[builder.length() - 1] == '#';
builder.deleteCharAt(builder.length() - 1);
}
}
// Caso haja mais de um caracter de formatação (da máscara) faz um loop
// até chegar em um caracter que não seja de formatação
while (builder.length() > 0 && mascara[builder.length() - 1] != '#') {
builder.deleteCharAt(builder.length() - 1);
}
return carregarMascara(builder.toString(), mascara);
}
private StringBuilder carregarMascara(String s, char[] mascara) {
final StringBuilder builder = new StringBuilder();
final String str = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(s).replaceAll("");
// Só carregará a máscara se existir algum valor informado
if (str.length() > 0) {
int j = 0; // Acompanha a posição nos dígitos
// É recomendado não usar enhanced for em Android
for (int i = 0; i < mascara.length; i++) {
final char charMascara = mascara[i];
if (charMascara != '#') { // '#' -> caracter de formatação
builder.append(charMascara);
continue;
}
if (j >= str.length()) {
break;
}
builder.append(str.charAt(j));
j++;
}
}
return builder;
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/BoletoBancarioTextWatcher.java
================================================
package br.com.concrete.canarinho.watcher;
import android.text.Editable;
import android.text.InputFilter;
import br.com.concrete.canarinho.validator.Validador;
import br.com.concrete.canarinho.validator.ValidadorBoleto;
import br.com.concrete.canarinho.watcher.evento.EventoDeValidacao;
import br.com.concrete.canarinho.watcher.evento.EventoDeValidacaoDeBoleto;
import java.util.Arrays;
/**
* {@link android.text.TextWatcher} responsável por formatar e validar um {@link
* android.widget.EditText} para boletos. Para usar este componente basta criar uma instância e
* chamar {@link android.widget.EditText#addTextChangedListener(android.text.TextWatcher)}.
*/
public final class BoletoBancarioTextWatcher extends BaseCanarinhoTextWatcher {
private static final char[] BOLETO_NORMAL = "#####.##### #####.###### #####.###### # ##############".toCharArray();
private static final char[] BOLETO_TRIBUTO = "############ ############ ############ ############".toCharArray();
private static final InputFilter[] FILTRO_TRIBUTO = new InputFilter[]{
new InputFilter.LengthFilter(BOLETO_TRIBUTO.length)};
private static final InputFilter[] FILTRO_NORMAL = new InputFilter[]{
new InputFilter.LengthFilter(BOLETO_NORMAL.length)};
private final Validador validador = ValidadorBoleto.getInstance();
private final Validador.ResultadoParcial resultadoParcial = new Validador.ResultadoParcial();
/**
* TODO Javadoc pendente.
*
* @param callbackErros a descrever
*/
public BoletoBancarioTextWatcher(EventoDeValidacao callbackErros) {
setEventoDeValidacao(callbackErros);
}
@Override
public final void afterTextChanged(Editable s) {
// retorna se a String é menor que o mínimo de caracteres
// para haver uma formatação ou se a mudança foi disparada
// pelo método atualizaTexto
if (isMudancaInterna()) {
return;
}
// Trata o caso em que tudo é apagado em lote
if (s.length() < 3) {
resultadoParcial.mensagem(null).parcialmenteValido(false).totalmenteValido(false);
if (getEventoDeValidacao() != null) {
getEventoDeValidacao().parcialmenteValido("");
}
}
if (s.length() == 0) {
verificaFiltro(s, false);
return;
}
final boolean tributo = ehTributo(s);
final char[] mascara = tributo ? BOLETO_TRIBUTO : BOLETO_NORMAL;
verificaFiltro(s, tributo);
// Trata deleção e adição de forma diferente (só formata em adições)
final StringBuilder builder = trataAdicaoRemocaoDeCaracter(s, mascara);
atualizaTexto(validador, resultadoParcial, s, builder);
}
public Validador.ResultadoParcial getResultadoParcial() {
return resultadoParcial;
}
@Override
protected void efetuaValidacao(Validador validador, Validador.ResultadoParcial resultadoParcial, Editable s) {
validador.ehValido(s, resultadoParcial);
final EventoDeValidacao callbackErros = getEventoDeValidacao();
if (callbackErros == null) {
return;
}
final String valorAtual = s.toString();
if (!resultadoParcial.isParcialmenteValido()) {
final String mensagem = resultadoParcial.getMensagem();
callbackErros.invalido(valorAtual, mensagem);
if (callbackErros instanceof EventoDeValidacaoDeBoleto) {
final int bloco;
if (mensagem.startsWith("Primeiro")) {
bloco = 1;
} else if (mensagem.startsWith("Segundo")) {
bloco = 2;
} else if (mensagem.startsWith("Terceiro")) {
bloco = 3;
} else if (mensagem.startsWith("Quarto")) {
bloco = 4;
} else {
throw new IllegalArgumentException("Valor não reconhecido para bloco");
}
((EventoDeValidacaoDeBoleto) callbackErros).invalido(valorAtual, bloco);
}
} else if (!resultadoParcial.isValido()) {
callbackErros.parcialmenteValido(valorAtual);
} else {
callbackErros.totalmenteValido(valorAtual);
}
}
private void verificaFiltro(final Editable s, final boolean tributo) {
// Filtro de tamanho
if (tributo && !Arrays.equals(s.getFilters(), FILTRO_TRIBUTO)) {
s.setFilters(FILTRO_TRIBUTO);
} else if (!tributo && !Arrays.equals(s.getFilters(), FILTRO_NORMAL)) {
s.setFilters(FILTRO_NORMAL);
}
}
// Boletos iniciados com 8 são tributos ou de concessionárias
private boolean ehTributo(Editable e) {
return e.charAt(0) == '8';
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/CEPTextWatcher.java
================================================
package br.com.concrete.canarinho.watcher;
import android.text.Editable;
import android.text.InputFilter;
import br.com.concrete.canarinho.validator.Validador;
import br.com.concrete.canarinho.validator.ValidadorCEP;
import br.com.concrete.canarinho.watcher.evento.EventoDeValidacao;
/**
* {@link android.text.TextWatcher} responsável por formatar e validar um {@link android.widget.EditText} para CEPs.
* Para usar este componente basta criar uma instância e chamar
* {@link android.widget.EditText#addTextChangedListener(android.text.TextWatcher)}.
*/
public final class CEPTextWatcher extends BaseCanarinhoTextWatcher {
private static final char[] CEP_DIGITOS = "#####-###".toCharArray();
private static final InputFilter[] FILTRO_OITO_DIGITOS = new InputFilter[]{
new InputFilter.LengthFilter(CEP_DIGITOS.length)};
private final Validador validador = ValidadorCEP.getInstance();
private final Validador.ResultadoParcial resultadoParcial = new Validador.ResultadoParcial();
/**
* Inicializa o validador com um callback de erros.
*
* @param callbackErros Instância que será chamada quando houverem erros.
*/
public CEPTextWatcher(EventoDeValidacao callbackErros) {
setEventoDeValidacao(callbackErros);
}
@Override
public void afterTextChanged(Editable s) {
if (isMudancaInterna()) {
return;
}
s.setFilters(FILTRO_OITO_DIGITOS);
final StringBuilder builder = trataAdicaoRemocaoDeCaracter(s, CEP_DIGITOS);
atualizaTexto(validador, resultadoParcial, s, builder);
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/CPFCNPJTextWatcher.java
================================================
package br.com.concrete.canarinho.watcher;
import android.text.Editable;
import android.text.InputFilter;
import br.com.concrete.canarinho.formatador.Formatador;
import br.com.concrete.canarinho.validator.Validador;
import br.com.concrete.canarinho.validator.ValidadorCPFCNPJ;
import br.com.concrete.canarinho.watcher.evento.EventoDeValidacao;
/**
* {@link android.text.TextWatcher} responsável por formatar e validar um
* {@link android.widget.EditText} para CPF / CNPJ.
* Para usar este componente basta criar uma instância e chamar
* {@link android.widget.EditText#addTextChangedListener(android.text.TextWatcher)}.
*/
public class CPFCNPJTextWatcher extends BaseCanarinhoTextWatcher {
private static final char[] CPF = "###.###.###-##".toCharArray();
private static final char[] CNPJ = "##.###.###/####-##".toCharArray();
private static final InputFilter[] FILTRO_CPF_CNPJ = new InputFilter[]{new InputFilter.LengthFilter(CNPJ.length)};
private final Validador validador = ValidadorCPFCNPJ.getInstance();
private final Validador.ResultadoParcial resultadoParcial = new Validador.ResultadoParcial();
/**
* TODO Javadoc pendente.
*/
public CPFCNPJTextWatcher() {
}
/**
* TODO Javadoc pendente.
*
* @param callbackErros a descrever
*/
public CPFCNPJTextWatcher(EventoDeValidacao callbackErros) {
setEventoDeValidacao(callbackErros);
}
@Override
public void afterTextChanged(final Editable s) {
if (isMudancaInterna()) {
return;
}
s.setFilters(FILTRO_CPF_CNPJ);
final char[] mascara = ehCpf(s) ? CPF : CNPJ;
final StringBuilder builder = trataAdicaoRemocaoDeCaracter(s, mascara);
atualizaTexto(validador, resultadoParcial, s, builder);
}
// Verifica se o valor informado é cpf
private boolean ehCpf(Editable e) {
return Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(e).replaceAll("").length() < 12;
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/MascaraNumericaTextWatcher.java
================================================
package br.com.concrete.canarinho.watcher;
import android.text.Editable;
import android.text.InputFilter;
import br.com.concrete.canarinho.validator.Validador;
import br.com.concrete.canarinho.watcher.evento.EventoDeValidacao;
import java.util.Arrays;
/**
* Máscara c/ validação genérica para campos numéricos.
*
* @see br.com.concrete.canarinho.watcher.MascaraNumericaTextWatcher.Builder
*/
public final class MascaraNumericaTextWatcher extends BaseCanarinhoTextWatcher {
private final Validador.ResultadoParcial resultadoParcial = new Validador.ResultadoParcial();
private final Validador validador;
private final char[] mascara;
private final InputFilter[] filtroNumerico;
/**
* Construtor para adicionar uma máscara sem validação.
*
* @param mascara Máscara para efetuar a formatação
*/
public MascaraNumericaTextWatcher(String mascara) {
this(new Builder().paraMascara(mascara));
}
private MascaraNumericaTextWatcher(Builder builder) {
this.mascara = builder.mascara.toCharArray();
this.validador = builder.validador;
final int length = mascara.length;
this.filtroNumerico = new InputFilter[]{new InputFilter.LengthFilter(length)};
setEventoDeValidacao(builder.eventoDeValidacao);
}
@Override
public void afterTextChanged(Editable s) {
// retorna se a mudança foi disparada pelo método atualizaTexto
if (isMudancaInterna()) {
return;
}
// Filtro de tamanho
if (!Arrays.equals(s.getFilters(), filtroNumerico)) {
s.setFilters(filtroNumerico);
}
final StringBuilder builder = trataAdicaoRemocaoDeCaracter(s, mascara);
atualizaTexto(validador, resultadoParcial, s, builder);
}
/**
* Builder para construção de máscaras que validam.
*/
public static final class Builder {
private Validador validador;
private EventoDeValidacao eventoDeValidacao;
private String mascara;
/**
* O validador que será usado. Será chamada a implementação de
* {@link Validador#ehValido(Editable, Validador.ResultadoParcial)}
*
* @param validador Implementação de {@link Validador}
* @return this para interface fluente
*/
public Builder comValidador(Validador validador) {
this.validador = validador;
return this;
}
/**
* Para cada caracter digitado será validado de acordo com o Validador e o callback
* correspondente ao resultado da validação será chamado para que a interface possa ser atualizada.
*
* @param callbackErros {@link EventoDeValidacao} que será chamado durante a validação
* @return this para interface fluente
*/
public Builder comCallbackDeValidacao(EventoDeValidacao callbackErros) {
this.eventoDeValidacao = callbackErros;
return this;
}
/**
* A máscara só pode conter os caracteres '#' no lugar dos números. Assim, a máscara
* '#####-##' irá aceitar apenas números no lugar de '#'. Ao digitar, o usuário irá ver:
* '12345-67'.
*
* @param mascara Máscara
* @return this para interface fluente
*/
public Builder paraMascara(String mascara) {
this.mascara = mascara;
return this;
}
/**
* Constrói a máscara.
*
* @return A instância imutável da máscara.
*/
public final MascaraNumericaTextWatcher build() {
if (mascara == null || mascara.isEmpty() || !mascara.contains("#")) {
throw new IllegalArgumentException("Máscara precisa conter ao menos um caracter '#'");
}
return new MascaraNumericaTextWatcher(this);
}
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/TelefoneTextWatcher.java
================================================
package br.com.concrete.canarinho.watcher;
import android.text.Editable;
import android.text.InputFilter;
import br.com.concrete.canarinho.formatador.Formatador;
import br.com.concrete.canarinho.validator.Validador;
import br.com.concrete.canarinho.validator.ValidadorTelefone;
import br.com.concrete.canarinho.watcher.evento.EventoDeValidacao;
/**
* {@link android.text.TextWatcher} responsável por formatar e validar um
* {@link android.widget.EditText} para telefones.
* Para usar este componente basta criar uma instância e chamar
* {@link android.widget.EditText#addTextChangedListener(android.text.TextWatcher)}.
*/
public final class TelefoneTextWatcher extends BaseCanarinhoTextWatcher {
private static final char[] TELEFONE_OITO_DIGITOS = "(##) ####-####".toCharArray();
private static final char[] TELEFONE_NOVE_DIGITOS = "(##) #####-####".toCharArray();
private static final InputFilter[] FILTRO_NOVE_DIGITOS = new InputFilter[]{
new InputFilter.LengthFilter(TELEFONE_NOVE_DIGITOS.length)};
private final Validador validador = ValidadorTelefone.getInstance();
private final Validador.ResultadoParcial resultadoParcial = new Validador.ResultadoParcial();
/**
* TODO Javadoc pendente.
*
* @param callbackErros a descrever
*/
public TelefoneTextWatcher(EventoDeValidacao callbackErros) {
setEventoDeValidacao(callbackErros);
}
@Override
public void afterTextChanged(Editable s) {
if (isMudancaInterna()) {
return;
}
s.setFilters(FILTRO_NOVE_DIGITOS);
final char[] mascara = ehNoveDigitos(s) ? TELEFONE_NOVE_DIGITOS : TELEFONE_OITO_DIGITOS;
final StringBuilder builder = trataAdicaoRemocaoDeCaracter(s, mascara);
atualizaTexto(validador, resultadoParcial, s, builder);
}
// Verifica se o telefone possui 9 dígitos
private boolean ehNoveDigitos(Editable e) {
return Formatador.Padroes.PADRAO_SOMENTE_NUMEROS.matcher(e).replaceAll("").length() > 10;
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/ValorMonetarioWatcher.java
================================================
package br.com.concrete.canarinho.watcher;
import android.text.Editable;
import android.text.InputFilter;
import android.text.Selection;
import android.text.TextWatcher;
import br.com.concrete.canarinho.formatador.Formatador;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* TextWatcher para valores monetários.
*/
public class ValorMonetarioWatcher implements TextWatcher {
private final boolean mantemZerosAoLimpar;
private final Formatador formatador;
private boolean mudancaInterna;
/**
* Constrói uma instância sem símbolo de Real (R$).
*/
public ValorMonetarioWatcher() {
this(false, true);
}
/**
* Constrói uma instância com opção de símbolo de Real (R$).
*
* @param comSimboloReal Flag para acrescentar ou não o símbolo
* @param mantemZerosAoLimpar Sempre que não houver números (apagar em lote) manter zeros
*/
ValorMonetarioWatcher(boolean comSimboloReal, boolean mantemZerosAoLimpar) {
this.formatador = comSimboloReal
? Formatador.VALOR_COM_SIMBOLO
: Formatador.VALOR;
this.mantemZerosAoLimpar = mantemZerosAoLimpar;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Não faz nada aqui
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Não faz nada aqui
}
@Override
public void afterTextChanged(Editable s) {
if (mudancaInterna) {
return;
}
final String somenteNumeros = Formatador.Padroes.PADRAO_SOMENTE_NUMEROS
.matcher(s.toString())
.replaceAll("");
// afterTextChanged é chamado ao rotacionar o dispositivo,
// essa condição evita que ao rotacionar a tela com o campo vazio ocorra NumberFormatException
if (somenteNumeros.length() == 0) {
if (mantemZerosAoLimpar) {
atualizaTexto(s, formatador.formata("000"));
}
return;
}
final BigDecimal resultado = new BigDecimal(somenteNumeros)
.divide(new BigDecimal(100))
.setScale(2, RoundingMode.HALF_DOWN);
atualizaTexto(s, formatador.formata(resultado.toPlainString()));
}
private void atualizaTexto(Editable editable, String valor) {
mudancaInterna = true;
final InputFilter[] oldFilters = editable.getFilters();
editable.setFilters(new InputFilter[] {});
editable.replace(0, editable.length(), valor);
editable.setFilters(oldFilters);
if (valor.equals(editable.toString())) {
// TODO: estudar implantar a manutenção da posição do cursor
Selection.setSelection(editable, valor.length());
}
mudancaInterna = false;
}
/**
* Builder para facilitar a construção de instâncias de {@link ValorMonetarioWatcher}.
*/
public static class Builder {
private boolean mantemZerosAoLimpar;
private boolean simboloReal;
/**
* Manterá os zeros ao limpar o campo.
*
* @return this Fluent interface
*/
public Builder comMantemZerosAoLimpar() {
this.mantemZerosAoLimpar = true;
return this;
}
/**
* Inclui o símbolo de real na formatação.
*
* @return this Fluent interface
*/
public Builder comSimboloReal() {
this.simboloReal = true;
return this;
}
/**
* Constrói a instância.
*
* @return Watcher para ser usado
*/
public ValorMonetarioWatcher build() {
return new ValorMonetarioWatcher(simboloReal, mantemZerosAoLimpar);
}
}
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/evento/EventoDeValidacao.java
================================================
package br.com.concrete.canarinho.watcher.evento;
/**
* Interface para quem estiver usando este TextWatcher poder ter uma ação quando um erro de validação acontecer.
* Os métodos desta interface serão chamados quando for digitado um NOVO caracter e quando for APAGADO um caracter.
*/
public interface EventoDeValidacao {
/**
* Invocado quando os números digitados estão inválidos. Pode ser apenas um trecho ou o número completo.
*
* @param valorAtual O valor após a digitação.
* @param mensagem A mensagem de erro da validação.
*/
void invalido(String valorAtual, String mensagem);
/**
* Invocado quando os números digitados estão parcialmente válidos. Quando o número estiver completamente válido
* será chamado o callback {@link #totalmenteValido(String)}.
*
* @param valorAtual O valor após a digitação.
*/
void parcialmenteValido(String valorAtual);
/**
* Invocado quando a máscara está completa e os números são válidos.
*
* @param valorAtual O valor após a digitação.
*/
void totalmenteValido(String valorAtual);
}
================================================
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/evento/EventoDeValidacaoDeBoleto.java
================================================
package br.com.concrete.canarinho.watcher.evento;
/**
* Evento de validação específico para boletos que permite saber qual o bloco que
* contém caracteres inválidos.
*/
public interface EventoDeValidacaoDeBoleto extends EventoDeValidacao {
/**
* Invocado quando os números digitados estão inválidos. Pode ser apenas um trecho ou o número completo.
*
* @param valorAtual O valor após a digitação.
* @param blocoInvalido O bloco com valor inválido
*/
void invalido(String valorAtual, int blocoInvalido);
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
================================================
FILE: gradle.properties
================================================
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.parallel=true
android.useAndroidX=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: sample/.gitignore
================================================
/build
================================================
FILE: sample/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'jacoco'
android {
compileSdkVersion 31
defaultConfig {
applicationId 'br.com.concrete.canarinho.sample'
minSdkVersion 21
targetSdkVersion 31
versionCode 1
versionName '1.0'
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
buildTypes {
debug {
testCoverageEnabled true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
testOptions {
execution 'ANDROIDX_TEST_ORCHESTRATOR'
unitTests {
includeAndroidResources = true
}
}
}
dependencies {
implementation project(':canarinho')
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
androidTestUtil 'androidx.test:orchestrator:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'androidx.test:core:1.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
testImplementation 'junit:junit:4.13.2'
testImplementation "org.robolectric:robolectric:4.7.3"
testImplementation 'androidx.test:core:1.3.0'
testImplementation 'androidx.test.ext:junit:1.1.2'
}
jacoco {
toolVersion = '0.8.3'
}
task fullCoverageReport(type: JacocoReport) {
dependsOn 'createDebugCoverageReport'
dependsOn 'testDebugUnitTest'
reports {
xml.enabled true
html.enabled true
}
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*',
'**/*Test*.*', 'android/**/*.*']
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
sourceDirectories.from = files([mainSrc])
classDirectories.from = files([debugTree])
executionData.from = fileTree(dir: "$buildDir", includes: [
"jacoco/testDebugUnitTest.exec",
"outputs/code_coverage/connected/*coverage.ec"
])
}
================================================
FILE: sample/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/victornascimento/Android/Sdk/linters/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: sample/src/androidTest/java/br/com/concrete/canarinho/sample/BugOnApi28Test.java
================================================
package br.com.concrete.canarinho.sample;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
import org.junit.Test;
import org.junit.runner.RunWith;
import br.com.concrete.canarinho.formatador.Formatador;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class BugOnApi28Test {
@Test
@SdkSuppress(minSdkVersion = 28)
public void moneyFormatSuccessfulRunsOnApi28() {
final String value = "1.000.000,00";
assertEquals("1000000.00", Formatador.VALOR.desformata(value));
}
@Test
@SdkSuppress(maxSdkVersion = 27)
public void moneyFormatSuccessfulRunsOnApi27() {
final String value = "1.000.000,00";
assertEquals("1000000.00", Formatador.VALOR.desformata(value));
}
}
================================================
FILE: sample/src/androidTest/java/br/com/concrete/canarinho/sample/DemoWatchersInstrumentationTest.java
================================================
package br.com.concrete.canarinho.sample;
import android.os.Build;
import android.view.View;
import android.widget.EditText;
import android.widget.SearchView;
import org.hamcrest.Matcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.rule.ActivityTestRule;
import br.com.concrete.canarinho.sample.ui.activity.MainActivity;
import br.com.concrete.canarinho.sample.ui.model.Watchers;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.clearText;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.pressBack;
import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.supportsInputMethods;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anyOf;
@RunWith(AndroidJUnit4.class)
public class DemoWatchersInstrumentationTest {
@Rule
public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
@Test
public void consegueDigitarUmBoletoNormalValido() {
navigateToTab(Watchers.BOLETO_BANCARIO);
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(typeText("34199310130010011560900500990007800000000000000"));
onView(withText("Campo válido!")).check(matches(isDisplayed()));
}
@Test
public void consegueValidarUmBoletoSetandoOCodigoInteiro() {
navigateToTab(Watchers.BOLETO_BANCARIO);
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(paste("34199310130010011560900500990007800000000000000"));
onView(withText("Campo válido!")).check(matches(isDisplayed()));
}
@Test
public void consegueValidarUmBoletoTributoSetandoOCodigoInteiro() {
navigateToTab(Watchers.BOLETO_BANCARIO);
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(paste("812345678901812345678901812345678901812345678901"));
onView(withText("Campo válido!")).check(matches(isDisplayed()));
}
@Test
public void consegueDigitarUmBoletoNormalComBlocosInvalidos() {
navigateToTab(Watchers.BOLETO_BANCARIO);
// primeiro
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(typeText("23790125016"));
onView(withText("Primeiro bloco inválido")).check(matches(isDisplayed()));
// segundo
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(clearText(), typeText("2379012301600000030054"));
onView(withText("Segundo bloco inválido")).check(matches(isDisplayed()));
// terceiro
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(clearText(), typeText("23790123016000000005325000456708"));
onView(withText("Terceiro bloco inválido")).check(matches(isDisplayed()));
}
@Test
public void consegueDigitarUmBoletoTributoValido() {
navigateToTab(Watchers.BOLETO_BANCARIO);
// Boleto válido
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(typeText("848600000015523301622010506101307129620012111220"));
onView(withText("Campo válido!"))
.check(matches(isDisplayed()))
.perform(pressBack());
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(clearText(), typeText("836600000019078800481000998854924516001265611135"));
onView(withText("Campo válido!"))
.check(matches(isDisplayed()))
.perform(pressBack());
}
@Test
public void consegueDigitarUmBoletoNormalComBlocosInvalidosComMensagemCustomizada() {
navigateToTab(Watchers.BOLETO_BANCARIO_MSG_CUSTOM);
// primeiro
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(typeText("23790125016"));
onView(withText("Primeira mensagem")).check(matches(isDisplayed()));
// segundo
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(clearText(), typeText("2379012301600000030054"));
onView(withText("Segunda mensagem")).check(matches(isDisplayed()));
// terceiro
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(clearText(), typeText("23790123016000000005325000456708"));
onView(withText("Terceira mensagem")).check(matches(isDisplayed()));
}
@Test
public void consegueDigitarUmCPFValido() {
navigateToTab(Watchers.CPF);
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(typeText("46574356636"));
onView(withText("Campo válido!"))
.check(matches(isDisplayed()))
.perform(pressBack());
}
@Test
public void consegueDigitarUmCPFInvalido() {
navigateToTab(Watchers.CPF);
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(typeText("46574356637"));
onView(withText("CPF inválido")).check(matches(isDisplayed()));
}
@Test
public void consegueDigitarUmCNPJValido() {
navigateToTab(Watchers.CNPJ);
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(typeText("95621433000170"));
onView(withText("Campo válido!"))
.check(matches(isDisplayed()))
.perform(pressBack());
}
@Test
public void consegueDigitarUmCNPJInvalido() {
navigateToTab(Watchers.CNPJ);
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(typeText("95621433000180"));
onView(withText("CNPJ inválido")).check(matches(isDisplayed()));
}
@Test
public void consegueDigitarUmTelefoneValido() {
navigateToTab(Watchers.TELEFONE);
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(typeText("1112345678"));
onView(withText("Campo válido!"))
.check(matches(isDisplayed()))
.perform(pressBack());
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(typeText("11123456789"));
onView(withText("Campo válido!"))
.check(matches(isDisplayed()))
.perform(pressBack());
}
@Test
public void consegueDigitarUmValorMonetarioFormatado() throws InterruptedException {
navigateToTab(Watchers.VALOR_MONETARIO);
Thread.sleep(200L);
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(typeText("1"))
.check(matches(withText("R$ 0,01"))) // inicia com 0,0X
.perform(typeText("2"))
.check(matches(withText("R$ 0,12"))) // 0,XY
.perform(typeText("3"))
.check(matches(withText("R$ 1,23"))) // X,YZ
.perform(typeText("4"))
.check(matches(withText("R$ 12,34"))) // etc, etc, etc
.perform(typeText("5"))
.check(matches(withText("R$ 123,45")))
.perform(typeText("6"))
.check(matches(withText("R$ 1.234,56")))
.perform(typeText("7"))
.check(matches(withText("R$ 12.345,67")))
.perform(typeText("8"))
.check(matches(withText("R$ 123.456,78")))
.perform(typeText("9"))
.check(matches(withText("R$ 1.234.567,89")))
.perform(clearText())
.check(matches(withText("R$ 0,00")));
}
@Test
public void consegueDigitarCPFCNPJValido() {
navigateToTab(Watchers.CPF_CNPJ);
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(typeText("46574356636"));
onView(withText("Campo válido!")).check(matches(isDisplayed())).perform(pressBack());
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(clearText(), typeText("95621433000170"));
onView(withText("Campo válido!")).check(matches(isDisplayed()));
}
@Test
public void consegueDigitarCPFCNPJInvalido() {
navigateToTab(Watchers.CPF_CNPJ);
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(typeText("46574356637"));
onView(withText("CPF inválido")).check(matches(isDisplayed()));
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(clearText(), typeText("15621433000180"));
onView(withText("CNPJ inválido")).check(matches(isDisplayed()));
}
@Test
public void consegueDigitarUmCEPValido() {
navigateToTab(Watchers.CEP);
onView(allOf(withId(R.id.edit_text), isDisplayed())).perform(typeText("49025090"));
onView(withText("Campo válido!"))
.check(matches(isDisplayed()));
}
@Test
public void consegueUtilizarUmaMascaraGenericaSemValidadorOuEvento() {
navigateToTab(Watchers.MASCARA_GENERICA);
onView(allOf(withId(R.id.edit_text), isDisplayed()))
.perform(typeText("12345"))
.check(matches(withText("1-2-3-4-5")));
}
private void navigateToTab(Watchers watcher) {
onView(
allOf(
withText(watcher.getTitle()),
isDescendantOfA(withId(R.id.tabs)))
)
.perform(scrollTo(), click());
}
private ViewAction paste(final String type) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
// noinspection unchecked
Matcher<View> matchers = allOf(isDisplayed());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return allOf(matchers, supportsInputMethods());
} else {
// SearchView does not support input methods itself (rather it delegates to an internal text
// view for input).
return allOf(matchers, anyOf(supportsInputMethods(), isAssignableFrom(SearchView.class)));
}
}
@Override
public String getDescription() {
return "Straight typing into view";
}
@Override
public void perform(UiController uiController, View view) {
((EditText) view).setText(type);
}
};
}
}
================================================
FILE: sample/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="br.com.concrete.canarinho.sample">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name="br.com.concrete.canarinho.sample.ui.activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/activity/MainActivity.java
================================================
package br.com.concrete.canarinho.sample.ui.activity;
import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.viewpager.widget.ViewPager;
import br.com.concrete.canarinho.sample.R;
import br.com.concrete.canarinho.sample.ui.adapter.WatchersPagerAdapter;
/** */
public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
final Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setDisplayHomeAsUpEnabled(true);
supportActionBar.setDisplayShowTitleEnabled(false);
}
final ViewPager viewPager = findViewById(R.id.viewpager);
viewPager.setAdapter(new WatchersPagerAdapter(getSupportFragmentManager()));
final TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
}
}
================================================
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/adapter/WatchersPagerAdapter.java
================================================
package br.com.concrete.canarinho.sample.ui.adapter;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import br.com.concrete.canarinho.sample.ui.model.Watchers;
public class WatchersPagerAdapter extends FragmentPagerAdapter {
private Watchers[] models = Watchers.values();
public WatchersPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@Override
public Fragment getItem(int position) {
return models[position].buildFragment();
}
@Override
public int getCount() {
return models.length;
}
@Override
public CharSequence getPageTitle(int position) {
return models[position].getTitle();
}
}
================================================
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/fragment/BaseWatcherFragment.java
================================================
package br.com.concrete.canarinho.sample.ui.fragment;
import androidx.fragment.app.Fragment;
import br.com.concrete.canarinho.sample.ui.model.Watchers;
public abstract class BaseWatcherFragment extends Fragment {
protected Watchers model;
public void setModel(Watchers model) {
this.model = model;
}
}
================================================
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/fragment/CanarinhoValorMonetarioWatcherFragment.java
================================================
package br.com.concrete.canarinho.sample.ui.fragment;
import android.os.Bundle;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import com.google.android.material.textfield.TextInputLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import br.com.concrete.canarinho.sample.R;
import br.com.concrete.canarinho.sample.ui.model.Watchers;
import br.com.concrete.canarinho.watcher.ValorMonetarioWatcher;
public class CanarinhoValorMonetarioWatcherFragment extends BaseWatcherFragment {
private EditText watcherEdit;
private TextView watcherTitle;
private TextInputLayout watcherInputLayout;
private TextWatcher currentWatcher;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View layout = inflater.inflate(R.layout.fragment_valor_monetario_watcher, null);
watcherTitle = layout.findViewById(R.id.watcher_title);
watcherInputLayout = layout.findViewById(R.id.edit_input_layout);
watcherEdit = layout.findViewById(R.id.edit_text);
setModel(Watchers.VALOR_MONETARIO);
bind(model);
return layout;
}
public CanarinhoValorMonetarioWatcherFragment bind(Watchers model) {
watcherTitle.setText(model.getTitle());
watcherEdit.setHint(model.getHint());
if (currentWatcher != null) {
watcherEdit.removeTextChangedListener(currentWatcher);
}
currentWatcher = new ValorMonetarioWatcher.Builder()
.comMantemZerosAoLimpar()
.comSimboloReal()
.build();
watcherEdit.addTextChangedListener(currentWatcher);
return this;
}
}
================================================
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/fragment/WatcherFragment.java
================================================
package br.com.concrete.canarinho.sample.ui.fragment;
import android.os.Bundle;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import com.google.android.material.textfield.TextInputLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import br.com.concrete.canarinho.sample.R;
import br.com.concrete.canarinho.sample.ui.model.Watchers;
public class WatcherFragment extends BaseWatcherFragment {
private EditText watcherEdit;
private TextView watcherTitle;
private TextInputLayout watcherInputLayout;
private TextWatcher currentWatcher;
public static WatcherFragment newInstance(Watchers model) {
final WatcherFragment watcherFragment = new WatcherFragment();
watcherFragment.setArguments(new Bundle());
watcherFragment.setModel(model);
return watcherFragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View layout = inflater.inflate(R.layout.fragment_canarinho_watcher, null);
watcherTitle = layout.findViewById(R.id.watcher_title);
watcherInputLayout = layout.findViewById(R.id.edit_input_layout);
watcherEdit = layout.findViewById(R.id.edit_text);
bind(model);
return layout;
}
public WatcherFragment bind(Watchers model) {
if (currentWatcher != null) {
watcherEdit.removeTextChangedListener(currentWatcher);
}
currentWatcher = model.setupWatcher(watcherInputLayout);
watcherTitle.setText(model.getTitle());
watcherEdit.setHint(model.getHint());
watcherEdit.addTextChangedListener(currentWatcher);
return this;
}
}
================================================
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/model/Watchers.java
================================================
package br.com.concrete.canarinho.sample.ui.model;
import android.text.TextWatcher;
import com.google.android.material.textfield.TextInputLayout;
import androidx.appcompat.app.AlertDialog;
import br.com.concrete.canarinho.sample.ui.fragment.BaseWatcherFragment;
import br.com.concrete.canarinho.sample.ui.fragment.CanarinhoValorMonetarioWatcherFragment;
import br.com.concrete.canarinho.sample.ui.fragment.WatcherFragment;
import br.com.concrete.canarinho.validator.Validador;
import br.com.concrete.canarinho.watcher.BoletoBancarioTextWatcher;
import br.com.concrete.canarinho.watcher.CEPTextWatcher;
import br.com.concrete.canarinho.watcher.CPFCNPJTextWatcher;
import br.com.concrete.canarinho.watcher.MascaraNumericaTextWatcher;
import br.com.concrete.canarinho.watcher.TelefoneTextWatcher;
import br.com.concrete.canarinho.watcher.evento.EventoDeValidacao;
import br.com.concrete.canarinho.watcher.evento.EventoDeValidacaoDeBoleto;
/**
*/
public enum Watchers {
BOLETO_BANCARIO("Boleto Bancário", "Digite um boleto válido") {
@Override
public WatcherFragment buildFragment() {
return WatcherFragment.newInstance(this);
}
@Override
public TextWatcher setupWatcher(TextInputLayout textInputLayout) {
return new BoletoBancarioTextWatcher(new SampleEventoDeValidacao(textInputLayout));
}
},
BOLETO_BANCARIO_MSG_CUSTOM("Boleto Bancário com mensagem", "Digite um boleto válido") {
@Override
public WatcherFragment buildFragment() {
return WatcherFragment.newInstance(this);
}
@Override
public TextWatcher setupWatcher(TextInputLayout textInputLayout) {
return new BoletoBancarioTextWatcher(new EventoDeValidacaoBoleto(textInputLayout));
}
},
CPF("CPF", "Digite um CPF válido") {
@Override
public WatcherFragment buildFragment() {
return WatcherFragment.newInstance(this);
}
@Override
public TextWatcher setupWatcher(TextInputLayout textInputLayout) {
return new MascaraNumericaTextWatcher.Builder()
.paraMascara("###.###.###-##")
.comCallbackDeValidacao(new SampleEventoDeValidacao(textInputLayout))
.comValidador(Validador.CPF)
.build();
}
},
CNPJ("CNPJ", "Digite um CNPJ válido") {
@Override
public WatcherFragment buildFragment() {
return WatcherFragment.newInstance(this);
}
@Override
public TextWatcher setupWatcher(TextInputLayout textInputLayout) {
return new MascaraNumericaTextWatcher.Builder()
.paraMascara("##.###.###/####-##")
.comCallbackDeValidacao(new SampleEventoDeValidacao(textInputLayout))
.comValidador(Validador.CNPJ)
.build();
}
},
TELEFONE("Telefone", "Digite um telefone válido") {
@Override
public WatcherFragment buildFragment() {
return WatcherFragment.newInstance(this);
}
@Override
public TextWatcher setupWatcher(TextInputLayout textInputLayout) {
return new TelefoneTextWatcher(new SampleEventoDeValidacao(textInputLayout));
}
},
CPF_CNPJ("CPF e CNPJ", "Digite um CPF ou CNPJ válido") {
@Override
public WatcherFragment buildFragment() {
return WatcherFragment.newInstance(this);
}
@Override
public TextWatcher setupWatcher(TextInputLayout textInputLayout) {
return new CPFCNPJTextWatcher(new SampleEventoDeValidacao(textInputLayout));
}
},
CEP("CEP", "Digite um CEP válido") {
@Override
public WatcherFragment buildFragment() {
return WatcherFragment.newInstance(this);
}
@Override
public TextWatcher setupWatcher(TextInputLayout textInputLayout) {
return new CEPTextWatcher(new SampleEventoDeValidacao(textInputLayout));
}
},
MASCARA_GENERICA("Máscara genérica", "5 números") {
@Override
public WatcherFragment buildFragment() {
return WatcherFragment.newInstance(this);
}
@Override
public TextWatcher setupWatcher(TextInputLayout textInputLayout) {
return new MascaraNumericaTextWatcher("#-#-#-#-#");
}
},
VALOR_MONETARIO("Valor monetário", "Digite um valor monetário") {
@Override
public CanarinhoValorMonetarioWatcherFragment buildFragment() {
return new CanarinhoValorMonetarioWatcherFragment();
}
@Override
public TextWatcher setupWatcher(TextInputLayout textInputLayout) {
return null;
}
};
private final String title;
private final String hint;
Watchers(String title, String hint) {
this.hint = hint;
this.title = title;
}
public String getTitle() {
return title;
}
public String getHint() {
return hint;
}
public abstract BaseWatcherFragment buildFragment();
public abstract TextWatcher setupWatcher(TextInputLayout textInputLayout);
/**
* {@link EventoDeValidacao} que mostra uma mensagem de erro no {@link TextInputLayout} passado
* como argumento.
*/
public static class SampleEventoDeValidacao implements EventoDeValidacao {
TextInputLayout textInputLayout;
public SampleEventoDeValidacao(TextInputLayout textInputLayout) {
this.textInputLayout = textInputLayout;
}
@Override
public void invalido(String valorAtual, String mensagem) {
textInputLayout.setError(mensagem);
}
@Override
public void parcialmenteValido(String valorAtual) {
textInputLayout.setErrorEnabled(false);
textInputLayout.setError(null);
}
@Override
public void totalmenteValido(String valorAtual) {
new AlertDialog.Builder(textInputLayout.getContext())
.setTitle("Campo válido!")
.setMessage(valorAtual)
.show();
}
}
/**
* {@link EventoDeValidacao} que valida blocos de boleto.
*
* @see SampleEventoDeValidacao
*/
public static class EventoDeValidacaoBoleto
extends SampleEventoDeValidacao
implements EventoDeValidacaoDeBoleto {
EventoDeValidacaoBoleto(TextInputLayout textInputLayout) {
super(textInputLayout);
}
@Override
public void invalido(String valorAtual, int blocoInvalido) {
if (blocoInvalido == 1)
textInputLayout.setError("Primeira mensagem");
else if (blocoInvalido == 2)
textInputLayout.setError("Segunda mensagem");
else if (blocoInvalido == 3)
textInputLayout.setError("Terceira mensagem");
else if (blocoInvalido == 4)
textInputLayout.setError("Quarta mensagem");
}
}
}
================================================
FILE: sample/src/main/res/layout/fragment_canarinho_watcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dp">
<TextView
android:id="@+id/watcher_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</com.google.android.material.card.MaterialCardView>
================================================
FILE: sample/src/main/res/layout/fragment_valor_monetario_watcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dp">
<TextView
android:id="@+id/watcher_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</com.google.android.material.card.MaterialCardView>
================================================
FILE: sample/src/main/res/layout/main_activity.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#008800"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:contentDescription="@string/app_name"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" />
</androidx.appcompat.widget.Toolbar>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMode="scrollable" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
================================================
FILE: sample/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">Android Canarinho</string>
</resources>
================================================
FILE: sample/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/white</item>
</style>
</resources>
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorBOLETO.java
================================================
package br.com.concrete.canarinho.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.formatador.Formatador;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@RunWith(AndroidJUnit4.class)
public class TesteFormatadorBOLETO {
@Test
public void consegueFormatar() {
assertThat(Formatador.BOLETO.formata("12345123451234512345612345123456712345678901234"),
is("12345.12345 12345.123456 12345.123456 7 12345678901234"));
assertThat(Formatador.BOLETO.formata("23790123016000000005325000456704364670000013580"),
is("23790.12301 60000.000053 25000.456704 3 64670000013580"));
assertThat(Formatador.BOLETO.formata("812345678901812345678901812345678901812345678901"),
is("812345678901 812345678901 812345678901 812345678901"));
assertThat(Formatador.BOLETO.formata("23790.12301 60000.000053 25000.456704 3 64670000013580"),
is("23790.12301 60000.000053 25000.456704 3 64670000013580"));
assertThat(Formatador.BOLETO.formata("812345678901 812345678901 812345678901 812345678901"),
is("812345678901 812345678901 812345678901 812345678901"));
}
@Test
public void consegueDesformatar() {
assertThat(Formatador.BOLETO.desformata("12345.12345 12345.123456 12345.123456 7 12345678901234"),
is("12345123451234512345612345123456712345678901234"));
assertThat(Formatador.BOLETO.desformata("23790.12301 60000.000053 25000.456704 3 64670000013580"),
is("23790123016000000005325000456704364670000013580"));
assertThat(Formatador.BOLETO.desformata("812345678901 812345678901 812345678901 812345678901"),
is("812345678901812345678901812345678901812345678901"));
assertThat(Formatador.BOLETO.desformata("23790123016000000005325000456704364670000013580"),
is("23790123016000000005325000456704364670000013580"));
assertThat(Formatador.BOLETO.desformata("812345678901812345678901812345678901812345678901"),
is("812345678901812345678901812345678901812345678901"));
}
@Test
public void consegueDizerSeEstaFormatado() {
assertThat(Formatador.BOLETO.estaFormatado("12345.12345 12345.123456 12345.123456 7 12345678901234"), is(true));
assertThat(Formatador.BOLETO.estaFormatado("23790.12301 60000.000053 25000.456704 3 64670000013580"), is(true));
assertThat(Formatador.BOLETO.estaFormatado("812345678901812345678901812345678901812345678901"), is(false));
assertThat(Formatador.BOLETO.estaFormatado("23790123016000000005325000456704364670000013580"), is(false));
assertThat(Formatador.BOLETO.estaFormatado("812345678901812345678901812345678901812345678901"), is(false));
}
@Test
public void consegueDizerSePodeFormatar() {
// TODO
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCEP.java
================================================
package br.com.concrete.canarinho.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.formatador.Formatador;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
public class TesteFormatadorCEP {
@Test
public void consegueFormatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CEP.formata("00000-000"), is("00000-000"));
assertThat(Formatador.CEP.formata("00000000"), is("00000-000"));
assertThat(Formatador.CEP.formata("12345-123"), is("12345-123"));
assertThat(Formatador.CEP.formata("12345678"), is("12345-678"));
assertThrowsFormat("");
assertThrowsFormat("123123");
}
@Test
public void consegueDesformatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CEP.desformata("00000-000"), is("00000000"));
assertThat(Formatador.CEP.desformata("00000000"), is("00000000"));
assertThat(Formatador.CEP.desformata("12345-123"), is("12345123"));
assertThat(Formatador.CEP.desformata("12345678"), is("12345678"));
assertThrowsDesformat("");
assertThrowsDesformat("123123");
}
@Test
public void consegueDizerSeEstaFormatado() {
// Gerados automaticamente para testes
assertThat(Formatador.CEP.estaFormatado("12345-678"), is(true));
assertThat(Formatador.CEP.estaFormatado("12345678"), is(false));
assertThat(Formatador.CEP.estaFormatado("00000-000"), is(true));
assertThat(Formatador.CEP.estaFormatado("12345-67"), is(false));
assertThat(Formatador.CEP.estaFormatado("047486"), is(false));
assertThat(Formatador.CEP.estaFormatado(""), is(false));
try {
Formatador.CEP.estaFormatado(null);
fail("Should have thrown!!!");
} catch (IllegalArgumentException e) {
}
}
@Test
public void consegueDizerSePodeFormatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CEP.podeSerFormatado("12345-678"), is(false));
assertThat(Formatador.CEP.podeSerFormatado("12345678"), is(true));
assertThat(Formatador.CEP.podeSerFormatado("020"), is(false));
assertThat(Formatador.CEP.podeSerFormatado(""), is(false));
assertThat(Formatador.CEP.podeSerFormatado(null), is(false));
}
private void assertThrowsFormat(String valor) {
try {
Formatador.CEP.formata(valor);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
private void assertThrowsDesformat(String valor) {
try {
Formatador.CEP.desformata(valor);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCNPJ.java
================================================
package br.com.concrete.canarinho.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.formatador.Formatador;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
public class TesteFormatadorCNPJ {
@Test
public void consegueFormatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CNPJ.formata("50.713.534/0001-33"), is("50.713.534/0001-33"));
assertThat(Formatador.CNPJ.formata("50713534000133"), is("50.713.534/0001-33"));
assertThat(Formatador.CNPJ.formata("72.606.598/0001-78"), is("72.606.598/0001-78"));
assertThat(Formatador.CNPJ.formata("72606598000178"), is("72.606.598/0001-78"));
assertThat(Formatador.CNPJ.formata("23.106.535/0001-47"), is("23.106.535/0001-47"));
assertThat(Formatador.CNPJ.formata("23106535000147"), is("23.106.535/0001-47"));
assertThrowsFormat("");
assertThrowsFormat("123123");
assertThrowsFormat("23.106.535/0001 - 47");
assertThrowsFormat(" 23.106.535/0001-47 ");
}
@Test
public void consegueDesformatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CNPJ.desformata("50.713.534/0001-33"), is("50713534000133"));
assertThat(Formatador.CNPJ.desformata("50713534000133"), is("50713534000133"));
assertThat(Formatador.CNPJ.desformata("72.606.598/0001-78"), is("72606598000178"));
assertThat(Formatador.CNPJ.desformata("72606598000178"), is("72606598000178"));
assertThat(Formatador.CNPJ.desformata("23.106.535/0001-47"), is("23106535000147"));
assertThat(Formatador.CNPJ.desformata("23106535000147"), is("23106535000147"));
assertThrowsDesformat("");
assertThrowsDesformat("123123");
assertThrowsDesformat("123.123.123 -12");
assertThrowsDesformat(" 047.486.777-32 ");
}
@Test
public void consegueDizerSeEstaFormatado() {
// Gerados automaticamente para testes
assertThat(Formatador.CNPJ.estaFormatado("72.606.598/0001-78"), is(true));
assertThat(Formatador.CNPJ.estaFormatado("72606598000178"), is(false));
assertThat(Formatador.CNPJ.estaFormatado("23.106.535/0001-47"), is(true));
assertThat(Formatador.CNPJ.estaFormatado("23106535000147"), is(false));
assertThat(Formatador.CNPJ.estaFormatado("047486"), is(false));
assertThat(Formatador.CNPJ.estaFormatado(""), is(false));
try {
Formatador.CNPJ.estaFormatado(null);
fail("Should have thrown!!!");
} catch (IllegalArgumentException e) {
}
}
@Test
public void consegueDizerSePodeFormatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CNPJ.podeSerFormatado("23.106.535/0001-47"), is(false));
assertThat(Formatador.CNPJ.podeSerFormatado("23106535000147"), is(true));
assertThat(Formatador.CNPJ.podeSerFormatado("020"), is(false));
assertThat(Formatador.CNPJ.podeSerFormatado(""), is(false));
assertThat(Formatador.CNPJ.podeSerFormatado(null), is(false));
}
private void assertThrowsFormat(String valor) {
try {
Formatador.CNPJ.formata(valor);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
private void assertThrowsDesformat(String valor) {
try {
Formatador.CNPJ.desformata(valor);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCPF.java
================================================
package br.com.concrete.canarinho.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.formatador.Formatador;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
public class TesteFormatadorCPF {
@Test
public void consegueFormatarCPF() {
// Gerados automaticamente para testes
assertThat(Formatador.CPF.formata("545.586.262-66"), is("545.586.262-66"));
assertThat(Formatador.CPF.formata("54558626266"), is("545.586.262-66"));
assertThat(Formatador.CPF.formata("020.724.833-87"), is("020.724.833-87"));
assertThat(Formatador.CPF.formata("02072483387"), is("020.724.833-87"));
assertThat(Formatador.CPF.formata("188.527.045-31"), is("188.527.045-31"));
assertThat(Formatador.CPF.formata("18852704531"), is("188.527.045-31"));
assertThat(Formatador.CPF.formata("047.486.777-32"), is("047.486.777-32"));
assertThat(Formatador.CPF.formata("04748677732"), is("047.486.777-32"));
assertThrowsFormat("");
assertThrowsFormat("123123");
assertThrowsFormat("123.123.123 -12");
assertThrowsFormat(" 047.486.777-32 ");
}
@Test
public void consegueDesformatarCPF() {
// Gerados automaticamente para testes
assertThat(Formatador.CPF.desformata("545.586.262-66"), is("54558626266"));
assertThat(Formatador.CPF.desformata("54558626266"), is("54558626266"));
assertThat(Formatador.CPF.desformata("020.724.833-87"), is("02072483387"));
assertThat(Formatador.CPF.desformata("02072483387"), is("02072483387"));
assertThat(Formatador.CPF.desformata("188.527.045-31"), is("18852704531"));
assertThat(Formatador.CPF.desformata("18852704531"), is("18852704531"));
assertThat(Formatador.CPF.desformata("047.486.777-32"), is("04748677732"));
assertThat(Formatador.CPF.desformata("04748677732"), is("04748677732"));
assertThrowsDesformat("");
assertThrowsDesformat("123123");
assertThrowsDesformat("123.123.123 -12");
assertThrowsDesformat(" 047.486.777-32 ");
}
@Test
public void consegueDizerSeEstaFormatado() {
// Gerados automaticamente para testes
assertThat(Formatador.CPF.estaFormatado("545.586.262-66"), is(true));
assertThat(Formatador.CPF.estaFormatado("54558626266"), is(false));
assertThat(Formatador.CPF.estaFormatado("020.724.833-87"), is(true));
assertThat(Formatador.CPF.estaFormatado("02072483387"), is(false));
assertThat(Formatador.CPF.estaFormatado("188.527.045-31"), is(true));
assertThat(Formatador.CPF.estaFormatado("18852704531"), is(false));
assertThat(Formatador.CPF.estaFormatado("047.486.777-32"), is(true));
assertThat(Formatador.CPF.estaFormatado("04748677732"), is(false));
assertThat(Formatador.CPF.estaFormatado("047486"), is(false));
assertThat(Formatador.CPF.estaFormatado(""), is(false));
try {
Formatador.CPF.estaFormatado(null);
fail("Should have thrown!!!");
} catch (IllegalArgumentException e) {
}
}
@Test
public void consegueDizerSePodeFormatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CPF.podeSerFormatado("545.586.262-66"), is(false));
assertThat(Formatador.CPF.podeSerFormatado("54558626266"), is(true));
assertThat(Formatador.CPF.podeSerFormatado("020"), is(false));
assertThat(Formatador.CPF.podeSerFormatado(""), is(false));
assertThat(Formatador.CPF.podeSerFormatado(null), is(false));
}
private void assertThrowsFormat(String valor) {
try {
Formatador.CPF.formata(valor);
fail("Should have thrown!!!");
} catch (IllegalArgumentException e) {
}
}
private void assertThrowsDesformat(String valor) {
try {
Formatador.CPF.desformata(valor);
fail("Should have thrown!!!");
} catch (IllegalArgumentException e) {
}
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCPFCNPJ.java
================================================
package br.com.concrete.canarinho.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.formatador.Formatador;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
public class TesteFormatadorCPFCNPJ {
@Test
public void consegueFormatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CPF_CNPJ.formata("50.713.534/0001-33"), is("50.713.534/0001-33"));
assertThat(Formatador.CPF_CNPJ.formata("50713534000133"), is("50.713.534/0001-33"));
assertThat(Formatador.CPF_CNPJ.formata("72.606.598/0001-78"), is("72.606.598/0001-78"));
assertThat(Formatador.CPF_CNPJ.formata("72606598000178"), is("72.606.598/0001-78"));
assertThat(Formatador.CPF_CNPJ.formata("23.106.535/0001-47"), is("23.106.535/0001-47"));
assertThat(Formatador.CPF_CNPJ.formata("23106535000147"), is("23.106.535/0001-47"));
assertThrowsFormat("");
assertThrowsFormat("123123");
assertThrowsFormat("23.106.535/0001 - 47");
assertThrowsFormat(" 23.106.535/0001-47 ");
// Gerados automaticamente para testes
assertThat(Formatador.CPF_CNPJ.formata("545.586.262-66"), is("545.586.262-66"));
assertThat(Formatador.CPF_CNPJ.formata("54558626266"), is("545.586.262-66"));
assertThat(Formatador.CPF_CNPJ.formata("020.724.833-87"), is("020.724.833-87"));
assertThat(Formatador.CPF_CNPJ.formata("02072483387"), is("020.724.833-87"));
assertThat(Formatador.CPF_CNPJ.formata("188.527.045-31"), is("188.527.045-31"));
assertThat(Formatador.CPF_CNPJ.formata("18852704531"), is("188.527.045-31"));
assertThat(Formatador.CPF_CNPJ.formata("047.486.777-32"), is("047.486.777-32"));
assertThat(Formatador.CPF_CNPJ.formata("04748677732"), is("047.486.777-32"));
assertThrowsFormat("");
assertThrowsFormat("123123");
assertThrowsFormat("123.123.123 -12");
assertThrowsFormat(" 047.486.777-32 ");
}
@Test
public void consegueDesformatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CPF_CNPJ.desformata("50.713.534/0001-33"), is("50713534000133"));
assertThat(Formatador.CPF_CNPJ.desformata("50713534000133"), is("50713534000133"));
assertThat(Formatador.CPF_CNPJ.desformata("72.606.598/0001-78"), is("72606598000178"));
assertThat(Formatador.CPF_CNPJ.desformata("72606598000178"), is("72606598000178"));
assertThat(Formatador.CPF_CNPJ.desformata("23.106.535/0001-47"), is("23106535000147"));
assertThat(Formatador.CPF_CNPJ.desformata("23106535000147"), is("23106535000147"));
assertThrowsDesformat("");
assertThrowsDesformat("123123");
assertThrowsDesformat("123.123.123 -12");
assertThrowsDesformat(" 047.486.777-32 ");
// Gerados automaticamente para testes
assertThat(Formatador.CPF_CNPJ.desformata("545.586.262-66"), is("54558626266"));
assertThat(Formatador.CPF_CNPJ.desformata("54558626266"), is("54558626266"));
assertThat(Formatador.CPF_CNPJ.desformata("020.724.833-87"), is("02072483387"));
assertThat(Formatador.CPF_CNPJ.desformata("02072483387"), is("02072483387"));
assertThat(Formatador.CPF_CNPJ.desformata("188.527.045-31"), is("18852704531"));
assertThat(Formatador.CPF_CNPJ.desformata("18852704531"), is("18852704531"));
assertThat(Formatador.CPF_CNPJ.desformata("047.486.777-32"), is("04748677732"));
assertThat(Formatador.CPF_CNPJ.desformata("04748677732"), is("04748677732"));
assertThrowsDesformat("");
assertThrowsDesformat("123123");
assertThrowsDesformat("123.123.123 -12");
assertThrowsDesformat(" 047.486.777-32 ");
}
@Test
public void consegueDizerSeEstaFormatado() {
// Gerados automaticamente para testes
assertThat(Formatador.CPF_CNPJ.estaFormatado("72.606.598/0001-78"), is(true));
assertThat(Formatador.CPF_CNPJ.estaFormatado("72606598000178"), is(false));
assertThat(Formatador.CPF_CNPJ.estaFormatado("23.106.535/0001-47"), is(true));
assertThat(Formatador.CPF_CNPJ.estaFormatado("23106535000147"), is(false));
assertThat(Formatador.CPF_CNPJ.estaFormatado("047486"), is(false));
assertThat(Formatador.CPF_CNPJ.estaFormatado(""), is(false));
try {
Formatador.CPF_CNPJ.estaFormatado(null);
fail("Should have thrown!!!");
} catch (IllegalArgumentException e) {
}
// Gerados automaticamente para testes
assertThat(Formatador.CPF_CNPJ.estaFormatado("545.586.262-66"), is(true));
assertThat(Formatador.CPF_CNPJ.estaFormatado("54558626266"), is(false));
assertThat(Formatador.CPF_CNPJ.estaFormatado("020.724.833-87"), is(true));
assertThat(Formatador.CPF_CNPJ.estaFormatado("02072483387"), is(false));
assertThat(Formatador.CPF_CNPJ.estaFormatado("188.527.045-31"), is(true));
assertThat(Formatador.CPF_CNPJ.estaFormatado("18852704531"), is(false));
assertThat(Formatador.CPF_CNPJ.estaFormatado("047.486.777-32"), is(true));
assertThat(Formatador.CPF_CNPJ.estaFormatado("04748677732"), is(false));
assertThat(Formatador.CPF_CNPJ.estaFormatado("047486"), is(false));
assertThat(Formatador.CPF_CNPJ.estaFormatado(""), is(false));
try {
Formatador.CPF_CNPJ.estaFormatado(null);
fail("Should have thrown!!!");
} catch (IllegalArgumentException e) {
}
}
@Test
public void consegueDizerSePodeFormatar() {
// Gerados automaticamente para testes
assertThat(Formatador.CPF_CNPJ.podeSerFormatado("23.106.535/0001-47"), is(false));
assertThat(Formatador.CPF_CNPJ.podeSerFormatado("23106535000147"), is(true));
assertThat(Formatador.CPF_CNPJ.podeSerFormatado("020"), is(false));
assertThat(Formatador.CPF_CNPJ.podeSerFormatado(""), is(false));
assertThat(Formatador.CPF_CNPJ.podeSerFormatado(null), is(false));
// Gerados automaticamente para testes
assertThat(Formatador.CPF_CNPJ.podeSerFormatado("545.586.262-66"), is(false));
assertThat(Formatador.CPF_CNPJ.podeSerFormatado("54558626266"), is(true));
assertThat(Formatador.CPF_CNPJ.podeSerFormatado("020"), is(false));
assertThat(Formatador.CPF_CNPJ.podeSerFormatado(""), is(false));
assertThat(Formatador.CPF_CNPJ.podeSerFormatado(null), is(false));
}
private void assertThrowsFormat(String valor) {
try {
Formatador.CPF_CNPJ.formata(valor);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
private void assertThrowsDesformat(String valor) {
try {
Formatador.CPF_CNPJ.desformata(valor);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorLinhaDigitavel.java
================================================
package br.com.concrete.canarinho.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.formatador.Formatador;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
@RunWith(AndroidJUnit4.class)
public class TesteFormatadorLinhaDigitavel {
@Test
public void consegueFormatarEDesformatar() {
assertThat(Formatador.LINHA_DIGITAVEL.desformata("812345678901812345678901812345678901812345678901"),
is("81234567890812345678908123456789081234567890"));
final String original = "23790123016000000005325000456704964680000013580";
final String boleto = Formatador.LINHA_DIGITAVEL.desformata(original);
final String linhaDigitavel = Formatador.LINHA_DIGITAVEL.formata(boleto);
assertThat(linhaDigitavel, equalTo(original));
}
@Test
public void consegueDizerSeEstaFormatado() {
assertThat(Formatador.LINHA_DIGITAVEL.estaFormatado("81234567890812345678908123456789081234567890"), is(false));
assertThat(Formatador.LINHA_DIGITAVEL.estaFormatado("812345678901 812345678901 812345678901 812345678901"), is(true));
assertThat(Formatador.LINHA_DIGITAVEL.estaFormatado("23790.12301 60000.000053 25000.456704 9 64680000013580"), is(true));
}
@Test
public void consegueDizerSePodeFormatar() {
assertThat(Formatador.LINHA_DIGITAVEL.podeSerFormatado("8123456789081234567890812345678908123456"), is(false));
assertThat(Formatador.LINHA_DIGITAVEL.podeSerFormatado("23790.1230 60000.00005 25000.45670 9 64680000013580"), is(true));
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorTelefone.java
================================================
package br.com.concrete.canarinho.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.formatador.Formatador;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
public class TesteFormatadorTelefone {
@Test
public void consegueFormatar() {
assertThat(Formatador.TELEFONE.formata("1112345678"),
is("(11) 1234-5678"));
assertThat(Formatador.TELEFONE.formata("11123456789"),
is("(11) 12345-6789"));
try {
Formatador.TELEFONE.formata(null);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
@Test
public void consegueDesformatar() {
assertThat(Formatador.TELEFONE.desformata("(11) 1234-5678"),
is("1112345678"));
assertThat(Formatador.TELEFONE.desformata("(11) 12345-6789"),
is("11123456789"));
try {
Formatador.TELEFONE.desformata(null);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
@Test
public void consegueDizerSeEstaFormatado() {
assertThat(Formatador.TELEFONE.estaFormatado("(11) 1234-5678"), is(true));
assertThat(Formatador.TELEFONE.estaFormatado("(11) 12345-6789"), is(true));
assertThat(Formatador.TELEFONE.estaFormatado("(11) 1234-56789"), is(false));
assertThat(Formatador.TELEFONE.estaFormatado("1112345678"), is(false));
assertThat(Formatador.TELEFONE.estaFormatado("11123456789"), is(false));
try {
Formatador.TELEFONE.estaFormatado(null);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
@Test
public void consegueDizerSePodeFormatar() {
assertThat(Formatador.TELEFONE.podeSerFormatado("11"), is(false));
assertThat(Formatador.TELEFONE.podeSerFormatado("111234567890"), is(false));
assertThat(Formatador.TELEFONE.podeSerFormatado("1112345678"), is(true));
assertThat(Formatador.TELEFONE.podeSerFormatado("11123456789"), is(true));
try {
Formatador.TELEFONE.podeSerFormatado(null);
fail("Deveria ter jogado exceção!!!");
} catch (IllegalArgumentException e) {
}
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorValor.java
================================================
package br.com.concrete.canarinho.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.formatador.Formatador;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
public class TesteFormatadorValor {
@Test
public void consegueFormatar() {
assertThat(Formatador.VALOR.formata("1.00"), is("1,00"));
assertThat(Formatador.VALOR.formata("1.0"), is("1,00"));
assertThat(Formatador.VALOR.formata("1"), is("1,00"));
assertThat(Formatador.VALOR.formata("1000"), is("1.000,00"));
assertThat(Formatador.VALOR.formata("1.23"), is("1,23"));
assertThat(Formatador.VALOR.formata("1.233"), is("1,23"));
assertThat(Formatador.VALOR.formata("1.01"), is("1,01"));
assertThat(Formatador.VALOR.formata("1.1"), is("1,10"));
assertThat(Formatador.VALOR.formata("1234.56"), is("1.234,56"));
assertThat(Formatador.VALOR.formata("1234.5"), is("1.234,50"));
}
@Test
public void consegueFormatarComSimbolo() {
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1.00"), is("R$ 1,00"));
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1.0"), is("R$ 1,00"));
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1"), is("R$ 1,00"));
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1000"), is("R$ 1.000,00"));
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1.23"), is("R$ 1,23"));
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1.233"), is("R$ 1,23"));
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1.01"), is("R$ 1,01"));
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1.1"), is("R$ 1,10"));
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1234.56"), is("R$ 1.234,56"));
assertThat(Formatador.VALOR_COM_SIMBOLO.formata("1234.5"), is("R$ 1.234,50"));
}
@Test
public void consegueDesformatar() {
assertThat(Formatador.VALOR.desformata("1,00"), is("1.00"));
assertThat(Formatador.VALOR.desformata("1.234,10"), is("1234.10"));
assertThat(Formatador.VALOR.desformata("0,10"), is("0.10"));
}
@Test
public void consegueDesformatarComSimbolo() {
assertThat(Formatador.VALOR_COM_SIMBOLO.desformata("R$ 1,00"), is("1.00"));
assertThat(Formatador.VALOR_COM_SIMBOLO.desformata("R$ 1.234,10"), is("1234.10"));
assertThat(Formatador.VALOR_COM_SIMBOLO.desformata("R$ 0,10"), is("0.10"));
}
@Test
public void consegueDizerSeEstaFormatado() {
assertThat(Formatador.VALOR.estaFormatado("1,00"), is(true));
assertThat(Formatador.VALOR.estaFormatado("1.234,10"), is(true));
assertThat(Formatador.VALOR.estaFormatado("1.34,10"), is(false));
try {
Formatador.VALOR.estaFormatado(null);
fail("Should have thrown!!!");
} catch (IllegalArgumentException e) {
}
}
@Test
public void consegueDizerSePodeFormatar() {
assertThat(Formatador.VALOR.podeSerFormatado("1.00"), is(true));
assertThat(Formatador.VALOR.podeSerFormatado("10"), is(true));
assertThat(Formatador.VALOR.podeSerFormatado("10.1"), is(true));
assertThat(Formatador.VALOR.podeSerFormatado("10,10"), is(false));
try {
Formatador.VALOR.podeSerFormatado(null);
fail("Should have thrown!!!");
} catch (IllegalArgumentException e) {
}
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteValidadores.java
================================================
package br.com.concrete.canarinho.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.validator.Validador;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
@RunWith(AndroidJUnit4.class)
public class TesteValidadores {
@Test
public void consegueValidarCPF() {
// Gerados automaticamente para testes
assertThat(Validador.CPF.ehValido("545.586.262-66"), is(true));
assertThat(Validador.CPF.ehValido("655.274.921-02"), is(true));
assertThat(Validador.CPF.ehValido("020.724.833-87"), is(true));
assertThat(Validador.CPF.ehValido("188.527.045-31"), is(true));
assertThat(Validador.CPF.ehValido("047.486.777-32"), is(true));
assertThat(Validador.CPF.ehValido("54558626266"), is(true));
assertThat(Validador.CPF.ehValido("65527492102"), is(true));
assertThat(Validador.CPF.ehValido("02072483387"), is(true));
assertThat(Validador.CPF.ehValido("18852704531"), is(true));
assertThat(Validador.CPF.ehValido("04748677732"), is(true));
assertThat(Validador.CPF.ehValido("545.111.262-66"), is(false));
assertThat(Validador.CPF.ehValido("655.111.921-02"), is(false));
assertThat(Validador.CPF.ehValido("020.111.833-87"), is(false));
assertThat(Validador.CPF.ehValido("188.111.045-31"), is(false));
assertThat(Validador.CPF.ehValido("047.111.777-32"), is(false));
assertThat(Validador.CPF.ehValido("54111626266"), is(false));
assertThat(Validador.CPF.ehValido("65111492102"), is(false));
assertThat(Validador.CPF.ehValido("02111483387"), is(false));
assertThat(Validador.CPF.ehValido("18111704531"), is(false));
assertThat(Validador.CPF.ehValido("04111677732"), is(false));
}
@Test
public void consegueValidarCNPJ() {
// Gerados automaticamente para testes
assertThat(Validador.CNPJ.ehValido("50.713.534/0001-33"), is(true));
assertThat(Validador.CNPJ.ehValido("77.135.038/0001-04"), is(true));
assertThat(Validador.CNPJ.ehValido("58.613.246/0001-19"), is(true));
assertThat(Validador.CNPJ.ehValido("50713534000133"), is(true));
assertThat(Validador.CNPJ.ehValido("77135038000104"), is(true));
// Gerados automaticamente para testes
assertThat(Validador.CNPJ.ehValido("50.713.111/0001-33"), is(false));
assertThat(Validador.CNPJ.ehValido("77.135.111/0001-04"), is(false));
assertThat(Validador.CNPJ.ehValido("50713531110133"), is(false));
assertThat(Validador.CNPJ.ehValido("77135031110104"), is(false));
}
@Test
public void consegueValidarBoletoNormal() {
// boleto bradesco
assertThat(Validador.BOLETO
.ehValido("23790.12301 60000.000053 25000.456704 9 64680000013580"), is(true));
// boleto bradesco
assertThat(Validador.BOLETO
.ehValido("23790123016000000005325000456704964680000013580"), is(true));
// boleto banco do brasil
assertThat(Validador.BOLETO
.ehValido("00199.38414 90480.002550 84666.970219 4 64290000007726"), is(true));
// Boleto itau desformatado
assertThat(Validador.BOLETO
.ehValido("34191750090000159091820521070001664890002370000"), is(true));
// Boleto itau
assertThat(Validador.BOLETO
.ehValido("34191.75009 00001.590918 20521.070001 6 64890002370000"), is(true));
// boleto Banestes
assertThat(Validador.BOLETO
.ehValido("02190.00015 05000.000017 23452.021696 2 25230000034000"), is(true));
// boleto Caixa
assertThat(Validador.BOLETO
.ehValido("10491.44338 55119.000002 00000.000141 3 25230000093423"), is(true));
// boleto Sudameris
assertThat(Validador.BOLETO
.ehValido("34790.12001 12345.695006 10502.000002 5 25230000034000"), is(true));
}
@Test
public void consegueValidarBoletoTributoSemSerTaxa() {
assertThat(Validador.BOLETO
.ehValido("848600000015 523301622010 506101307129 620012111220"), is(true));
}
@Test
public void consegueValidarBoletoTributoDeTaxa() {
assertThat(Validador.BOLETO
.ehValido("836600000019 078800481000 998854924516 001265611135"), is(true));
assertThat(Validador.BOLETO
.ehValido("826100000007 265400971429 620232390612 725103150621"), is(true));
}
@Test
public void consegueValidarTelefone() {
assertThat(Validador.TELEFONE.ehValido("1112345678"), is(true));
assertThat(Validador.TELEFONE.ehValido("11123456789"), is(true));
assertThat(Validador.TELEFONE.ehValido("(11) 12345-6789"), is(true));
assertThat(Validador.TELEFONE.ehValido("111234567890"), is(false));
assertThat(Validador.TELEFONE.ehValido("1112345"), is(false));
}
@Test
public void consegueValidarCEP() {
assertThat(Validador.CEP.ehValido("12345678"), is(true));
assertThat(Validador.CEP.ehValido("12345-678"), is(true));
assertThat(Validador.CEP.ehValido("12345-67"), is(false));
assertThat(Validador.CEP.ehValido("1234-678"), is(false));
assertThat(Validador.CEP.ehValido(""), is(false));
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/watcher/BoletoTextWatcherTest.java
================================================
package br.com.concrete.canarinho.test.watcher;
import android.widget.EditText;
import com.google.android.material.textfield.TextInputLayout;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import br.com.concrete.canarinho.sample.ui.activity.MainActivity;
import br.com.concrete.canarinho.sample.ui.model.Watchers;
import br.com.concrete.canarinho.watcher.BoletoBancarioTextWatcher;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@RunWith(AndroidJUnit4.class)
public class BoletoTextWatcherTest {
private BoletoBancarioTextWatcher watcher;
private EditText editText;
@Before
public void setUp() {
final ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class);
scenario.onActivity(new ActivityScenario.ActivityAction<MainActivity>() {
@Override
public void perform(MainActivity activity) {
final TextInputLayout textInputLayout = new TextInputLayout(activity);
textInputLayout.addView(editText = new EditText(activity));
final Watchers.SampleEventoDeValidacao sampleEventoDeValidacao =
new Watchers.SampleEventoDeValidacao(textInputLayout);
editText.addTextChangedListener(watcher = new BoletoBancarioTextWatcher(sampleEventoDeValidacao));
activity.setContentView(textInputLayout);
}
});
}
@Test
public void typing_canValidateEmptyState() {
editText.append("");
assertThat(editText.getText().toString(), is(""));
assertThat(watcher.getResultadoParcial().isParcialmenteValido(), is(true));
}
@Test
public void typing_canValidateProperCharacters() {
editText.append("1bas2nas3lamsd4");
assertThat(editText.getText().toString(), is("1234"));
assertThat(watcher.getResultadoParcial().isParcialmenteValido(), is(true));
}
@Test
public void deleting_canEmptyEditText() {
editText.append("1234");
assertThat(editText.getText().toString(), is("1234"));
assertThat(watcher.getResultadoParcial().isParcialmenteValido(), is(true));
editText.getEditableText().clear();
assertThat(editText.getText().toString(), is(""));
}
// Teste de regressão
@Test
public void deleting_afterEmptyingEditTextItKeepsValidatingInput() {
editText.append("1234");
assertThat(editText.getText().toString(), is("1234"));
assertThat(watcher.getResultadoParcial().isParcialmenteValido(), is(true));
editText.getEditableText().clear();
assertThat(editText.getText().toString(), is(""));
// menos caracteres que o tamanho inicial para saber qual máscara aplicar
editText.append("$$");
assertThat(editText.getText().toString(), is(""));
}
}
================================================
FILE: sample/src/test/java/br/com/concrete/canarinho/test/watcher/ValorMonetarioWatcherTest.java
================================================
package br.com.concrete.canarinho.test.watcher;
import android.app.Activity;
import android.widget.EditText;
import com.google.android.material.textfield.TextInputLayout;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.robolectric.android.controller.ActivityController;
import br.com.concrete.canarinho.watcher.ValorMonetarioWatcher;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.robolectric.Robolectric.buildActivity;
@RunWith(AndroidJUnit4.class)
public class ValorMonetarioWatcherTest {
private EditText editText;
@Before
public void setUp() {
final ActivityController<Activity> activityController = buildActivity(Activity.class);
final Activity activity = activityController.create().get();
activityController.start().resume().visible();
TextInputLayout textInputLayout;
activity.setContentView(textInputLayout = new TextInputLayout(activity));
textInputLayout.addView(editText = new EditText(activity));
editText.setText("0,00");
}
@Test
public void watcher_formataOk() {
editText.addTextChangedListener(new ValorMonetarioWatcher());
editText.append("1234567890");
assertThat(editText.getText().toString(), is("12.345.678,90"));
}
@Test
public void watcher_formataOkComSimbolo() {
editText.addTextChangedListener(new ValorMonetarioWatcher.Builder()
.comSimboloReal()
.comMantemZerosAoLimpar()
.build());
editText.append("1234567890");
assertThat(editText.getText().toString(), is("R$ 12.345.678,90"));
}
@Test
public void watcher_canEmptyTextAndKeepZeroes() {
editText.addTextChangedListener(new ValorMonetarioWatcher.Builder()
.comSimboloReal()
.comMantemZerosAoLimpar()
.build());
editText.append("1234567890");
assertThat(editText.getText().toString(), is("R$ 12.345.678,90"));
editText.getText().clear();
assertThat(editText.getText().toString(), is("R$ 0,00"));
editText.getText().append('1');
assertThat(editText.getText().toString(), is("R$ 0,01"));
}
@Test
public void watcher_canEmptyTextWithoutZeroes() {
editText.addTextChangedListener(new ValorMonetarioWatcher.Builder()
.comSimboloReal()
.build());
editText.append("1234567890");
assertThat(editText.getText().toString(), is("R$ 12.345.678,90"));
editText.getText().clear();
assertThat(editText.getText().toString(), is(""));
editText.getText().append('1');
assertThat(editText.getText().toString(), is("R$ 0,01"));
}
}
================================================
FILE: settings.gradle
================================================
include ':sample', ':canarinho'
================================================
FILE: tools/linters/checkstyle/checkstyle.xml
================================================
<?xml version="1.0"?><!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the Google coding conventions from Google Java Style
that can be found at https://google.github.io/styleguide/javaguide.html.
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.sf.net (or in your downloaded distribution).
To completely disable a check, just comment it out or delete it from the file.
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
-->
<module name="Checker">
<property name="charset" value="UTF-8" />
<property name="severity" value="warning" />
<property name="fileExtensions" value="java, properties, xml" />
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="FileTabCharacter">
<property name="eachLine" value="true" />
</module>
<module name="TreeWalker">
<module name="OuterTypeFilename" />
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL" />
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)" />
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value." />
</module>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true" />
<property name="allowByTailComment" value="true" />
<property name="allowNonPrintableEscapes" value="true" />
</module>
<module name="LineLength">
<property name="max" value="120" />
<property name="ignorePattern"
value="^package.*|^import.*|a href|href|http://|https://|ftp://" />
</module>
<module name="AvoidStarImport" />
<module name="OneTopLevelClass" />
<module name="NoLineWrap" />
<module name="EmptyBlock">
<property name="option" value="TEXT" />
<property name="tokens"
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH" />
</module>
<module name="NeedBraces" />
<module name="LeftCurly" />
<module name="RightCurly">
<property name="id" value="Righ
gitextract_bmhm5g9q/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ └── android_master.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE.txt
├── PULL_REQUEST_TEMPLATE.md
├── README.md
├── build.gradle
├── canarinho/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── br/
│ └── com/
│ └── concrete/
│ └── canarinho/
│ ├── DigitoPara.java
│ ├── formatador/
│ │ ├── Formatador.java
│ │ ├── FormatadorBase.java
│ │ ├── FormatadorBoleto.java
│ │ ├── FormatadorCEP.java
│ │ ├── FormatadorCPFCNPJ.java
│ │ ├── FormatadorLinhaDigitavel.java
│ │ ├── FormatadorTelefone.java
│ │ └── FormatadorValor.java
│ ├── validator/
│ │ ├── Validador.java
│ │ ├── ValidadorBoleto.java
│ │ ├── ValidadorCEP.java
│ │ ├── ValidadorCNPJ.java
│ │ ├── ValidadorCPF.java
│ │ ├── ValidadorCPFCNPJ.java
│ │ └── ValidadorTelefone.java
│ └── watcher/
│ ├── BaseCanarinhoTextWatcher.java
│ ├── BoletoBancarioTextWatcher.java
│ ├── CEPTextWatcher.java
│ ├── CPFCNPJTextWatcher.java
│ ├── MascaraNumericaTextWatcher.java
│ ├── TelefoneTextWatcher.java
│ ├── ValorMonetarioWatcher.java
│ └── evento/
│ ├── EventoDeValidacao.java
│ └── EventoDeValidacaoDeBoleto.java
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── sample/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── br/
│ │ └── com/
│ │ └── concrete/
│ │ └── canarinho/
│ │ └── sample/
│ │ ├── BugOnApi28Test.java
│ │ └── DemoWatchersInstrumentationTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── br/
│ │ │ └── com/
│ │ │ └── concrete/
│ │ │ └── canarinho/
│ │ │ └── sample/
│ │ │ └── ui/
│ │ │ ├── activity/
│ │ │ │ └── MainActivity.java
│ │ │ ├── adapter/
│ │ │ │ └── WatchersPagerAdapter.java
│ │ │ ├── fragment/
│ │ │ │ ├── BaseWatcherFragment.java
│ │ │ │ ├── CanarinhoValorMonetarioWatcherFragment.java
│ │ │ │ └── WatcherFragment.java
│ │ │ └── model/
│ │ │ └── Watchers.java
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── fragment_canarinho_watcher.xml
│ │ │ ├── fragment_valor_monetario_watcher.xml
│ │ │ └── main_activity.xml
│ │ └── values/
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── br/
│ └── com/
│ └── concrete/
│ └── canarinho/
│ └── test/
│ ├── TesteFormatadorBOLETO.java
│ ├── TesteFormatadorCEP.java
│ ├── TesteFormatadorCNPJ.java
│ ├── TesteFormatadorCPF.java
│ ├── TesteFormatadorCPFCNPJ.java
│ ├── TesteFormatadorLinhaDigitavel.java
│ ├── TesteFormatadorTelefone.java
│ ├── TesteFormatadorValor.java
│ ├── TesteValidadores.java
│ └── watcher/
│ ├── BoletoTextWatcherTest.java
│ └── ValorMonetarioWatcherTest.java
├── settings.gradle
└── tools/
├── linters/
│ ├── checkstyle/
│ │ ├── checkstyle.xml
│ │ └── suppressions.xml
│ ├── linters.gradle
│ └── pmd/
│ └── pmd-ruleset.xml
└── publish.gradle
SYMBOL INDEX (327 symbols across 44 files)
FILE: canarinho/src/main/java/br/com/concrete/canarinho/DigitoPara.java
class DigitoPara (line 38) | public final class DigitoPara {
method DigitoPara (line 47) | private DigitoPara(Builder builder) {
method calcula (line 63) | public final String calcula(String trecho) {
method somaDigitos (line 104) | private int somaDigitos(int total) {
method proximoMultiplicador (line 112) | private int proximoMultiplicador(int multiplicadorDaVez) {
class Builder (line 127) | public static final class Builder {
method mod (line 142) | public final Builder mod(int modulo) {
method comMultiplicadoresDeAte (line 157) | public final Builder comMultiplicadoresDeAte(int inicio, int fim) {
method somandoIndividualmente (line 177) | public final Builder somandoIndividualmente() {
method complementarAoModulo (line 190) | public final Builder complementarAoModulo() {
method trocandoPorSeEncontrar (line 202) | public final Builder trocandoPorSeEncontrar(String substituto, Integ...
method comMultiplicadores (line 221) | public final Builder comMultiplicadores(Integer... multiplicadoresEm...
method build (line 237) | public final DigitoPara build() {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/Formatador.java
type Formatador (line 9) | public interface Formatador {
method formata (line 79) | String formata(String value);
method desformata (line 87) | String desformata(String value);
method estaFormatado (line 95) | boolean estaFormatado(String value);
method podeSerFormatado (line 103) | boolean podeSerFormatado(String value);
class Padroes (line 108) | abstract class Padroes {
method Padroes (line 130) | private Padroes() {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorBase.java
class FormatadorBase (line 10) | final class FormatadorBase implements Formatador {
method FormatadorBase (line 28) | FormatadorBase(
method formata (line 40) | @Override
method desformata (line 54) | @Override
method estaFormatado (line 69) | @Override
method podeSerFormatado (line 79) | @Override
method matchAndReplace (line 84) | private String matchAndReplace(Matcher matcher, String replacement) {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorBoleto.java
class FormatadorBoleto (line 11) | public final class FormatadorBoleto implements Formatador {
method FormatadorBoleto (line 41) | private FormatadorBoleto() {
method formata (line 44) | @Override
method desformata (line 54) | @Override
method estaFormatado (line 64) | @Override
method podeSerFormatado (line 74) | @Override
method ehTributo (line 84) | private boolean ehTributo(String value) {
method getInstance (line 93) | static FormatadorBoleto getInstance() {
class SingletonHolder (line 97) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorCEP.java
class FormatadorCEP (line 6) | public final class FormatadorCEP implements Formatador {
method FormatadorCEP (line 8) | private FormatadorCEP() {
method getInstance (line 11) | static FormatadorCEP getInstance() {
method formata (line 15) | @Override
method desformata (line 20) | @Override
method estaFormatado (line 25) | @Override
method podeSerFormatado (line 30) | @Override
class SingletonHolder (line 39) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorCPFCNPJ.java
class FormatadorCPFCNPJ (line 7) | public final class FormatadorCPFCNPJ implements Formatador {
method FormatadorCPFCNPJ (line 9) | private FormatadorCPFCNPJ() {
method getInstance (line 12) | static FormatadorCPFCNPJ getInstance() {
method formata (line 16) | @Override
method desformata (line 25) | @Override
method estaFormatado (line 34) | @Override
method podeSerFormatado (line 43) | @Override
method ehCpf (line 56) | private boolean ehCpf(String value) {
class SingletonHolder (line 66) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorLinhaDigitavel.java
class FormatadorLinhaDigitavel (line 16) | public final class FormatadorLinhaDigitavel implements Formatador {
method FormatadorLinhaDigitavel (line 18) | private FormatadorLinhaDigitavel() {
method getInstance (line 21) | static FormatadorLinhaDigitavel getInstance() {
method formata (line 25) | @Override
method desformata (line 79) | @Override
method estaFormatado (line 131) | @Override
method podeSerFormatado (line 136) | @Override
class SingletonHolder (line 143) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorTelefone.java
class FormatadorTelefone (line 8) | public final class FormatadorTelefone implements Formatador {
method FormatadorTelefone (line 40) | private FormatadorTelefone() {
method getInstance (line 43) | static FormatadorTelefone getInstance() {
method formata (line 47) | @Override
method desformata (line 56) | @Override
method estaFormatado (line 65) | @Override
method podeSerFormatado (line 74) | @Override
method ehNoveDigitos (line 83) | private boolean ehNoveDigitos(String value) {
class SingletonHolder (line 93) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorValor.java
class FormatadorValor (line 20) | public final class FormatadorValor implements Formatador {
method FormatadorValor (line 45) | private FormatadorValor(boolean comSimboloReal) {
method getInstance (line 55) | static FormatadorValor getInstance(boolean comSimboloReal) {
method formata (line 61) | @Override
method desformata (line 67) | @Override
method estaFormatado (line 88) | @Override
method podeSerFormatado (line 98) | @Override
class SingletonHolder (line 108) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/Validador.java
type Validador (line 14) | public interface Validador {
method ehValido (line 47) | boolean ehValido(String valor);
method ehValido (line 57) | ResultadoParcial ehValido(Editable valor, ResultadoParcial resultadoPa...
class ResultadoParcial (line 62) | class ResultadoParcial {
method isValido (line 68) | public boolean isValido() {
method isParcialmenteValido (line 72) | public boolean isParcialmenteValido() {
method getMensagem (line 76) | public String getMensagem() {
method totalmenteValido (line 86) | public ResultadoParcial totalmenteValido(boolean valido) {
method parcialmenteValido (line 97) | public ResultadoParcial parcialmenteValido(boolean parcialmenteValid...
method mensagem (line 108) | public ResultadoParcial mensagem(String mensagem) {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorBoleto.java
class ValidadorBoleto (line 16) | public final class ValidadorBoleto implements Validador {
method ValidadorBoleto (line 41) | private ValidadorBoleto() {
method getInstance (line 44) | public static ValidadorBoleto getInstance() {
method ehValido (line 48) | @Override
method ehValido (line 59) | @Override
method validaNormal (line 85) | private ResultadoParcial validaNormal(String valor, ResultadoParcial r...
method validaTributo (line 106) | private ResultadoParcial validaTributo(String valor, ResultadoParcial ...
method ehTributo (line 136) | private boolean ehTributo(CharSequence valor) {
method validaBloco (line 140) | private boolean validaBloco(String valor, ResultadoParcial resultadoPa...
class SingletonHolder (line 162) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCEP.java
class ValidadorCEP (line 12) | public final class ValidadorCEP implements Validador {
method ValidadorCEP (line 15) | private ValidadorCEP() {
method getInstance (line 18) | public static ValidadorCEP getInstance() {
method ehValido (line 22) | @Override
method ehValido (line 34) | @Override
class SingletonHolder (line 55) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCNPJ.java
class ValidadorCNPJ (line 13) | public final class ValidadorCNPJ implements Validador {
method ValidadorCNPJ (line 21) | private ValidadorCNPJ() {
method getInstance (line 24) | public static ValidadorCNPJ getInstance() {
method ehValido (line 28) | @Override
method ehValido (line 50) | @Override
class SingletonHolder (line 71) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCPF.java
class ValidadorCPF (line 13) | public final class ValidadorCPF implements Validador {
method ValidadorCPF (line 22) | private ValidadorCPF() {
method getInstance (line 25) | static ValidadorCPF getInstance() {
method ehValido (line 29) | @Override
method ehValido (line 55) | @Override
method estaNaListaNegra (line 80) | private boolean estaNaListaNegra(String valor) {
class SingletonHolder (line 93) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCPFCNPJ.java
class ValidadorCPFCNPJ (line 7) | public final class ValidadorCPFCNPJ implements Validador {
method ValidadorCPFCNPJ (line 10) | private ValidadorCPFCNPJ() {
method getInstance (line 13) | public static ValidadorCPFCNPJ getInstance() {
method ehValido (line 17) | @Override
method ehValido (line 30) | @Override
method ehCpf (line 43) | private boolean ehCpf(String valor) {
class SingletonHolder (line 52) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorTelefone.java
class ValidadorTelefone (line 7) | public final class ValidadorTelefone implements Validador {
method ValidadorTelefone (line 10) | private ValidadorTelefone() {
method getInstance (line 13) | public static ValidadorTelefone getInstance() {
method ehValido (line 17) | @Override
method ehValido (line 28) | @Override
class SingletonHolder (line 48) | private static class SingletonHolder {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/BaseCanarinhoTextWatcher.java
class BaseCanarinhoTextWatcher (line 16) | public abstract class BaseCanarinhoTextWatcher implements TextWatcher {
method beforeTextChanged (line 22) | @Override
method onTextChanged (line 27) | @Override
method isMudancaInterna (line 32) | public boolean isMudancaInterna() {
method getEventoDeValidacao (line 36) | @SuppressWarnings("unchecked")
method setEventoDeValidacao (line 41) | public void setEventoDeValidacao(EventoDeValidacao eventoDeValidacao) {
method isApagouCaracter (line 52) | protected boolean isApagouCaracter(Editable s) {
method atualizaTexto (line 67) | protected void atualizaTexto(Validador validador, Validador.ResultadoP...
method efetuaValidacao (line 91) | protected void efetuaValidacao(Validador validador, Validador.Resultad...
method trataAdicaoRemocaoDeCaracter (line 120) | protected StringBuilder trataAdicaoRemocaoDeCaracter(Editable s, char[...
method trataAdicaoDeCaracter (line 126) | private StringBuilder trataAdicaoDeCaracter(Editable s, char[] mascara) {
method trataRemocaoDeCaracter (line 131) | private StringBuilder trataRemocaoDeCaracter(Editable s, char[] mascar...
method carregarMascara (line 159) | private StringBuilder carregarMascara(String s, char[] mascara) {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/BoletoBancarioTextWatcher.java
class BoletoBancarioTextWatcher (line 18) | public final class BoletoBancarioTextWatcher extends BaseCanarinhoTextWa...
method BoletoBancarioTextWatcher (line 35) | public BoletoBancarioTextWatcher(EventoDeValidacao callbackErros) {
method afterTextChanged (line 39) | @Override
method getResultadoParcial (line 71) | public Validador.ResultadoParcial getResultadoParcial() {
method efetuaValidacao (line 75) | @Override
method verificaFiltro (line 118) | private void verificaFiltro(final Editable s, final boolean tributo) {
method ehTributo (line 128) | private boolean ehTributo(Editable e) {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/CEPTextWatcher.java
class CEPTextWatcher (line 15) | public final class CEPTextWatcher extends BaseCanarinhoTextWatcher {
method CEPTextWatcher (line 30) | public CEPTextWatcher(EventoDeValidacao callbackErros) {
method afterTextChanged (line 34) | @Override
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/CPFCNPJTextWatcher.java
class CPFCNPJTextWatcher (line 17) | public class CPFCNPJTextWatcher extends BaseCanarinhoTextWatcher {
method CPFCNPJTextWatcher (line 29) | public CPFCNPJTextWatcher() {
method CPFCNPJTextWatcher (line 37) | public CPFCNPJTextWatcher(EventoDeValidacao callbackErros) {
method afterTextChanged (line 41) | @Override
method ehCpf (line 56) | private boolean ehCpf(Editable e) {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/MascaraNumericaTextWatcher.java
class MascaraNumericaTextWatcher (line 16) | public final class MascaraNumericaTextWatcher extends BaseCanarinhoTextW...
method MascaraNumericaTextWatcher (line 28) | public MascaraNumericaTextWatcher(String mascara) {
method MascaraNumericaTextWatcher (line 32) | private MascaraNumericaTextWatcher(Builder builder) {
method afterTextChanged (line 42) | @Override
class Builder (line 63) | public static final class Builder {
method comValidador (line 76) | public Builder comValidador(Validador validador) {
method comCallbackDeValidacao (line 88) | public Builder comCallbackDeValidacao(EventoDeValidacao callbackErro...
method paraMascara (line 101) | public Builder paraMascara(String mascara) {
method build (line 111) | public final MascaraNumericaTextWatcher build() {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/TelefoneTextWatcher.java
class TelefoneTextWatcher (line 17) | public final class TelefoneTextWatcher extends BaseCanarinhoTextWatcher {
method TelefoneTextWatcher (line 32) | public TelefoneTextWatcher(EventoDeValidacao callbackErros) {
method afterTextChanged (line 36) | @Override
method ehNoveDigitos (line 52) | private boolean ehNoveDigitos(Editable e) {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/ValorMonetarioWatcher.java
class ValorMonetarioWatcher (line 16) | public class ValorMonetarioWatcher implements TextWatcher {
method ValorMonetarioWatcher (line 25) | public ValorMonetarioWatcher() {
method ValorMonetarioWatcher (line 35) | ValorMonetarioWatcher(boolean comSimboloReal, boolean mantemZerosAoLim...
method beforeTextChanged (line 42) | @Override
method onTextChanged (line 47) | @Override
method afterTextChanged (line 52) | @Override
method atualizaTexto (line 79) | private void atualizaTexto(Editable editable, String valor) {
class Builder (line 100) | public static class Builder {
method comMantemZerosAoLimpar (line 110) | public Builder comMantemZerosAoLimpar() {
method comSimboloReal (line 120) | public Builder comSimboloReal() {
method build (line 130) | public ValorMonetarioWatcher build() {
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/evento/EventoDeValidacao.java
type EventoDeValidacao (line 7) | public interface EventoDeValidacao {
method invalido (line 15) | void invalido(String valorAtual, String mensagem);
method parcialmenteValido (line 23) | void parcialmenteValido(String valorAtual);
method totalmenteValido (line 30) | void totalmenteValido(String valorAtual);
FILE: canarinho/src/main/java/br/com/concrete/canarinho/watcher/evento/EventoDeValidacaoDeBoleto.java
type EventoDeValidacaoDeBoleto (line 7) | public interface EventoDeValidacaoDeBoleto extends EventoDeValidacao {
method invalido (line 15) | void invalido(String valorAtual, int blocoInvalido);
FILE: sample/src/androidTest/java/br/com/concrete/canarinho/sample/BugOnApi28Test.java
class BugOnApi28Test (line 12) | @RunWith(AndroidJUnit4.class)
method moneyFormatSuccessfulRunsOnApi28 (line 15) | @Test
method moneyFormatSuccessfulRunsOnApi27 (line 22) | @Test
FILE: sample/src/androidTest/java/br/com/concrete/canarinho/sample/DemoWatchersInstrumentationTest.java
class DemoWatchersInstrumentationTest (line 36) | @RunWith(AndroidJUnit4.class)
method consegueDigitarUmBoletoNormalValido (line 42) | @Test
method consegueValidarUmBoletoSetandoOCodigoInteiro (line 53) | @Test
method consegueValidarUmBoletoTributoSetandoOCodigoInteiro (line 64) | @Test
method consegueDigitarUmBoletoNormalComBlocosInvalidos (line 75) | @Test
method consegueDigitarUmBoletoTributoValido (line 96) | @Test
method consegueDigitarUmBoletoNormalComBlocosInvalidosComMensagemCustomizada (line 117) | @Test
method consegueDigitarUmCPFValido (line 138) | @Test
method consegueDigitarUmCPFInvalido (line 150) | @Test
method consegueDigitarUmCNPJValido (line 159) | @Test
method consegueDigitarUmCNPJInvalido (line 171) | @Test
method consegueDigitarUmTelefoneValido (line 179) | @Test
method consegueDigitarUmValorMonetarioFormatado (line 197) | @Test
method consegueDigitarCPFCNPJValido (line 227) | @Test
method consegueDigitarCPFCNPJInvalido (line 238) | @Test
method consegueDigitarUmCEPValido (line 250) | @Test
method consegueUtilizarUmaMascaraGenericaSemValidadorOuEvento (line 261) | @Test
method navigateToTab (line 271) | private void navigateToTab(Watchers watcher) {
method paste (line 280) | private ViewAction paste(final String type) {
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/activity/MainActivity.java
class MainActivity (line 15) | public class MainActivity extends AppCompatActivity {
method onCreate (line 17) | @Override
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/adapter/WatchersPagerAdapter.java
class WatchersPagerAdapter (line 8) | public class WatchersPagerAdapter extends FragmentPagerAdapter {
method WatchersPagerAdapter (line 12) | public WatchersPagerAdapter(FragmentManager fragmentManager) {
method getItem (line 16) | @Override
method getCount (line 21) | @Override
method getPageTitle (line 26) | @Override
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/fragment/BaseWatcherFragment.java
class BaseWatcherFragment (line 6) | public abstract class BaseWatcherFragment extends Fragment {
method setModel (line 10) | public void setModel(Watchers model) {
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/fragment/CanarinhoValorMonetarioWatcherFragment.java
class CanarinhoValorMonetarioWatcherFragment (line 19) | public class CanarinhoValorMonetarioWatcherFragment extends BaseWatcherF...
method onCreateView (line 26) | @Nullable
method bind (line 39) | public CanarinhoValorMonetarioWatcherFragment bind(Watchers model) {
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/fragment/WatcherFragment.java
class WatcherFragment (line 18) | public class WatcherFragment extends BaseWatcherFragment {
method newInstance (line 25) | public static WatcherFragment newInstance(Watchers model) {
method onCreateView (line 32) | @Nullable
method bind (line 44) | public WatcherFragment bind(Watchers model) {
FILE: sample/src/main/java/br/com/concrete/canarinho/sample/ui/model/Watchers.java
type Watchers (line 22) | public enum Watchers {
method buildFragment (line 25) | @Override
method setupWatcher (line 30) | @Override
method buildFragment (line 36) | @Override
method setupWatcher (line 41) | @Override
method buildFragment (line 48) | @Override
method setupWatcher (line 53) | @Override
method buildFragment (line 64) | @Override
method setupWatcher (line 69) | @Override
method buildFragment (line 80) | @Override
method setupWatcher (line 85) | @Override
method buildFragment (line 92) | @Override
method setupWatcher (line 97) | @Override
method buildFragment (line 104) | @Override
method setupWatcher (line 109) | @Override
method buildFragment (line 116) | @Override
method setupWatcher (line 121) | @Override
method buildFragment (line 128) | @Override
method setupWatcher (line 133) | @Override
method Watchers (line 142) | Watchers(String title, String hint) {
method getTitle (line 147) | public String getTitle() {
method getHint (line 151) | public String getHint() {
method buildFragment (line 155) | public abstract BaseWatcherFragment buildFragment();
method setupWatcher (line 157) | public abstract TextWatcher setupWatcher(TextInputLayout textInputLayo...
class SampleEventoDeValidacao (line 163) | public static class SampleEventoDeValidacao implements EventoDeValidac...
method SampleEventoDeValidacao (line 167) | public SampleEventoDeValidacao(TextInputLayout textInputLayout) {
method invalido (line 171) | @Override
method parcialmenteValido (line 176) | @Override
method totalmenteValido (line 182) | @Override
class EventoDeValidacaoBoleto (line 196) | public static class EventoDeValidacaoBoleto
method EventoDeValidacaoBoleto (line 200) | EventoDeValidacaoBoleto(TextInputLayout textInputLayout) {
method invalido (line 204) | @Override
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorBOLETO.java
class TesteFormatadorBOLETO (line 12) | @RunWith(AndroidJUnit4.class)
method consegueFormatar (line 15) | @Test
method consegueDesformatar (line 33) | @Test
method consegueDizerSeEstaFormatado (line 52) | @Test
method consegueDizerSePodeFormatar (line 63) | @Test
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCEP.java
class TesteFormatadorCEP (line 13) | @RunWith(AndroidJUnit4.class)
method consegueFormatar (line 16) | @Test
method consegueDesformatar (line 30) | @Test
method consegueDizerSeEstaFormatado (line 44) | @Test
method consegueDizerSePodeFormatar (line 62) | @Test
method assertThrowsFormat (line 73) | private void assertThrowsFormat(String valor) {
method assertThrowsDesformat (line 81) | private void assertThrowsDesformat(String valor) {
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCNPJ.java
class TesteFormatadorCNPJ (line 13) | @RunWith(AndroidJUnit4.class)
method consegueFormatar (line 16) | @Test
method consegueDesformatar (line 35) | @Test
method consegueDizerSeEstaFormatado (line 54) | @Test
method consegueDizerSePodeFormatar (line 72) | @Test
method assertThrowsFormat (line 83) | private void assertThrowsFormat(String valor) {
method assertThrowsDesformat (line 91) | private void assertThrowsDesformat(String valor) {
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCPF.java
class TesteFormatadorCPF (line 13) | @RunWith(AndroidJUnit4.class)
method consegueFormatarCPF (line 16) | @Test
method consegueDesformatarCPF (line 38) | @Test
method consegueDizerSeEstaFormatado (line 60) | @Test
method consegueDizerSePodeFormatar (line 82) | @Test
method assertThrowsFormat (line 93) | private void assertThrowsFormat(String valor) {
method assertThrowsDesformat (line 101) | private void assertThrowsDesformat(String valor) {
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCPFCNPJ.java
class TesteFormatadorCPFCNPJ (line 13) | @RunWith(AndroidJUnit4.class)
method consegueFormatar (line 16) | @Test
method consegueDesformatar (line 53) | @Test
method consegueDizerSeEstaFormatado (line 90) | @Test
method consegueDizerSePodeFormatar (line 126) | @Test
method assertThrowsFormat (line 144) | private void assertThrowsFormat(String valor) {
method assertThrowsDesformat (line 152) | private void assertThrowsDesformat(String valor) {
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorLinhaDigitavel.java
class TesteFormatadorLinhaDigitavel (line 13) | @RunWith(AndroidJUnit4.class)
method consegueFormatarEDesformatar (line 16) | @Test
method consegueDizerSeEstaFormatado (line 29) | @Test
method consegueDizerSePodeFormatar (line 36) | @Test
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorTelefone.java
class TesteFormatadorTelefone (line 13) | @RunWith(AndroidJUnit4.class)
method consegueFormatar (line 16) | @Test
method consegueDesformatar (line 31) | @Test
method consegueDizerSeEstaFormatado (line 46) | @Test
method consegueDizerSePodeFormatar (line 62) | @Test
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorValor.java
class TesteFormatadorValor (line 13) | @RunWith(AndroidJUnit4.class)
method consegueFormatar (line 16) | @Test
method consegueFormatarComSimbolo (line 31) | @Test
method consegueDesformatar (line 46) | @Test
method consegueDesformatarComSimbolo (line 53) | @Test
method consegueDizerSeEstaFormatado (line 60) | @Test
method consegueDizerSePodeFormatar (line 74) | @Test
FILE: sample/src/test/java/br/com/concrete/canarinho/test/TesteValidadores.java
class TesteValidadores (line 12) | @RunWith(AndroidJUnit4.class)
method consegueValidarCPF (line 15) | @Test
method consegueValidarCNPJ (line 44) | @Test
method consegueValidarBoletoNormal (line 61) | @Test
method consegueValidarBoletoTributoSemSerTaxa (line 98) | @Test
method consegueValidarBoletoTributoDeTaxa (line 105) | @Test
method consegueValidarTelefone (line 115) | @Test
method consegueValidarCEP (line 124) | @Test
FILE: sample/src/test/java/br/com/concrete/canarinho/test/watcher/BoletoTextWatcherTest.java
class BoletoTextWatcherTest (line 20) | @RunWith(AndroidJUnit4.class)
method setUp (line 26) | @Before
method typing_canValidateEmptyState (line 45) | @Test
method typing_canValidateProperCharacters (line 52) | @Test
method deleting_canEmptyEditText (line 59) | @Test
method deleting_afterEmptyingEditTextItKeepsValidatingInput (line 70) | @Test
FILE: sample/src/test/java/br/com/concrete/canarinho/test/watcher/ValorMonetarioWatcherTest.java
class ValorMonetarioWatcherTest (line 20) | @RunWith(AndroidJUnit4.class)
method setUp (line 25) | @Before
method watcher_formataOk (line 37) | @Test
method watcher_formataOkComSimbolo (line 44) | @Test
method watcher_canEmptyTextAndKeepZeroes (line 54) | @Test
method watcher_canEmptyTextWithoutZeroes (line 70) | @Test
Condensed preview — 77 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (217K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 799,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\n\n---\n\n**Describe the bug**\nA clear and concise descriptio"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 560,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\n\n---\n\n**Is your feature request related to a problem? "
},
{
"path": ".github/workflows/android_master.yml",
"chars": 1092,
"preview": "name: Android Pull Request Master CI\n\non:\n pull_request:\n branches:\n - 'master'\n\njobs:\n Instrumented_Test:\n "
},
{
"path": ".gitignore",
"chars": 395,
"preview": ".gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.idea/\n*.iml\n\n# Built applica"
},
{
"path": "CHANGELOG.md",
"chars": 2068,
"preview": "# Changelog\n\n## 2.0.3\n - Migração do bintray para Github Package Registry\n\n## 2.0.2\n - Atualização androidx\n - "
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3224,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "LICENSE.txt",
"chars": 10142,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "PULL_REQUEST_TEMPLATE.md",
"chars": 288,
"preview": "# Título para o PullRequest\nO que acha de deixar uma descrição simples sobre o seu PR?\n\n## Tipo de mudança\n- [ ] Bug fix"
},
{
"path": "README.md",
"chars": 5309,
"preview": "# Android Canarinho\n\n\n jcenter()\n }\n\n dependencies {\n classpath 'com.and"
},
{
"path": "canarinho/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "canarinho/build.gradle",
"chars": 630,
"preview": "apply plugin: 'com.android.library'\napply from: \"$rootDir/tools/linters/linters.gradle\"\napply from: \"$rootDir/tools/publ"
},
{
"path": "canarinho/src/main/AndroidManifest.xml",
"chars": 49,
"preview": "<manifest package=\"br.com.concrete.canarinho\" />\n"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/DigitoPara.java",
"chars": 7981,
"preview": "package br.com.concrete.canarinho;\n\nimport android.util.SparseArray;\n\nimport java.util.ArrayList;\nimport java.util.Array"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/formatador/Formatador.java",
"chars": 3662,
"preview": "package br.com.concrete.canarinho.formatador;\n\nimport java.util.regex.Pattern;\n\n/**\n * Interface de formatação. Formata "
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorBase.java",
"chars": 2806,
"preview": "package br.com.concrete.canarinho.formatador;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * C"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorBoleto.java",
"chars": 2936,
"preview": "package br.com.concrete.canarinho.formatador;\n\nimport java.util.regex.Pattern;\n\n/**\n * Formatador especializado para lin"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorCEP.java",
"chars": 978,
"preview": "package br.com.concrete.canarinho.formatador;\n\n/**\n * Formatador para CEP. Segue o padrão 99999-999.\n */\npublic final cl"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorCPFCNPJ.java",
"chars": 1777,
"preview": "package br.com.concrete.canarinho.formatador;\n\n/**\n * Formatador para CPF e CNPJ no mesmo campo. Formata como CPF até 11"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorLinhaDigitavel.java",
"chars": 5931,
"preview": "package br.com.concrete.canarinho.formatador;\n\nimport br.com.concrete.canarinho.DigitoPara;\nimport br.com.concrete.canar"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorTelefone.java",
"chars": 2709,
"preview": "package br.com.concrete.canarinho.formatador;\n\nimport java.util.regex.Pattern;\n\n/**\n * Formata no padrão de telefone bra"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/formatador/FormatadorValor.java",
"chars": 3715,
"preview": "package br.com.concrete.canarinho.formatador;\n\nimport android.os.Build;\n\nimport java.math.BigDecimal;\nimport java.text.D"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/validator/Validador.java",
"chars": 3278,
"preview": "package br.com.concrete.canarinho.validator;\n\nimport android.text.Editable;\n\n/**\n * Interface de validação de campos. Há"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorBoleto.java",
"chars": 5386,
"preview": "package br.com.concrete.canarinho.validator;\n\nimport android.text.Editable;\nimport android.text.SpannableStringBuilder;\n"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCEP.java",
"chars": 1619,
"preview": "package br.com.concrete.canarinho.validator;\n\nimport android.text.Editable;\n\nimport br.com.concrete.canarinho.formatador"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCNPJ.java",
"chars": 2233,
"preview": "package br.com.concrete.canarinho.validator;\n\nimport android.text.Editable;\n\nimport br.com.concrete.canarinho.DigitoPara"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCPF.java",
"chars": 2954,
"preview": "package br.com.concrete.canarinho.validator;\n\nimport android.text.Editable;\n\nimport br.com.concrete.canarinho.DigitoPara"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorCPFCNPJ.java",
"chars": 1636,
"preview": "package br.com.concrete.canarinho.validator;\n\nimport android.text.Editable;\n\nimport br.com.concrete.canarinho.formatador"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/validator/ValidadorTelefone.java",
"chars": 1571,
"preview": "package br.com.concrete.canarinho.validator;\n\nimport android.text.Editable;\n\nimport br.com.concrete.canarinho.formatador"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/watcher/BaseCanarinhoTextWatcher.java",
"chars": 6584,
"preview": "package br.com.concrete.canarinho.watcher;\n\nimport android.text.Editable;\nimport android.text.Selection;\nimport android."
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/watcher/BoletoBancarioTextWatcher.java",
"chars": 4822,
"preview": "package br.com.concrete.canarinho.watcher;\n\nimport android.text.Editable;\nimport android.text.InputFilter;\n\nimport br.co"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/watcher/CEPTextWatcher.java",
"chars": 1610,
"preview": "package br.com.concrete.canarinho.watcher;\n\nimport android.text.Editable;\nimport android.text.InputFilter;\n\nimport br.co"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/watcher/CPFCNPJTextWatcher.java",
"chars": 1997,
"preview": "package br.com.concrete.canarinho.watcher;\n\nimport android.text.Editable;\nimport android.text.InputFilter;\n\nimport br.co"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/watcher/MascaraNumericaTextWatcher.java",
"chars": 3916,
"preview": "package br.com.concrete.canarinho.watcher;\n\nimport android.text.Editable;\nimport android.text.InputFilter;\n\nimport br.co"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/watcher/TelefoneTextWatcher.java",
"chars": 2036,
"preview": "package br.com.concrete.canarinho.watcher;\n\nimport android.text.Editable;\nimport android.text.InputFilter;\n\nimport br.co"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/watcher/ValorMonetarioWatcher.java",
"chars": 3862,
"preview": "package br.com.concrete.canarinho.watcher;\n\nimport android.text.Editable;\nimport android.text.InputFilter;\nimport androi"
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/watcher/evento/EventoDeValidacao.java",
"chars": 1124,
"preview": "package br.com.concrete.canarinho.watcher.evento;\n\n/**\n * Interface para quem estiver usando este TextWatcher poder ter "
},
{
"path": "canarinho/src/main/java/br/com/concrete/canarinho/watcher/evento/EventoDeValidacaoDeBoleto.java",
"chars": 546,
"preview": "package br.com.concrete.canarinho.watcher.evento;\n\n/**\n * Evento de validação específico para boletos que permite saber "
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 202,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dist"
},
{
"path": "gradle.properties",
"chars": 154,
"preview": "org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\norg.gradle.paral"
},
{
"path": "gradlew",
"chars": 5080,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "gradlew.bat",
"chars": 2404,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "sample/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "sample/build.gradle",
"chars": 2315,
"preview": "apply plugin: 'com.android.application'\napply plugin: 'jacoco'\n\nandroid {\n compileSdkVersion 31\n\n defaultConfig {\n"
},
{
"path": "sample/proguard-rules.pro",
"chars": 667,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /h"
},
{
"path": "sample/src/androidTest/java/br/com/concrete/canarinho/sample/BugOnApi28Test.java",
"chars": 809,
"preview": "package br.com.concrete.canarinho.sample;\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport androidx.test.fi"
},
{
"path": "sample/src/androidTest/java/br/com/concrete/canarinho/sample/DemoWatchersInstrumentationTest.java",
"chars": 11080,
"preview": "package br.com.concrete.canarinho.sample;\n\nimport android.os.Build;\nimport android.view.View;\nimport android.widget.Edit"
},
{
"path": "sample/src/main/AndroidManifest.xml",
"chars": 643,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"br.com.concrete.canarinho.sample\">\n\n "
},
{
"path": "sample/src/main/java/br/com/concrete/canarinho/sample/ui/activity/MainActivity.java",
"chars": 1252,
"preview": "package br.com.concrete.canarinho.sample.ui.activity;\n\nimport android.os.Bundle;\n\nimport com.google.android.material.tab"
},
{
"path": "sample/src/main/java/br/com/concrete/canarinho/sample/ui/adapter/WatchersPagerAdapter.java",
"chars": 825,
"preview": "package br.com.concrete.canarinho.sample.ui.adapter;\n\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.ap"
},
{
"path": "sample/src/main/java/br/com/concrete/canarinho/sample/ui/fragment/BaseWatcherFragment.java",
"chars": 326,
"preview": "package br.com.concrete.canarinho.sample.ui.fragment;\n\nimport androidx.fragment.app.Fragment;\nimport br.com.concrete.can"
},
{
"path": "sample/src/main/java/br/com/concrete/canarinho/sample/ui/fragment/CanarinhoValorMonetarioWatcherFragment.java",
"chars": 1883,
"preview": "package br.com.concrete.canarinho.sample.ui.fragment;\n\nimport android.os.Bundle;\nimport android.text.TextWatcher;\nimport"
},
{
"path": "sample/src/main/java/br/com/concrete/canarinho/sample/ui/fragment/WatcherFragment.java",
"chars": 1890,
"preview": "package br.com.concrete.canarinho.sample.ui.fragment;\n\nimport android.os.Bundle;\nimport android.text.TextWatcher;\nimport"
},
{
"path": "sample/src/main/java/br/com/concrete/canarinho/sample/ui/model/Watchers.java",
"chars": 7158,
"preview": "package br.com.concrete.canarinho.sample.ui.model;\n\nimport android.text.TextWatcher;\n\nimport com.google.android.material"
},
{
"path": "sample/src/main/res/layout/fragment_canarinho_watcher.xml",
"chars": 1496,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.card.MaterialCardView xmlns:android=\"http://schemas."
},
{
"path": "sample/src/main/res/layout/fragment_valor_monetario_watcher.xml",
"chars": 1496,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.card.MaterialCardView xmlns:android=\"http://schemas."
},
{
"path": "sample/src/main/res/layout/main_activity.xml",
"chars": 2169,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schema"
},
{
"path": "sample/src/main/res/values/strings.xml",
"chars": 80,
"preview": "<resources>\n <string name=\"app_name\">Android Canarinho</string>\n</resources>\n"
},
{
"path": "sample/src/main/res/values/styles.xml",
"chars": 326,
"preview": "<resources>\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.MaterialComponents.Light.NoAct"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorBOLETO.java",
"chars": 3001,
"preview": "package br.com.concrete.canarinho.test;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport androidx.test.ex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCEP.java",
"chars": 2996,
"preview": "package br.com.concrete.canarinho.test;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport androidx.test.ex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCNPJ.java",
"chars": 3781,
"preview": "package br.com.concrete.canarinho.test;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport androidx.test.ex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCPF.java",
"chars": 4270,
"preview": "package br.com.concrete.canarinho.test;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport androidx.test.ex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorCPFCNPJ.java",
"chars": 7222,
"preview": "package br.com.concrete.canarinho.test;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport androidx.test.ex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorLinhaDigitavel.java",
"chars": 1739,
"preview": "package br.com.concrete.canarinho.test;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport androidx.test.ex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorTelefone.java",
"chars": 2512,
"preview": "package br.com.concrete.canarinho.test;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport androidx.test.ex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/TesteFormatadorValor.java",
"chars": 3601,
"preview": "package br.com.concrete.canarinho.test;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport androidx.test.ex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/TesteValidadores.java",
"chars": 5425,
"preview": "package br.com.concrete.canarinho.test;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport androidx.test.ex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/watcher/BoletoTextWatcherTest.java",
"chars": 3012,
"preview": "package br.com.concrete.canarinho.test.watcher;\n\nimport android.widget.EditText;\n\nimport com.google.android.material.tex"
},
{
"path": "sample/src/test/java/br/com/concrete/canarinho/test/watcher/ValorMonetarioWatcherTest.java",
"chars": 2870,
"preview": "package br.com.concrete.canarinho.test.watcher;\n\nimport android.app.Activity;\nimport android.widget.EditText;\n\nimport co"
},
{
"path": "settings.gradle",
"chars": 32,
"preview": "include ':sample', ':canarinho'\n"
},
{
"path": "tools/linters/checkstyle/checkstyle.xml",
"chars": 12379,
"preview": "<?xml version=\"1.0\"?><!DOCTYPE module PUBLIC\n \"-//Puppy Crawl//DTD Check Configuration 1.3//EN\"\n \"http://checkstyl"
},
{
"path": "tools/linters/checkstyle/suppressions.xml",
"chars": 369,
"preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE suppressions PUBLIC\n \"-//Puppy Crawl//DTD Suppressions 1.1//EN\"\n \"http://www.puppy"
},
{
"path": "tools/linters/linters.gradle",
"chars": 1099,
"preview": "apply plugin: 'checkstyle'\napply plugin: 'pmd'\n\ncheck.dependsOn 'checkstyle', 'pmd'\n\ncheckstyle {\n toolVersion '8.17'"
},
{
"path": "tools/linters/pmd/pmd-ruleset.xml",
"chars": 1261,
"preview": "<?xml version=\"1.0\"?>\n<ruleset xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" name=\"Android Application Rules\"\n "
},
{
"path": "tools/publish.gradle",
"chars": 638,
"preview": "apply plugin: 'maven-publish'\n\nafterEvaluate {\n publishing {\n publications {\n stable(MavenPublicati"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the concretesolutions/canarinho GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 77 files (196.4 KB), approximately 51.7k tokens, and a symbol index with 327 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.