I dispositivi pieghevoli offrono esperienze di visualizzazione uniche. La modalità di visualizzazione posteriore e la modalità Dual Screen ti consentono di creare funzionalità di visualizzazione speciali per i dispositivi pieghevoli, come l'anteprima dei selfie con la fotocamera posteriore e la visualizzazione simultanea, ma diversa, sugli schermi interno ed esterno.
Modalità di visualizzazione posteriore
In genere, quando un dispositivo pieghevole è aperto, è attivo solo lo schermo interno. La modalità di visualizzazione posteriore ti consente di spostare un'attività sullo schermo esterno di un dispositivo pieghevole, che in genere è rivolto verso l'esterno quando il dispositivo è aperto. Il display interno si spegne automaticamente.
Un'applicazione innovativa consiste nel visualizzare l'anteprima della fotocamera sullo schermo esterno, in modo che gli utenti possano scattare selfie con la fotocamera posteriore, che in genere offre prestazioni di scatto migliori rispetto alla fotocamera anteriore.
Per attivare la modalità di visualizzazione posteriore, gli utenti rispondono a una finestra di dialogo per consentire all'app di cambiare schermo, ad esempio:
Il sistema crea la finestra di dialogo, quindi non è necessario alcun intervento di sviluppo da parte tua. Vengono visualizzate finestre di dialogo diverse a seconda dello stato del dispositivo; ad esempio, il sistema chiede agli utenti di aprire il dispositivo se è chiuso. Non puoi personalizzare la finestra di dialogo e può variare sui dispositivi di OEM diversi.
Puoi provare la modalità di visualizzazione posteriore con l'app Fotocamera di Pixel Fold. Consulta un'implementazione di esempio nel codelab Ottimizzare l'app Fotocamera sui dispositivi pieghevoli con Jetpack WindowManager.
Modalità Dual Screen
La modalità Dual Screen ti consente di mostrare contenuti su entrambi i display di un dispositivo pieghevole contemporaneamente. La modalità Dual Screen è disponibile su Pixel Fold con Android 14 (livello API 34) o versioni successive.
Un esempio di caso d'uso è l'interprete Dual Screen.
Abilitare le modalità a livello di programmazione
Puoi accedere alla modalità di visualizzazione posteriore e alla modalità Dual Screen tramite le API Jetpack WindowManager, a partire dalla versione 1.2.0-beta03 della libreria.
Aggiungi la dipendenza WindowManager al file build.gradle del modulo dell'app:
Kotlin
dependencies {
// Define window_version in your project's build configuration.
implementation("androidx.window:window:$window_version")
}
Groovy
dependencies {
// TODO: Define window_version in your project's build configuration.
implementation "androidx.window:window:$window_version"
}
Il punto di ingresso è il WindowAreaController, che fornisce le
informazioni e il comportamento relativi allo spostamento delle finestre tra i display o tra le
aree di visualizzazione di un dispositivo. WindowAreaController ti consente di eseguire query sull'elenco degli oggetti WindowAreaInfo
disponibili.
Utilizza WindowAreaInfo per accedere a WindowAreaSession, un'interfaccia che
rappresenta una funzionalità attiva dell'area della finestra. Utilizza WindowAreaSession per determinare
la disponibilità di un WindowAreaCapability specifico.
Ogni funzionalità è correlata a un WindowAreaCapability.Operation particolare.
Nella versione 1.2.0-beta03, Jetpack WindowManager supporta due tipi di operazioni:
WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA, utilizzata per avviare la modalità Dual ScreenWindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA, utilizzata per avviare la modalità di visualizzazione posteriore
Ecco un esempio di come dichiarare le variabili per la modalità di visualizzazione posteriore e la modalità Dual Screen nell'attività principale dell'app:
Kotlin
private lateinit var windowAreaController: WindowAreaController
private lateinit var displayExecutor: Executor
private var windowAreaSession: WindowAreaSession? = null
private var windowAreaInfo: WindowAreaInfo? = null
private var capabilityStatus: WindowAreaCapability.Status =
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED
private val dualScreenOperation = WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA
private val rearDisplayOperation = WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
Java
private WindowAreaControllerCallbackAdapter windowAreaController = null;
private Executor displayExecutor = null;
private WindowAreaSessionPresenter windowAreaSession = null;
private WindowAreaInfo windowAreaInfo = null;
private WindowAreaCapability.Status capabilityStatus =
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED;
private WindowAreaCapability.Operation dualScreenOperation =
WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA;
private WindowAreaCapability.Operation rearDisplayOperation =
WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA;
Ecco come inizializzare le variabili nel metodo onCreate() dell'attività:
Kotlin
displayExecutor = ContextCompat.getMainExecutor(this)
windowAreaController = WindowAreaController.getOrCreate()
lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
windowAreaController.windowAreaInfos
.map { info -> info.firstOrNull { it.type == WindowAreaInfo.Type.TYPE_REAR_FACING } }
.onEach { info -> windowAreaInfo = info }
.map { it?.getCapability(operation)?.status ?: WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED }
.distinctUntilChanged()
.collect {
capabilityStatus = it
}
}
}
Java
displayExecutor = ContextCompat.getMainExecutor(this);
windowAreaController = new WindowAreaControllerCallbackAdapter(WindowAreaController.getOrCreate());
windowAreaController.addWindowAreaInfoListListener(displayExecutor, this);
windowAreaController.addWindowAreaInfoListListener(displayExecutor,
windowAreaInfos -> {
for(WindowAreaInfo newInfo : windowAreaInfos){
if(newInfo.getType().equals(WindowAreaInfo.Type.TYPE_REAR_FACING)){
windowAreaInfo = newInfo;
capabilityStatus = newInfo.getCapability(presentOperation).getStatus();
break;
}
}
});
Prima di avviare un'operazione, controlla la disponibilità della funzionalità specifica:
Kotlin
when (capabilityStatus) {
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED -> {
// The selected display mode is not supported on this device.
}
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE -> {
// The selected display mode is not available.
}
WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> {
// The selected display mode is available and can be enabled.
}
WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE -> {
// The selected display mode is already active.
}
else -> {
// The selected display mode status is unknown.
}
}
Java
if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED)) {
// The selected display mode is not supported on this device.
}
else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE)) {
// The selected display mode is not available.
}
else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE)) {
// The selected display mode is available and can be enabled.
}
else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE)) {
// The selected display mode is already active.
}
else {
// The selected display mode status is unknown.
}
Modalità Dual Screen
L'esempio seguente chiude la sessione se la funzionalità è già attiva, o
altrimenti chiama la funzione presentContentOnWindowArea():
Kotlin
fun toggleDualScreenMode() {
if (windowAreaSession != null) {
windowAreaSession?.close()
}
else {
windowAreaInfo?.token?.let { token ->
windowAreaController.presentContentOnWindowArea(
token = token,
activity = this,
executor = displayExecutor,
windowAreaPresentationSessionCallback = this
)
}
}
}
Java
private void toggleDualScreenMode() {
if(windowAreaSession != null) {
windowAreaSession.close();
}
else {
Binder token = windowAreaInfo.getToken();
windowAreaController.presentContentOnWindowArea( token, this, displayExecutor, this);
}
}
Tieni presente l'utilizzo dell'attività principale dell'app come
WindowAreaPresentationSessionCallback argomento.
L'API utilizza un approccio basato su listener: quando invii una richiesta per presentare i contenuti
all'altro display di un dispositivo pieghevole, avvii una sessione che viene restituita
tramite il metodo onSessionStarted() del listener. Quando chiudi la
sessione, ricevi una conferma nel onSessionEnded() metodo.
Per creare il listener, implementa l'interfaccia WindowAreaPresentationSessionCallback:
Kotlin
class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback
Java
public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback
Il listener deve implementare i metodi onSessionStarted(), onSessionEnded(),
e onContainerVisibilityChanged(). I metodi di callback ti notificano lo stato della sessione e ti consentono di aggiornare l'app di conseguenza.
La callback onSessionStarted() riceve un WindowAreaSessionPresenter come
argomento. L'argomento è il container che ti consente di accedere a un'area della finestra e di mostrare i contenuti. La presentazione può essere chiusa automaticamente dal sistema
quando l'utente esce dalla finestra dell'applicazione principale oppure può essere
chiusa chiamando WindowAreaSessionPresenter#close().
Per le altre callback, per semplicità, controlla solo nel corpo della funzione eventuali errori e registra lo stato:
Kotlin
override fun onSessionStarted(session: WindowAreaSessionPresenter) {
windowAreaSession = session
val view = TextView(session.context)
view.text = "Hello world!"
session.setContentView(view)
}
override fun onSessionEnded(t: Throwable?) {
if(t != null) {
Log.e(logTag, "Something was broken: ${t.message}")
}
}
override fun onContainerVisibilityChanged(isVisible: Boolean) {
Log.d(logTag, "onContainerVisibilityChanged. isVisible = $isVisible")
}
Java
@Override
public void onSessionStarted(@NonNull WindowAreaSessionPresenter session) {
windowAreaSession = session;
TextView view = new TextView(session.getContext());
view.setText("Hello world, from the other screen!");
session.setContentView(view);
}
@Override public void onSessionEnded(@Nullable Throwable t) {
if(t != null) {
Log.e(logTag, "Something was broken: ${t.message}");
}
}
@Override public void onContainerVisibilityChanged(boolean isVisible) {
Log.d(logTag, "onContainerVisibilityChanged. isVisible = " + isVisible);
}
Per mantenere la coerenza nell'ecosistema, utilizza l'icona ufficiale Dual Screen per indicare agli utenti come attivare o disattivare la modalità Dual Screen.
Per un esempio funzionante, consulta DualScreenActivity.kt.
Modalità di visualizzazione posteriore
Analogamente all'esempio della modalità Dual Screen, l'esempio seguente di una
toggleRearDisplayMode() funzione chiude la sessione se la funzionalità è
già attiva, altrimenti chiama la transferActivityToWindowArea()
funzione:
Kotlin
fun toggleRearDisplayMode() {
if(capabilityStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) {
if(windowAreaSession == null) {
windowAreaSession = windowAreaInfo?.getActiveSession(
operation
)
}
windowAreaSession?.close()
} else {
windowAreaInfo?.token?.let { token ->
windowAreaController.transferActivityToWindowArea(
token = token,
activity = this,
executor = displayExecutor,
windowAreaSessionCallback = this
)
}
}
}
Java
void toggleRearDisplayMode() {
if(capabilityStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) {
if(windowAreaSession == null) {
windowAreaSession = windowAreaInfo.getActiveSession(
operation
)
}
windowAreaSession.close();
}
else {
Binder token = windowAreaInfo.getToken();
windowAreaController.transferActivityToWindowArea(token, this, displayExecutor, this);
}
}
In questo caso, l'attività visualizzata viene utilizzata come WindowAreaSessionCallback, che è più semplice da implementare perché la callback non riceve un presenter che consente di mostrare i contenuti in un'area della finestra, ma trasferisce l'intera attività a un'altra area:
Kotlin
override fun onSessionStarted() {
Log.d(logTag, "onSessionStarted")
}
override fun onSessionEnded(t: Throwable?) {
if(t != null) {
Log.e(logTag, "Something was broken: ${t.message}")
}
}
Java
@Override public void onSessionStarted(){
Log.d(logTag, "onSessionStarted");
}
@Override public void onSessionEnded(@Nullable Throwable t) {
if(t != null) {
Log.e(logTag, "Something was broken: ${t.message}");
}
}
Per mantenere la coerenza nell'ecosistema, utilizza l'icona ufficiale Fotocamera posteriore per indicare agli utenti come attivare o disattivare la modalità di visualizzazione posteriore.
Risorse aggiuntive
- Ottimizzare l'app Fotocamera sui dispositivi pieghevoli con Jetpack WindowManager codelab
androidx.window.areariepilogo del pacchetto- Codice campione di Jetpack WindowManager: