# PhotoPicker

> Capture a photo or video with the device camera, returning the path of the file it produced.

Source: https://crossfly.dev/docs/ui/widgets/photopicker — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# PhotoPicker

`PhotoPicker` captures a **photo** or records a **video** (with/without audio) using the device camera, with effects (brightness/contrast/sharpness), camera switching and optional confirmation. It follows the picker pattern (see [`Picker`](/docs/ui/widgets/picker)), but the result is a **file path** — or an error string.

> 🔴 · **Package:** `dev.crossfly.ui.picker` · **Extends:** `Picker implements GdxCamera.Listener` · **Hardware:** camera

## When to use
- Take a photo or record a video inside the app.

## When NOT to use
- Read a barcode/QR → use [`BarcodePicker`](/docs/ui/widgets/barcodepicker).

## Appearance
Full-screen camera preview (black background, no frame) with semi-transparent circular buttons: Cancel (X), Capture/Record (center), Switch camera and Settings/Pause. In photo mode with confirmation, a confirm/retake screen.

## Complete example (copy, paste and run)
`PhotoPicker` is a camera overlay — to open it, trigger it from a `Button`. The example reuses the basic usage and shows the returned path (or the error) in a `Label`.

```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.picker.*;

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Label saida = new Label("Nenhuma foto ainda");
      add(LAY_AUTO, saida);

      Button tirarFoto = new Button("Tirar foto")
         .onAction(c ->
         {
            PhotoPicker pp = new PhotoPicker(PhotoPicker.Preview.PHOTO);
            pp.show(answer ->
            {
               String caminho = (String) answer;
               if (pp.isError())
                  saida.setText("Falha na câmera: " + caminho);
               else
                  saida.setText("Foto salva em: " + caminho);
            });
         });
      add(LAY_AUTO, tirarFoto);
   }
}
```

> The snippets below show **only the core** (what goes inside `onShow()`). To run them, place them in the `MainWindow` skeleton above.

## Basic usage
```java
PhotoPicker pp = new PhotoPicker(PhotoPicker.Preview.PHOTO);
pp.show(answer ->
{
   String caminho = (String) answer;
   if (pp.isError())
      new Alert("Falha na câmera", caminho, "OK").show();
   else
      Debug.log("Foto salva em: " + caminho);
});
```

## Constructors
| Constructor | Description |
|---|---|
| `PhotoPicker(Preview type)` | Mode: `Preview.PHOTO`, `Preview.MUTED_VIDEO` or `Preview.AUDIO_VIDEO`. |

## Properties and customization
| Field | Type | Default | What it does |
|---|---|---|---|
| `PhotoPicker.defaultJpegQuality` (static) | `int` | 90 | JPG quality when saving the photo. |
| `PhotoPicker.showConfirmation` (static) | `boolean` | `true` | Confirm/retake screen in photo mode. |
| `videoQuality` | `Camera.AVQuality` | HIGH | Video quality. |
| `videoTimeLimit` | `int` (s) | 0 | Recording limit (0 = no limit). |

## Callbacks and events
| Callback | When it fires |
|---|---|
| `show(onAnswer)` | on closing, delivers the path `String` (or `"ERROR: ..."`). |

## Result (important)
`getAnswer()` returns the file **path** (JPG for the photo or MP4 for the video). In case of a camera error, it returns the string `"ERROR: ..."`.

> ⚠️ **Always check `isError()` first** before treating the value as a path — `getAnswer()` mixes path and error message.

## States
- `onAppPause`/`onAppResume` pause/resume the camera; `onRemove` stops everything and clears the listener.
- Camera error → records `answer = "ERROR: ..."` and closes.

### Video with audio and a 30 s limit
```java
PhotoPicker vp = new PhotoPicker(PhotoPicker.Preview.AUDIO_VIDEO);
vp.videoTimeLimit = 30;
vp.show(a -> Debug.log("MP4 em: " + a));
```

## Best practices
- **Check `isError()`** before opening the returned file.
- Do not reuse the same instance after `hide()` — the camera resources are released on close.

## Common mistakes
- **Treating `answer` as a path without checking `isError()`** — it may be `"ERROR: ..."`.
- **Assuming a portable path** — the video path is fixed at `/sdcard/...` (Android).

## See also
- Area guide: [`ui/`](/docs/ui) · Recipes: [`cookbook/`](/docs/cookbook)
- Pattern base: [`Picker`](/docs/ui/widgets/picker)
- Related components: [`BarcodePicker`](/docs/ui/widgets/barcodepicker)
