depois do ollama
This commit is contained in:
@@ -4,26 +4,26 @@ import android.content.Context;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.google.ai.client.generativeai.GenerativeModel;
|
import org.json.JSONArray;
|
||||||
import com.google.ai.client.generativeai.java.GenerativeModelFutures;
|
|
||||||
import com.google.ai.client.generativeai.type.Content;
|
|
||||||
import com.google.ai.client.generativeai.type.GenerateContentResponse;
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class InvoiceScannerHelper {
|
public class InvoiceScannerHelper {
|
||||||
|
|
||||||
private static final String API_KEY = "AIzaSyCoUZSXfEk43LfPtkCCjsnQ_ZMWX7NG1xQ"; // Substitua pela sua chave API
|
private static final String API_URL = "https://apichat.epvc.pt/api/chat";
|
||||||
|
private static final String MODEL_NAME = "qwen3.6";
|
||||||
private static final String PROMPT = "Analisa esta fatura. Devolve APENAS um objeto JSON com os campos: 'valor' (Double, apenas números e ponto decimal), 'descricao' (String curta, nome do estabelecimento ou produto), 'categoria' (String, ex: Alimentação, Transporte, Lazer, Saúde, Casa, Educação, Outros), e 'data' (String formato DD/MM/AAAA). Se não conseguires ler algum, coloca valor padrão ou string vazia.";
|
private static final String PROMPT = "Analisa esta fatura. Devolve APENAS um objeto JSON com os campos: 'valor' (Double, apenas números e ponto decimal), 'descricao' (String curta, nome do estabelecimento ou produto), 'categoria' (String, ex: Alimentação, Transporte, Lazer, Saúde, Casa, Educação, Outros), e 'data' (String formato DD/MM/AAAA). Se não conseguires ler algum, coloca valor padrão ou string vazia.";
|
||||||
|
|
||||||
public interface ScanCallback {
|
public interface ScanCallback {
|
||||||
@@ -32,81 +32,119 @@ public class InvoiceScannerHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void scanInvoice(Context context, Uri imageUri, ScanCallback callback) {
|
public static void scanInvoice(Context context, Uri imageUri, ScanCallback callback) {
|
||||||
if (API_KEY == null || API_KEY.isEmpty() || API_KEY.contains("CHAVE_API_KEY")) {
|
|
||||||
callback.onError("Chave API do Gemini não configurada.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Executor executor = Executors.newSingleThreadExecutor();
|
Executor executor = Executors.newSingleThreadExecutor();
|
||||||
try (InputStream imageStream = context.getContentResolver().openInputStream(imageUri)) {
|
executor.execute(() -> {
|
||||||
Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
|
try (InputStream imageStream = context.getContentResolver().openInputStream(imageUri)) {
|
||||||
|
Bitmap originalBitmap = BitmapFactory.decodeStream(imageStream);
|
||||||
|
|
||||||
if (bitmap == null) {
|
if (originalBitmap == null) {
|
||||||
callback.onError("Não foi possível carregar a imagem da fatura.");
|
callback.onError("Não foi possível carregar a imagem da fatura.");
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
GenerativeModel gm = new GenerativeModel(
|
|
||||||
"gemini-1.5-flash",
|
|
||||||
API_KEY
|
|
||||||
);
|
|
||||||
GenerativeModelFutures model = GenerativeModelFutures.from(gm);
|
|
||||||
|
|
||||||
Content content = new Content.Builder()
|
|
||||||
.addText(PROMPT)
|
|
||||||
.addImage(bitmap)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ListenableFuture<GenerateContentResponse> response = model.generateContent(content);
|
|
||||||
|
|
||||||
Futures.addCallback(response, new FutureCallback<GenerateContentResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(GenerateContentResponse result) {
|
|
||||||
try {
|
|
||||||
String textResponse = result.getText();
|
|
||||||
if (textResponse != null) {
|
|
||||||
int start = textResponse.indexOf("{");
|
|
||||||
int end = textResponse.lastIndexOf("}");
|
|
||||||
if (start != -1 && end != -1 && end > start) {
|
|
||||||
String jsonContent = textResponse.substring(start, end + 1);
|
|
||||||
JSONObject json = new JSONObject(jsonContent);
|
|
||||||
double valor = json.optDouble("valor", 0.0);
|
|
||||||
String descricao = json.optString("descricao", "");
|
|
||||||
String categoria = json.optString("categoria", "Outros");
|
|
||||||
String data = json.optString("data", "");
|
|
||||||
callback.onSuccess(valor, descricao, categoria, data);
|
|
||||||
} else {
|
|
||||||
callback.onError("Não foi possível extrair os dados formatados em JSON.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
callback.onError("Resposta vazia da IA.");
|
|
||||||
}
|
|
||||||
} catch (JSONException e) {
|
|
||||||
Log.e("InvoiceScanner", "Erro ao fazer parse do JSON: " + result.getText(), e);
|
|
||||||
callback.onError("Erro ao ler dados da fatura.");
|
|
||||||
} finally {
|
|
||||||
if (executor instanceof java.util.concurrent.ExecutorService) {
|
|
||||||
((java.util.concurrent.ExecutorService) executor).shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Redimensionar a imagem para otimizar o envio
|
||||||
public void onFailure(Throwable t) {
|
Bitmap bitmap = scaleBitmap(originalBitmap, 1024);
|
||||||
Log.e("InvoiceScanner", "Falha na API do Gemini", t);
|
String base64Image = bitmapToBase64(bitmap);
|
||||||
callback.onError("Falha ao comunicar com a IA: " + t.getMessage());
|
|
||||||
if (executor instanceof java.util.concurrent.ExecutorService) {
|
|
||||||
((java.util.concurrent.ExecutorService) executor).shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, executor);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
// Criar o payload JSON para o Ollama
|
||||||
Log.e("InvoiceScanner", "Erro geral", e);
|
JSONObject payload = new JSONObject();
|
||||||
callback.onError("Erro ao processar imagem: " + e.getMessage());
|
payload.put("model", MODEL_NAME);
|
||||||
if (executor instanceof java.util.concurrent.ExecutorService) {
|
payload.put("stream", false);
|
||||||
((java.util.concurrent.ExecutorService) executor).shutdown();
|
payload.put("format", "json");
|
||||||
|
|
||||||
|
JSONArray messages = new JSONArray();
|
||||||
|
JSONObject message = new JSONObject();
|
||||||
|
message.put("role", "user");
|
||||||
|
message.put("content", PROMPT);
|
||||||
|
|
||||||
|
JSONArray images = new JSONArray();
|
||||||
|
images.put(base64Image);
|
||||||
|
message.put("images", images);
|
||||||
|
|
||||||
|
messages.put(message);
|
||||||
|
payload.put("messages", messages);
|
||||||
|
|
||||||
|
// Enviar a requisição HTTP
|
||||||
|
URL url = new URL(API_URL);
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod("POST");
|
||||||
|
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
|
||||||
|
conn.setRequestProperty("Accept", "application/json");
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
conn.setConnectTimeout(60000); // 60 segundos de timeout
|
||||||
|
conn.setReadTimeout(60000);
|
||||||
|
|
||||||
|
try (OutputStream os = conn.getOutputStream()) {
|
||||||
|
byte[] input = payload.toString().getBytes("utf-8");
|
||||||
|
os.write(input, 0, input.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
int responseCode = conn.getResponseCode();
|
||||||
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
|
||||||
|
StringBuilder response = new StringBuilder();
|
||||||
|
String responseLine;
|
||||||
|
while ((responseLine = br.readLine()) != null) {
|
||||||
|
response.append(responseLine.trim());
|
||||||
|
}
|
||||||
|
br.close();
|
||||||
|
|
||||||
|
JSONObject responseJson = new JSONObject(response.toString());
|
||||||
|
JSONObject messageObj = responseJson.getJSONObject("message");
|
||||||
|
String contentStr = messageObj.getString("content");
|
||||||
|
|
||||||
|
// Tratar a extração robusta do JSON
|
||||||
|
int start = contentStr.indexOf("{");
|
||||||
|
int end = contentStr.lastIndexOf("}");
|
||||||
|
if (start != -1 && end != -1 && end > start) {
|
||||||
|
String jsonContent = contentStr.substring(start, end + 1);
|
||||||
|
JSONObject invoiceJson = new JSONObject(jsonContent);
|
||||||
|
double valor = invoiceJson.optDouble("valor", 0.0);
|
||||||
|
String descricao = invoiceJson.optString("descricao", "");
|
||||||
|
String categoria = invoiceJson.optString("categoria", "Outros");
|
||||||
|
String data = invoiceJson.optString("data", "");
|
||||||
|
callback.onSuccess(valor, descricao, categoria, data);
|
||||||
|
} else {
|
||||||
|
callback.onError("Não foi possível extrair os dados formatados da fatura.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback.onError("Erro ao comunicar com a IA: Servidor respondeu com código " + responseCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("InvoiceScanner", "Erro geral", e);
|
||||||
|
callback.onError("Erro ao processar fatura: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (executor instanceof java.util.concurrent.ExecutorService) {
|
||||||
|
((java.util.concurrent.ExecutorService) executor).shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap scaleBitmap(Bitmap bitmap, int maxDimension) {
|
||||||
|
int width = bitmap.getWidth();
|
||||||
|
int height = bitmap.getHeight();
|
||||||
|
if (width <= maxDimension && height <= maxDimension) {
|
||||||
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
float ratio = (float) width / (float) height;
|
||||||
|
int newWidth, newHeight;
|
||||||
|
if (width > height) {
|
||||||
|
newWidth = maxDimension;
|
||||||
|
newHeight = Math.round(maxDimension / ratio);
|
||||||
|
} else {
|
||||||
|
newHeight = maxDimension;
|
||||||
|
newWidth = Math.round(maxDimension * ratio);
|
||||||
|
}
|
||||||
|
return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String bitmapToBase64(Bitmap bitmap) {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
|
||||||
|
byte[] byteArray = outputStream.toByteArray();
|
||||||
|
return Base64.encodeToString(byteArray, Base64.NO_WRAP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user