Questa pagina descrive le diverse funzionalità della raccolta di app per auto che puoi utilizzare per implementare la funzionalità della tua app di navigazione passo passo.
Dichiarare il supporto della navigazione nel file manifest
L'app di navigazione deve dichiarare la androidx.car.app.category.NAVIGATION
categoria di app per auto nel filtro per intent del suo CarAppService
:
<application>
...
<service
...
android:name=".MyNavigationCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION"/>
</intent-filter>
</service>
...
</application>
Supporta gli intent di navigazione
Per supportare le intent di navigazione per la tua app, incluse quelle provenienti dall'Assistente Google tramite una query vocale, la tua app deve gestire l'intent CarContext.ACTION_NAVIGATE
nel suo Session.onCreateScreen
e Session.onNewIntent
.
Per informazioni dettagliate sul formato dell'intent, consulta la documentazione relativa a CarContext.startCarApp
.
Accedere ai modelli di navigazione
Le app di navigazione possono accedere ai seguenti modelli, che mostrano una superficie in background con la mappa e, durante la navigazione attiva, le indicazioni stradali passo passo.
NavigationTemplate
: visualizza anche un messaggio informativo facoltativo e le stime di viaggio durante la navigazione attiva.MapWithContentTemplate
: un modello che consente a un'app di visualizzare riquadri della mappa con alcuni tipi di contenuti (ad es. un elenco). I contenuti vengono solitamente visualizzati come overlay sopra i riquadri della mappa, con le aree stabili e visibili della mappa che si adattano ai contenuti.
Per ulteriori dettagli su come progettare l'interfaccia utente dell'app di navigazione utilizzando questi modelli, consulta App di navigazione.
Per accedere ai modelli di navigazione, l'app deve dichiarare l'autorizzazione androidx.car.app.NAVIGATION_TEMPLATES
nel file AndroidManifest.xml
:
<manifest ...>
...
<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>
...
</manifest>
Per disegnare mappe è necessaria un'autorizzazione aggiuntiva.
Esegui la migrazione a MapWithContentTemplate
A partire dal livello 7 dell'API Car App, i valori MapTemplate
, PlaceListNavigationTemplate
e RoutePreviewNavigationTemplate
sono ritirati. I modelli ritirati continueranno a essere supportati, ma è vivamente consigliata la migrazione a MapWithContentTemplate
.
La funzionalità fornita da questi modelli può essere implementata
utilizzando MapWithContentTemplate
. Di seguito sono riportati alcuni esempi di snippet:
MapTemplate
Kotlin
// MapTemplate (deprecated) val template = MapTemplate.Builder() .setPane(paneBuilder.build()) .setActionStrip(actionStrip) .setHeader(header) .setMapController(mapController) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( PaneTemplate.Builder(paneBuilder.build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController(mapController) .build()
Java
// MapTemplate (deprecated) MapTemplate template = new MapTemplate.Builder() .setPane(paneBuilder.build()) .setActionStrip(actionStrip) .setHeader(header) .setMapController(mapController) .build(); // MapWithContentTemplate MapWithContentTemplate template = new MapWithContentTemplate.Builder() .setContentTemplate(new PaneTemplate.Builder(paneBuilder.build()) .setHeader(header) build()) .setActionStrip(actionStrip) .setMapController(mapController) .build();
PlaceListNavigationTemplate
Kotlin
// PlaceListNavigationTemplate (deprecated) val template = PlaceListNavigationTemplate.Builder() .setItemList(itemListBuilder.build()) .setHeader(header) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( ListTemplate.Builder() .setSingleList(itemListBuilder.build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController( MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build()
Java
// PlaceListNavigationTemplate (deprecated) PlaceListNavigationTemplate template = new PlaceListNavigationTemplate.Builder() .setItemList(itemListBuilder.build()) .setHeader(header) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build(); // MapWithContentTemplate MapWithContentTemplate template = new MapWithContentTemplate.Builder() .setContentTemplate(new ListTemplate.Builder() .setSingleList(itemListBuilder.build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController(new MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build();
RoutePreviewNavigationTemplate
Kotlin
// RoutePreviewNavigationTemplate (deprecated) val template = RoutePreviewNavigationTemplate.Builder() .setItemList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .build()) .build()) .setHeader(header) .setNavigateAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { ... } .build()) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( ListTemplate.Builder() .setSingleList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .addAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { ... } .build()) .build()) .build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController( MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build()
Java
// RoutePreviewNavigationTemplate (deprecated) RoutePreviewNavigationTemplate template = new RoutePreviewNavigationTemplate.Builder() .setItemList(new ItemList.Builder() .addItem(new Row.Builder() .setTitle(title)) .build()) .build()) .setHeader(header) .setNavigateAction(new Action.Builder() .setTitle(actionTitle) .setOnClickListener(() -> { ... }) .build()) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build(); // MapWithContentTemplate MapWithContentTemplate template = new MapWithContentTemplate.Builder() .setContentTemplate(new ListTemplate.Builder() .setSingleList(new ItemList.Builder() .addItem(new Row.Builder() .setTitle(title)) .addAction(new Action.Builder() .setTitle(actionTitle) .setOnClickListener(() -> { ... }) .build()) .build()) .build())) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController(new MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build();
Comunicare i metadati di navigazione
Le app di navigazione devono comunicare metadati di navigazione aggiuntivi con l'host. L'host utilizza le informazioni per fornire informazioni all'unità principale del veicolo e per evitare conflitti tra le applicazioni di navigazione per le risorse condivise.
I metadati di navigazione vengono forniti tramite il servizio per auto NavigationManager
accessibile da CarContext
:
Kotlin
val navigationManager = carContext.getCarService(NavigationManager::class.java)
Java
NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);
Avviare, terminare e interrompere la navigazione
Affinché l'host possa gestire più app di navigazione, notifiche relative al routing e dati dei cluster di veicoli, deve essere a conoscenza dello stato corrente della navigazione. Quando un utente avvia la navigazione, chiama
NavigationManager.navigationStarted
.
Analogamente, quando la navigazione termina, ad esempio quando l'utente arriva a destinazione o annulla la navigazione, chiama NavigationManager.navigationEnded
.
Chiama NavigationManager.navigationEnded
solo quando l'utente termina la navigazione. Ad esempio, se devi ricalcolare il percorso nel bel mezzo di un viaggio, utilizza Trip.Builder.setLoading(true)
.
A volte, l'host ha bisogno di un'app per interrompere la navigazione e le chiamateonStopNavigation
in un oggetto
NavigationManagerCallback
fornito dalla tua app tramite
NavigationManager.setNavigationManagerCallback
.
L'app deve quindi interrompere l'invio di informazioni sulla svolta successiva nel display del cluster, nelle notifiche di navigazione e nella guida vocale.
Aggiornare le informazioni sulla corsa
Durante la navigazione attiva, chiama
NavigationManager.updateTrip
.
Le informazioni fornite in questa chiamata possono essere utilizzate dal quadro strumenti e dal display a sovrimpressione del veicolo. A seconda del veicolo guidato, non tutte le informazioni vengono mostrate all'utente.
Ad esempio, la DHU (unità principale desktop) mostra
Step
aggiunto a
Trip
, ma non mostra
le informazioni su Destination
.
Disegno sul display del cluster
Per offrire un'esperienza utente più coinvolgente, ti consigliamo di andare oltre la visualizzazione dei metadati di base sul display del cruscotto del veicolo. A partire dal livello 6 dell'API Car App, le app di navigazione hanno la possibilità di eseguire il rendering degli propri contenuti direttamente sul display del cruscotto (nei veicoli supportati), con le seguenti limitazioni:
- L'API di visualizzazione del cluster non supporta i controlli di immissione
- Norme sulla qualità delle app per auto
NF-9
: Il display del cluster deve mostrare solo riquadri della mappa. Se vuoi, su questi riquadri puoi visualizzare un percorso di navigazione attivo. - L'API di visualizzazione del cluster supporta solo l'utilizzo del
NavigationTemplate
- A differenza dei display principali, i display cluster potrebbero non mostrare in modo coerente tutti gli elementi dell'interfaccia utente di
NavigationTemplate
, come le istruzioni stradali passo passo, le schede con l'orario di arrivo stimato e le azioni. I riquadri della mappa sono l'unico elemento della UI visualizzato in modo coerente.
- A differenza dei display principali, i display cluster potrebbero non mostrare in modo coerente tutti gli elementi dell'interfaccia utente di
Dichiarare il supporto del cluster
Per comunicare all'applicazione host che la tua app supporta il rendering su display di cluster, devi aggiungere un elemento androidx.car.app.category.FEATURE_CLUSTER
<category>
a <intent-filter>
di CarAppService
, come mostrato nel seguente snippet:
<application> ... <service ... android:name=".MyNavigationCarAppService" android:exported="true"> <intent-filter> <action android:name="androidx.car.app.CarAppService" /> <category android:name="androidx.car.app.category.NAVIGATION"/> <category android:name="androidx.car.app.category.FEATURE_CLUSTER"/> </intent-filter> </service> ... </application>
Gestione del ciclo di vita e dello stato
A partire dal livello 6 dell'API, il
flusso di vita
dell'app per auto rimane invariato, ma ora CarAppService::onCreateSession
accetta un parametro di
tipo SessionInfo
che fornisce
informazioni aggiuntive sul Session
in fase di creazione (ovvero il
tipo di visualizzazione e l'insieme di modelli supportati).
Le app hanno la possibilità di utilizzare la stessa classe Session
per gestire sia il cluster sia il display principale oppure di creare Sessions
specifici per il display per personalizzare il comportamento su ogni display (come mostrato nello snippet seguente).
Kotlin
override fun onCreateSession(sessionInfo: SessionInfo): Session { return if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) { ClusterSession() } else { MainDisplaySession() } }
Java
@Override @NonNull public Session onCreateSession(@NonNull SessionInfo sessionInfo) { if (sessionInfo.getDisplayType() == SessionInfo.DISPLAY_TYPE_CLUSTER) { return new ClusterSession(); } else { return new MainDisplaySession(); } }
Non ci sono garanzie su quando o se viene fornito il display del cluster ed è anche possibile che il cluster Session
sia l'unico Session
(ad esempio, l'utente ha scambiato il display principale con un'altra app mentre la tua app è in navigazione attiva). Il contratto "standard" prevede che l'app acquisisca il controllo della visualizzazione del cluster solo dopo che è stata invocata NavigationManager::navigationStarted
. Tuttavia, è possibile che all'app venga fornito il display del cluster
quando non è in corso alcuna navigazione attiva o che non venga mai fornito il display del cluster. Spetta all'app gestire questi scenari eseguendo il rendering dello stato inattivo delle schede della mappa.
L'host crea istanze di binder e CarContext
separate per Session
. Ciò significa che, quando utilizzi metodi come ScreenManager::push
o
Screen::invalidate
, viene interessato solo il Session
da cui vengono chiamati. Le app devono creare i propri canali di comunicazione tra queste istanze se è necessaria la comunicazione tra più Session
(ad esempio, utilizzando trasmissioni, un singleton condiviso o altro).
Assistenza per i cluster di test
Puoi testare l'implementazione sia su Android Auto che su Android Automotive OS. Per Android Auto, questo viene fatto configurando l'unità principale del computer per emulare un display del cruscotto secondario. Per Android Automotive OS, le immagini di sistema generiche per il livello API 30 e versioni successive simulano un display del cluster.
Personalizzare la stima dei tempi di percorrenza con testo o icona
Per personalizzare la stima del viaggio con testo, un'icona o entrambi, utilizza i metodi della classe setTripIcon
o setTripText
.TravelEstimate.Builder
NavigationTemplate
utilizza TravelEstimate
per impostare facoltativamente testo e icone accanto o al posto dell'orario di arrivo stimato, del tempo rimanente e della distanza rimanente.
Lo snippet seguente utilizza setTripIcon
e setTripText
per personalizzare la stima del viaggio:
Kotlin
TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build()
Java
new TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build();
Fornire notifiche passo passo
Fornire istruzioni di navigazione passo passo (TBT) utilizzando una notifica di navigazione aggiornata di frequente. Per essere considerata una notifica di navigazione sullo schermo dell'auto, il creator della notifica deve:
- Contrassegna la notifica come in corso con il metodo
NotificationCompat.Builder.setOngoing
. - Imposta la categoria della notifica su
Notification.CATEGORY_NAVIGATION
. - Espandi la notifica con un
CarAppExtender
.
Una notifica di navigazione viene visualizzata nel widget della barra nella parte inferiore dello schermo dell'auto. Se il livello di importanza della notifica è impostato su
IMPORTANCE_HIGH
, viene visualizzata anche come notifica in primo piano (HUN).
Se l'importanza non è impostata con il metodo
CarAppExtender.Builder.setImportance
viene utilizzata l'importanza del canale di notifica.
L'app può impostare un PendingIntent
nel
CarAppExtender
che viene inviato all'app quando l'utente tocca l'HUN o il widget della barra laterale.
Se
NotificationCompat.Builder.setOnlyAlertOnce
viene chiamato con un valore di true
, una notifica di alta importanza viene inviata solo
una volta nell'HUN.
Lo snippet seguente mostra come creare una notifica di navigazione:
Kotlin
NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) ... .setOnlyAlertOnce(true) .setOngoing(true) .setCategory(NotificationCompat.CATEGORY_NAVIGATION) .extend( CarAppExtender.Builder() .setContentTitle(carScreenTitle) ... .setContentIntent( PendingIntent.getBroadcast( context, ACTION_OPEN_APP.hashCode(), Intent(ACTION_OPEN_APP).setComponent( ComponentName(context, MyNotificationReceiver::class.java)), 0)) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build()) .build()
Java
new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) ... .setOnlyAlertOnce(true) .setOngoing(true) .setCategory(NotificationCompat.CATEGORY_NAVIGATION) .extend( new CarAppExtender.Builder() .setContentTitle(carScreenTitle) ... .setContentIntent( PendingIntent.getBroadcast( context, ACTION_OPEN_APP.hashCode(), new Intent(ACTION_OPEN_APP).setComponent( new ComponentName(context, MyNotificationReceiver.class)), 0)) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build()) .build();
Aggiorna regolarmente la notifica TBT per le modifiche della distanza, che aggiorna il widget della metropolitana e mostra la notifica solo come HUN.
Puoi controllare il comportamento di HUN impostando l'importanza della notifica con
CarAppExtender.Builder.setImportance
. Se imposti l'importanza su
IMPORTANCE_HIGH
, viene visualizzato un HUN. Se lo imposti su un altro valore, viene aggiornato solo il widget della barra laterale.
Aggiornare i contenuti di PlaceListNavigationTemplate
Puoi consentire ai conducenti di aggiornare i contenuti con un semplice tocco mentre sfogliano gli elenchi di luoghi creati con PlaceListNavigationTemplate
.
Per attivare l'aggiornamento dell'elenco, implementa il metodo
onContentRefreshRequested
dell'interfaccia
OnContentRefreshListener
e utilizza
PlaceListNavigationTemplate.Builder.setOnContentRefreshListener
per impostare l'ascoltatore nel modello.
Lo snippet seguente mostra come impostare l'ascoltatore nel modello:
Kotlin
PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate() } .build()
Java
new PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener(() -> { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate(); }) .build();
Il pulsante di aggiornamento viene visualizzato nell'intestazione del
PlaceListNavigationTemplate
solo se l'ascoltatore ha un valore.
Quando l'utente fa clic sul pulsante di aggiornamento, viene chiamato il metodo onContentRefreshRequested
della tua implementazione OnContentRefreshListener
. All'interno di onContentRefreshRequested
, chiama il metodo Screen.invalidate
.
L'host richiama quindi il metodo
Screen.onGetTemplate
dell'app per recuperare il modello con i contenuti aggiornati. Per maggiori informazioni sull'aggiornamento dei modelli, consulta Aggiornare i contenuti di un modello. Se il successivo modello
ristituito da
onGetTemplate
è dello stesso tipo, viene conteggiato come aggiornamento e non viene conteggiato ai fini della quota di modelli.
Fornire indicazioni audio
Per riprodurre le indicazioni stradali tramite gli altoparlanti dell'auto, l'app deve richiedere il focus audio. Nell'ambito del tuo
AudioFocusRequest
, imposta
l'utilizzo su AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
. Inoltre, imposta il guadagno della messa a fuoco su AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
.
Simula la navigazione
Per verificare la funzionalità di navigazione dell'app quando la invii al Google Play Store, l'app deve implementare il callback NavigationManagerCallback.onAutoDriveEnabled
. Quando viene chiamato questo callback, l'app deve simulare la navigazione fino alla destinazione scelta quando l'utente inizia a navigare. L'app può uscire da questa modalità ogni volta che il ciclo di vita dell'Session
corrente raggiunge lo stato Lifecycle.Event.ON_DESTROY
.
Puoi verificare che l'implementazione di onAutoDriveEnabled
venga chiamata eseguendo quanto segue da una riga di comando:
adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE
Ciò è mostrato nell'esempio seguente:
adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE
App di navigazione auto predefinita
In Android Auto, l'app di navigazione predefinita per l'auto corrisponde all'ultima app di navigazione avviata dall'utente. L'app predefinita riceve intent di navigazione quando l'utente invoca i comandi di navigazione tramite l'assistente o quando un'altra app invia un intent per avviare la navigazione.
Mostrare avvisi di navigazione contestuali
Alert
mostra informazioni importanti al conducente con azioni facoltative e un riepilogo, senza uscire dal contesto della schermata di navigazione. Per offrire la migliore esperienza al conducente,
Alert
lavora all'interno di
NavigationTemplate
per evitare di bloccare il percorso di navigazione e ridurre al minimo le distrazioni del conducente.
Alert
è disponibile solo all'interno di NavigationTemplate
.
Per avvisare l'utente al di fuori del NavigationTemplate
,
valuta la possibilità di utilizzare una notifica in primo piano (HUN), come spiegato in
Mostrare le notifiche.
Ad esempio, utilizza Alert
per:
- Informare il conducente di un aggiornamento pertinente alla navigazione in corso, ad esempio un cambiamento delle condizioni del traffico.
- Chiedi al conducente un aggiornamento relativo alla navigazione corrente, ad esempio l'esistenza di un posto di blocco.
- Proponi un'attività imminente e chiedi se il conducente la accetta, ad esempio se è disposto a prendere qualcuno lungo il percorso.
Nella sua forma di base, un Alert
è composto da un titolo e dalla durata Alert
. La durata è rappresentata da una barra di avanzamento. Se vuoi, puoi aggiungere un sottotitolo, un'icona e fino a due oggetti Action
.
Una volta visualizzato un Alert
, questo non viene trasferito a un altro modello se l'interazione del conducente comporta l'uscita dal Alert
.NavigationTemplate
Rimane nella NavigationTemplate
originale finché Alert
non scade, l'utente compie un'azione o l'app chiude la Alert
.
Creare un avviso
Utilizza Alert.Builder
per creare un'istanza Alert
:
Kotlin
Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build()
Java
new Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build();
Se vuoi ascoltare l'annullamento o la chiusura di Alert
, crea un'implementazione dell'interfaccia AlertCallback
.
I percorsi di chiamata AlertCallback
sono:
Se il timeout di
Alert
scade, l'host chiama il metodoAlertCallback.onCancel
con il valoreAlertCallback.REASON_TIMEOUT
. Quindi chiama il metodoAlertCallback.onDismiss
.Se il conducente fa clic su uno dei pulsanti di azione, l'host chiama
Action.OnClickListener
e poi chiamaAlertCallback.onDismiss
.Se
Alert
non è supportato, l'host chiamaAlertCallback.onCancel
con il valoreAlertCallback.REASON_NOT_SUPPORTED
. L'host non chiamaAlertCallback.onDismiss
, perchéAlert
non è stato mostrato.
Configura la durata dell'avviso
Scegli una durata di Alert
che sia adatta alle esigenze della tua app. La durata consigliata per un menu di navigazioneAlert
è di 10 secondi. Per ulteriori informazioni, consulta la sezione Avvisi di navigazione.
Mostrare un avviso
Per mostrare un Alert
, chiama il metodo
AppManager.showAlert
disponibile tramite il CarContext
della tua app.
// Show an alert
carContext.getCarService(AppManager.class).showAlert(alert)
- La chiamata a
showAlert
con unAlert
che ha unalertId
uguale all'ID delAlert
attualmente visualizzato non fa nulla.Alert
non si aggiorna. Per aggiornare unAlert
, devi ricrearlo con un nuovoalertId
. - Se chiami
showAlert
con unAlert
che ha unalertId
diverso rispetto a quello visualizzato al momento, ilAlert
visualizzato al momento viene ignorato.Alert
Ignorare un avviso
Sebbene un Alert
venga chiuso automaticamente per timeout o interazione del conducente, puoi anche chiuderlo manualmente, ad esempio se le relative informazioni diventano obsolete.Alert
Per ignorare un
Alert
, chiama il
metodo
dismissAlert
con il
alertId
del Alert
.
// Dismiss the same alert
carContext.getCarService(AppManager.class).dismissAlert(alert.getId())
Se chiami dismissAlert
con un alertId
che non corrisponde al Alert
visualizzato al momento, non succede nulla. Non genera un'eccezione.