npm package: @kaymasoft/file-chooser
Description: Plugin Capacitor para Android que abre o chooser nativo para seleção de arquivo ou captura de foto, corrigindo problemas recentes em WebView/inputs file e permitindo integração com apps Ionic Angular.
Author: Enio K S Meyer
Latest version: 0.0.3

# @kaymasoft/file-chooser

Plugin para Android que permite abrir o chooser nativo para seleção de arquivo ou captura de foto, integrando com apps Ionic Angular via Capacitor.

## 🚀 Visão Geral

Este plugin resolve problemas específicos do Android 14/15 onde os inputs `<input type="file">` em WebViews não abrem o chooser nativo corretamente. Oferece uma solução robusta para seleção de arquivos e captura de fotos tanto dentro de WebViews quanto em apps nativos.

## 📋 Funcionalidades

- ✅ Abrir chooser nativo do Android
- ✅ Captura de fotos via câmera
- ✅ Seleção de arquivos de qualquer tipo
- ✅ Integração com WebViews (resolve problemas Android 14/15)
- ✅ Retorno de arquivos em base64 (opcional)
- ✅ Suporte multiplataforma (Android/Web)
- ✅ Gestão automática de permissões

## 📦 Instalação

```bash
npm install @kaymasoft/file-chooser
npx cap sync android
```

## ⚙️ Configuração

### Android

1. **Permissões necessárias** - Adicione ao `android/app/src/main/AndroidManifest.xml`:

```xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
```

2. **FileProvider** - Certifique-se que está configurado no `AndroidManifest.xml`:

```xml
<application>
  <!-- Outros componentes -->
  
  <provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
      android:name="android.support.FILE_PROVIDER_PATHS"
      android:resource="@xml/file_paths" />
  </provider>
</application>
```

3. **file_paths.xml** - Crie o arquivo `android/app/src/main/res/xml/file_paths.xml`:

```xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <cache-path name="images" path="images" />
    <external-files-path name="external_files" path="." />
    <files-path name="files" path="." />
</paths>
```

## 🛠️ Uso Básico

### Importação

```typescript
import FileChooser from '@kaymasoft/file-chooser';
```

### Exemplos de Uso

#### 1. Abrir Chooser Completo (Arquivo + Câmera)

```typescript
async function abrirFileChooser() {
  try {
    const result = await FileChooser.open({
      includeBase64: true // Opcional: inclui base64 no resultado
    });
    
    console.log('URI do arquivo:', result.uri);
    console.log('Origem:', result.source); // 'file', 'camera' ou 'unknown'
    console.log('Caminho:', result.path);
    
    if (result.base64) {
      console.log('Base64 disponível para WebView');
    }
  } catch (error) {
    console.error('Erro ao abrir chooser:', error);
  }
}
```

#### 2. Somente Seleção de Arquivos

```typescript
async function selecionarArquivo() {
  try {
    const result = await FileChooser.openFileOnly();
    console.log('Arquivo selecionado:', result.uri);
  } catch (error) {
    console.error('Seleção cancelada:', error);
  }
}
```

#### 3. Somente Captura de Foto

```typescript
async function capturarFoto() {
  try {
    const result = await FileChooser.openCameraOnly({
      includeBase64: true
    });
    console.log('Foto capturada:', result.uri);
    console.log('Base64:', result.base64);
  } catch (error) {
    console.error('Captura cancelada:', error);
  }
}
```

## 🌐 Integração com WebView

### Para corrigir inputs file em WebViews:

```typescript
import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx';
import FileChooser from '@kaymasoft/file-chooser';

async function abrirWebViewComFileChooser(url: string) {
  const browser = this.iab.create(url, '_blank', {
    location: 'no',
    toolbar: 'no'
  });

  browser.on('loadstop').subscribe(() => {
    // Injetar script para interceptar file inputs
    this.injectFileChooserScript(browser);
  });

  browser.on('message').subscribe(async (event: any) => {
    if (event.data.message === 'openFileChooser') {
      await this.handleFileChooserRequest(browser, event.data);
    }
  });
}

private injectFileChooserScript(browser: any) {
  const script = `
    document.addEventListener('click', function(e) {
      if (e.target.type === 'file') {
        e.preventDefault();
        var inputId = e.target.id || 'file_input_' + Date.now();
        e.target.id = inputId;
        
        window.webkit.messageHandlers.capacitor.postMessage({
          type: 'message',
          data: { message: 'openFileChooser', inputId: inputId }
        });
      }
    }, true);
  `;
  
  browser.executeScript({ code: script });
}

private async handleFileChooserRequest(browser: any, data: any) {
  try {
    const result = await FileChooser.open({ includeBase64: true });
    
    const injectScript = `
      var input = document.getElementById('${data.inputId}');
      if (input && '${result.base64}') {
        var base64 = '${result.base64}'.split(',')[1];
        var byteCharacters = atob(base64);
        var byteNumbers = new Array(byteCharacters.length);
        for (var i = 0; i < byteCharacters.length; i++) {
          byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        var byteArray = new Uint8Array(byteNumbers);
        var fileName = '${result.source}' === 'camera' ? 'photo.jpg' : 'file';
        var file = new File([byteArray], fileName);
        
        var dt = new DataTransfer();
        dt.items.add(file);
        input.files = dt.files;
        input.dispatchEvent(new Event('change', { bubbles: true }));
      }
    `;
    
    browser.executeScript({ code: injectScript });
  } catch (error) {
    console.error('Erro no FileChooser:', error);
  }
}
```

## 📱 Exemplo Completo em Ionic

```typescript
import { Component } from '@angular/core';
import FileChooser from '@kaymasoft/file-chooser';

@Component({
  selector: 'app-file-upload',
  template: `
    <ion-content>
      <ion-button (click)="abrirChooser()">
        Selecionar Arquivo ou Tirar Foto
      </ion-button>
      
      <ion-button (click)="somenteArquivo()">
        Somente Arquivo
      </ion-button>
      
      <ion-button (click)="somenteFoto()">
        Somente Foto
      </ion-button>
      
      <div *ngIf="arquivoSelecionado">
        <p>Arquivo: {{ arquivoSelecionado.uri }}</p>
        <p>Fonte: {{ arquivoSelecionado.source }}</p>
      </div>
    </ion-content>
  `
})
export class FileUploadPage {
  arquivoSelecionado: any = null;

  async abrirChooser() {
    try {
      this.arquivoSelecionado = await FileChooser.open({
        includeBase64: true
      });
    } catch (error) {
      console.error('Erro:', error);
    }
  }

  async somenteArquivo() {
    try {
      this.arquivoSelecionado = await FileChooser.openFileOnly();
    } catch (error) {
      console.error('Erro:', error);
    }
  }

  async somenteFoto() {
    try {
      this.arquivoSelecionado = await FileChooser.openCameraOnly({
        includeBase64: true
      });
    } catch (error) {
      console.error('Erro:', error);
    }
  }
}
```

## 🔧 API Reference

### Métodos

| Método | Descrição | Parâmetros |
|--------|-----------|------------|
| `open()` | Abre chooser com opções de arquivo e câmera | `options?: { includeBase64?: boolean }` |
| `openFileOnly()` | Abre somente seletor de arquivos | `options?: { includeBase64?: boolean }` |
| `openCameraOnly()` | Abre somente a câmera | `options?: { includeBase64?: boolean }` |

### Resultado (FileChooserResult)

```typescript
interface FileChooserResult {
  uri: string;           // URI do arquivo/foto
  path?: string;         // Caminho do arquivo
  source: 'file' | 'camera' | 'unknown';  // Origem do arquivo
  base64?: string;       // Base64 (se includeBase64: true)
}
```

## 🐛 Solução de Problemas

### Erro de Compilação Java
Se receber erro sobre versão Java, certifique-se que o `build.gradle` do plugin usa a mesma versão Java do seu app:

```gradle
android {
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_17
    targetCompatibility JavaVersion.VERSION_17
  }
}
```

### Permissões não Funcionam
Execute os comandos:
```bash
npx cap clean android
npx cap sync android
``` 

### WebView não Intercepta File Inputs
Certifique-se de injetar o script após o `loadstop` e usar o postMessage correto para sua versão do Capacitor.

## 📄 Licença
 
MIT License - veja [LICENSE](LICENSE) para detalhes.

## 👨‍💻 Autor

**Enio K S Meyer** - [Kaymasoft](https://kaymasoft.com)

## 🤝 Contribuições

Contribuições são bem-vindas! Por favor, abra uma issue ou pull request.

## 🔗 Links

- [GitHub Repository](https://github.com/E-KMeyer/capacitor-file-chooser)
- [NPM Package](https://www.npmjs.com/package/@kaymasoft/file-chooser)
- [Issues](https://github.com/E-KMeyer/capacitor-file-chooser/issues)