feat(celular): rosto e app usam SSE e token; remove telas legadas
- carinha.html assina /eventos (expressao, app_config, conversa, reacao, recarregar) e volta ao polling de /fila sozinho se o SSE cair; foto dos ajustes a cada (re)conexao; token (?t=) vai em todo fetch ao servidor - APK 0.8-token: token nas preferencias e no menu de toque longo, enviado na pagina, no HTTP do Java e ao llama-server (--api-key); trocar servidor por Intent exige o token salvo; camera/microfone so para o servidor configurado ou a copia local - cerebro/conversa mandam a chave ao llama-server do S10 - servidor: evento SSE 'recarregar'; remove /camera/frame e /app/connect; enderecos antigos redirecionam para a secao do painel novo - remove dashboard, painel, conversa, emojis, treinamento, braco, servos, api, scripts e CSS antigos, audio-robot.js e servir-local.bat - logica pura do painel em web/src/lib/nucleo.ts com testes node --test (substitui test_movimentos.js e test_rastreamento.js) - testes de navegador portados para o painel novo (rtc, rosto, rastreamento) - README, docs/PAINEL.md e docs/SEGURANCA.md reescritos; versao 0.8.0; carinha-pc.service le /etc/carinha.env Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.BatteryManager;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
@@ -83,6 +84,7 @@ public class MainActivity extends Activity {
|
||||
|
||||
private static final String PREFS = "carinha";
|
||||
private static final String CHAVE = "servidor";
|
||||
private static final String CHAVE_TOKEN = "token";
|
||||
private static final String PADRAO = "http://192.168.1.15:8100";
|
||||
|
||||
private static final String TAG = "CarinhaMic";
|
||||
@@ -100,6 +102,7 @@ public class MainActivity extends Activity {
|
||||
|
||||
private WebView web;
|
||||
private String servidor;
|
||||
private String token = ""; // CARINHA_TOKEN do servidor; vazio = servidor aberto
|
||||
private final CerebroLocal cerebroLocal = new CerebroLocal();
|
||||
private boolean jaCaiuParaLocal = false;
|
||||
private boolean jaRecarregou = false;
|
||||
@@ -179,8 +182,10 @@ public class MainActivity extends Activity {
|
||||
|
||||
SharedPreferences p = getSharedPreferences(PREFS, MODE_PRIVATE);
|
||||
servidor = p.getString(CHAVE, PADRAO);
|
||||
token = p.getString(CHAVE_TOKEN, "");
|
||||
movimentoLigado = p.getBoolean(CHAVE_MOV, true);
|
||||
usarServidorDoPedido(getIntent()); // permite apontar o servidor ao lancar
|
||||
cerebroLocal.chave = token;
|
||||
cerebroLocal.iniciar(this, servidor);
|
||||
remoto = new ControleRemoto(new Runnable() {
|
||||
@Override public void run() {
|
||||
@@ -272,7 +277,7 @@ public class MainActivity extends Activity {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean pode = true;
|
||||
boolean pode = origemConfiavel(pedido.getOrigin());
|
||||
for (String recurso : pedido.getResources()) {
|
||||
if (PermissionRequest.RESOURCE_VIDEO_CAPTURE.equals(recurso)
|
||||
&& checkSelfPermission(Manifest.permission.CAMERA)
|
||||
@@ -348,6 +353,15 @@ public class MainActivity extends Activity {
|
||||
if (it == null) return;
|
||||
String novo = it.getStringExtra("servidor");
|
||||
if (novo == null) return;
|
||||
String tokenPedido = it.getStringExtra("token");
|
||||
if (!token.isEmpty() && !token.equals(tokenPedido)) {
|
||||
Log.w(TAG, "pedido para trocar o servidor sem o token certo: ignorado");
|
||||
return;
|
||||
}
|
||||
if (token.isEmpty() && tokenPedido != null && !tokenPedido.trim().isEmpty()) {
|
||||
token = tokenPedido.trim();
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE).edit().putString(CHAVE_TOKEN, token).apply();
|
||||
}
|
||||
novo = novo.trim();
|
||||
while (novo.endsWith("/")) novo = novo.substring(0, novo.length() - 1);
|
||||
if (novo.length() == 0) return;
|
||||
@@ -374,8 +388,9 @@ public class MainActivity extends Activity {
|
||||
private void abrirRosto() {
|
||||
naCamera = false;
|
||||
remoto.servidor(servidor);
|
||||
remoto.token(token);
|
||||
if (usandoLocal) web.loadUrl("file:///android_asset/carinha.html");
|
||||
else web.loadUrl(servidor + "/carinha.html?aparelho=1");
|
||||
else web.loadUrl(servidor + "/carinha.html?aparelho=1" + (token.isEmpty() ? "" : "&t=" + Uri.encode(token)));
|
||||
iniciarMovimento(); // volta a observar quem passa na frente
|
||||
}
|
||||
|
||||
@@ -397,7 +412,7 @@ public class MainActivity extends Activity {
|
||||
private void menu() {
|
||||
final String[] opcoes = {"Ver a camera", "Voltar para a carinha",
|
||||
(movimentoLigado ? "Desligar reacao ao movimento" : "Ligar reacao ao movimento"),
|
||||
"Mudar o servidor", "Sensores acordado / dormindo", "Camera e qualidade do video"};
|
||||
"Mudar o servidor", "Sensores acordado / dormindo", "Camera e qualidade do video", "Token do servidor"};
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Carinha")
|
||||
.setItems(opcoes, new DialogInterface.OnClickListener() {
|
||||
@@ -408,7 +423,8 @@ public class MainActivity extends Activity {
|
||||
else if (i == 2) alternarMovimento();
|
||||
else if (i == 3) pedirServidor();
|
||||
else if (i == 4) menuSensores();
|
||||
else menuVideo();
|
||||
else if (i == 5) menuVideo();
|
||||
else pedirToken();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
@@ -520,6 +536,37 @@ public class MainActivity extends Activity {
|
||||
.show();
|
||||
}
|
||||
|
||||
/** Token do servidor (CARINHA_TOKEN da placa). Vazio = servidor sem autenticacao. */
|
||||
private void pedirToken() {
|
||||
final EditText campo = new EditText(this);
|
||||
campo.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
campo.setText(token);
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Token do servidor")
|
||||
.setMessage("O mesmo CARINHA_TOKEN configurado na placa")
|
||||
.setView(campo)
|
||||
.setPositiveButton("Salvar", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int w) {
|
||||
token = campo.getText().toString().trim();
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE).edit().putString(CHAVE_TOKEN, token).apply();
|
||||
cerebroLocal.chave = token;
|
||||
aviso(token.isEmpty() ? "token removido" : "token salvo");
|
||||
abrirRosto();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancelar", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
/** So o servidor configurado (ou a copia local do APK) pode usar camera/microfone. */
|
||||
private boolean origemConfiavel(Uri origem) {
|
||||
if (origem == null) return false;
|
||||
String o = origem.toString();
|
||||
while (o.endsWith("/")) o = o.substring(0, o.length() - 1);
|
||||
return o.startsWith("file:") || o.equals(servidor);
|
||||
}
|
||||
|
||||
/** Voltar nao sai do app (num enfeite de mesa isso seria chato): volta a carinha. */
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
|
||||
Reference in New Issue
Block a user