Dispositivos dobráveis oferecem experiências de visualização únicas. O modo de tela traseira e o Dual Screen permitem criar recursos especiais de exibição para dispositivos dobráveis, como a prévia da selfie de câmera traseira e telas interna e externa simultâneas.
Modo de tela traseira
Normalmente, quando um dispositivo dobrável é aberto, apenas a tela interna fica ativa. O modo de tela traseira permite mover uma atividade para a tela externa de um dispositivo dobrável, que geralmente fica voltada para o lado oposto ao usuário enquanto o dispositivo está aberto. O display interno é desligado automaticamente.
Um aplicativo inovador deve mostrar a prévia da câmera na tela externa. Assim, os usuários poderão tirar selfies com a câmera traseira, o que geralmente oferece uma qualidade de fotos muito melhor.
Para ativar o modo de tela traseira, os usuários respondem a uma caixa de diálogo que permite a troca de tela pelo app. Por exemplo:
![](https://developer.android.com/static/images/guide/topics/large-screens/foldables/rear-display-dialog.png?authuser=19&hl=pt)
O sistema cria a caixa de diálogo. Assim, ela não precisa ser desenvolvida por você. Diferentes caixas de diálogo aparecem dependendo do estado do dispositivo. Por exemplo, o sistema instrui o usuário a abrir o dispositivo se ele estiver fechado. Não é possível personalizar a caixa de diálogo, mas ela pode variar de acordo com os dispositivos de diferentes OEMs.
Você pode testar o modo de tela traseira com o app de câmera do Pixel Fold. Confira um exemplo de implementação no codelab Conheça os recursos da câmera em dispositivos dobráveis.
Modo Dual Screen
O modo Dual Screen permite mostrar conteúdo nas duas telas do dispositivo dobrável ao mesmo tempo. Ele está disponível no Pixel Fold com o Android 14 (nível 34 da API) e em versões mais recentes.
Um exemplo de caso de uso é o intérprete com Dual Screen.
![](https://developer.android.com/static/images/guide/topics/large-screens/foldables/dual-screen-interpreter.png?authuser=19&hl=pt)
Ativar os modos de forma programática
Você pode acessar o modo de tela traseira e o Dual Screen pelas APIs Jetpack WindowManager da versão 1.2.0-beta03 da biblioteca em diante.
Adicione a dependência WindowManager ao arquivo build.gradle
do módulo do app:
Groovy
dependencies { implementation "androidx.window:window:1.2.0-beta03" }
Kotlin
dependencies { implementation("androidx.window:window:1.2.0-beta03") }
O ponto de entrada é o WindowAreaController
,
que fornece informações e o comportamento ao mover janelas entre telas ou áreas de exibição em um dispositivo.
WindowAreaController
permite consultar a lista de objetos
WindowAreaInfo
disponíveis.
Use WindowAreaInfo
para acessar a WindowAreaSession
,
uma interface que representa um recurso de área da janela ativa.
Use WindowAreaSession
para determinar a disponibilidade de uma
WindowAreaCapability
específica.
Cada recurso está relacionado a uma WindowAreaCapability.Operation
específica.
Na versão 1.2.0-beta03, a API Jetpack WindowManager tem suporte a dois tipos de operações:
WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA
, usada para iniciar o modo Dual ScreenWindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
, usada para iniciar o modo de tela traseira
Confira um exemplo de como declarar variáveis para os modos de tela traseira e Dual Screen na atividade principal do 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;
Confira como inicializar as variáveis no método onCreate()
da sua atividade:
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; } } });
Antes de iniciar uma operação, verifique a disponibilidade do recurso específico:
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 currently available to be enabled. } WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> { // The selected display mode is currently available to 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 currently available to be enabled. } else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE)) { // The selected display mode is currently available to 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. }
Modo Dual Screen
O exemplo a seguir fecha a sessão caso o recurso já esteja ativo. Se não estiver, ele vai chamar a função 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); } }
Observe o uso da atividade principal do app como um WindowAreaPresentationSessionCallback
.
A API usa uma abordagem de listener: ao fazer uma solicitação para apresentar o conteúdo
na outra tela de um dispositivo dobrável, você inicia uma sessão que é retornada
usando o método onSessionStarted()
.
Ao encerrar a sessão, você recebe uma confirmação no método onSessionEnded()
.
Para criar o listener, implemente a interface WindowAreaPresentationSessionCallback
:
Kotlin
class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback
Java
public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback
O listener precisa implementar os métodos onSessionStarted()
, onSessionEnded(),
e onContainerVisibilityChanged()
.
Os métodos de callback notificam você sobre o status da sessão e permitem atualizar o app adequadamente.
O callback onSessionStarted()
recebe um WindowAreaSessionPresenter
como argumento.
O argumento é o contêiner que permite acessar uma área da janela e mostrar conteúdo.
A apresentação poderá ser dispensada automaticamente pelo sistema quando o usuário sair da janela principal do app ou ser fechada chamando WindowAreaSessionPresenter#close()
.
Para os outros callbacks, você pode simplificar o processo verificando se há erros no corpo da função e registrando o estado:
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); }
Para manter a consistência em todo o ecossistema, use o ícone oficial de Dual Screen para indicar aos usuários como ativar ou desativar esse modo.
Para um exemplo funcional, consulte DualScreenActivity.kt (link em inglês).
Modo de tela traseira
Semelhante ao exemplo do modo Dual Screen, o seguinte exemplo de uma função toggleRearDisplayMode()
fecha a sessão caso o recurso já esteja ativo
ou chama a função transferActivityToWindowArea()
:
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 toggleDualScreenMode() { 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); } }
Nesse caso, a atividade mostrada é usada como um WindowAreaSessionCallback,
,
que é mais simples de implementar porque o callback não recebe um apresentador
que permita mostrar conteúdo em uma área de janela, mas transfere toda a atividade para outra área:
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}"); } }
Para manter a consistência em todo o ecossistema, use o ícone oficial de câmera traseira para indicar aos usuários como ativar ou desativar esse modo.
Outros recursos
- Codelab Conheça os recursos da câmera em dispositivos dobráveis
- Resumo do pacote
androidx.window.area
- Exemplo de código da API Jetpack WindowManager (links em inglês):
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Otimizar o app de câmera em dispositivos dobráveis com a Jetpack WindowManager