Telas recentes

A tela "Recentes", também chamada de tela "Visão geral", lista de tarefas recentes ou apps recentes, é uma interface do sistema que lista as atividades e tarefas acessadas recentemente. O usuário pode navegar pela lista, selecionar uma tarefa para retomar ou remover uma tarefa da lista deslizando-a para fora.

A tela "Recentes" usa um modelo centrado em documentos, introduzido no Android 5.0 (nível 21 da API), em que várias instâncias da mesma atividade que contêm documentos diferentes podem aparecer como tarefas na tela "Recentes". Por exemplo, o Google Drive pode ter uma tarefa para cada um dos diversos documentos do Google. Cada documento aparece como uma tarefa na tela "Recentes":

A tela "Recentes" mostrando dois documentos do Google Drive, cada um representado como uma tarefa separada.

Outro exemplo comum é quando o usuário está usando o navegador e toca em Compartilhar > Gmail. A tela Escrever do app Gmail aparece. Ao tocar no botão Recentes nesse momento, o Chrome e o Gmail são executados como tarefas separadas:

A tela "Recentes" mostrando o Chrome e o Gmail executados como tarefas separadas.

Normalmente, você permite que o sistema defina como suas tarefas e atividades são representadas na tela de visão geral. Não é necessário modificar esse comportamento. No entanto, seu app pode determinar como e quando as atividades aparecem na tela "Recentes".

A classe ActivityManager.AppTask permite gerenciar tarefas, e as flags de atividade da classe Intent permitem especificar quando uma atividade é adicionada ou removida da tela "Recentes". Além disso, os atributos <activity> permitem definir o comportamento no manifesto.

Adicionar tarefas à tela "Recentes"

Usar as flags da classe Intent para adicionar uma tarefa oferece mais controle sobre quando e como um documento é aberto ou reaberto na tela "Recentes". Ao usar os atributos <activity>, você pode escolher entre sempre abrir o documento em uma nova tarefa ou reutilizar uma tarefa existente para o documento.

Usar a sinalização de intent para adicionar uma tarefa

Ao criar um novo documento para sua atividade, você chama o método startActivity() e transmite para ele o intent que inicia a atividade. Para inserir uma quebra lógica para que o sistema trate sua atividade como uma nova tarefa na tela Recentes, transmita a flag FLAG_ACTIVITY_NEW_DOCUMENT no método addFlags() do Intent que inicia a atividade.

Se você definir a flag FLAG_ACTIVITY_MULTIPLE_TASK ao criar o novo documento, o sistema sempre criará uma nova tarefa com a atividade de destino como a raiz. Essa configuração permite que o mesmo documento seja aberto em mais de uma tarefa. O código a seguir demonstra como a atividade principal faz isso e inicia a nova atividade do seu elemento combinável:

private fun newDocumentIntent(context: Context): Intent =
    Intent(context, NewDocumentActivity::class.java).apply {
        addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)
        putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, documentCounter++)
    }

@Composable
fun CreateDocumentButton() {
    val context = LocalContext.current
    Button(
        onClick = {
            val intent = newDocumentIntent(context)
            // Add FLAG_ACTIVITY_MULTIPLE_TASK if needed based on state
            context.startActivity(intent)
        }
    ) {
        Text("Create New Document")
    }
}

Quando a atividade principal inicia uma nova atividade, o sistema pesquisa as tarefas existentes por uma cujo intent corresponda ao nome do componente do intent e aos dados do intent da atividade. Se a tarefa não for encontrada ou se o intent contiver a flag FLAG_ACTIVITY_MULTIPLE_TASK, uma nova tarefa será criada com a atividade como raiz.

Se o sistema encontrar uma tarefa cujo intent corresponda ao nome do componente do intent e aos dados do intent, ela será levada para a frente e o novo intent será transmitido para onNewIntent(). A nova atividade recebe o intent e cria um novo documento na tela "Recentes", como mostrado no exemplo a seguir:

class DocumentCentricActivity : ComponentActivity() {
    private var documentState by mutableStateOf(
        DocumentState(
            count = 0,
            textResId = R.string.hello_new_document_counter
        )
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val initialCount = intent.getIntExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0)

        documentState = documentState.copy(count = initialCount)

        setContent {
            MaterialTheme {
                DocumentScreen(
                    count = documentState.count,
                    textResId = documentState.textResId
                )
            }
        }
    }

    override fun onNewIntent(newIntent: Intent) {
        super.onNewIntent(newIntent)
        // If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this Activity is reused.
        documentState = documentState.copy(
            textResId = R.string.reusing_document_counter
        )
    }

    data class DocumentState(val count: Int, @StringRes val textResId: Int)

    companion object {
        const val KEY_EXTRA_NEW_DOCUMENT_COUNTER = "KEY_EXTRA_NEW_DOCUMENT_COUNTER"
    }
}

@Composable
fun DocumentScreen(count: Int, @StringRes textResId: Int) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center
    ) {
        // UI reacts to whichever string resource ID was passed down
        Text(text = stringResource(id = textResId))
        Spacer(modifier = Modifier.height(8.dp))
        Text(text = "Counter: $count")
    }
}

No código anterior, a atividade processa o roteamento no nível do SO (onCreate e onNewIntent), enquanto a função @Composable é responsável apenas por renderizar a interface com base no estado fornecido.

Usar o atributo da atividade para adicionar uma tarefa

Uma atividade também pode especificar no manifesto que ela sempre se inicia em uma nova tarefa usando o atributo <activity> android:documentLaunchMode. Esse atributo tem quatro valores que produzem os seguintes efeitos quando o usuário abre um documento com o aplicativo:

intoExisting
A atividade reutiliza uma tarefa existente para o documento. Isso é o mesmo que definir a flag FLAG_ACTIVITY_NEW_DOCUMENT sem definir a flag FLAG_ACTIVITY_MULTIPLE_TASK, conforme descrito na seção Como usar a flag de intent para adicionar uma tarefa.
always
A atividade cria uma nova tarefa para o documento, mesmo se ele já estiver aberto. Usar esse valor é o mesmo que definir as flags FLAG_ACTIVITY_NEW_DOCUMENT e FLAG_ACTIVITY_MULTIPLE_TASK.
none
A atividade não cria uma nova tarefa para o documento. A tela Recentes trata a atividade como faria por padrão. Ela mostra uma única tarefa para o app, que é retomada de qualquer atividade que o usuário tenha invocado pela última vez.
never
A atividade não cria uma nova tarefa para o documento. Definir esse valor substitui o comportamento das flags FLAG_ACTIVITY_NEW_DOCUMENT e FLAG_ACTIVITY_MULTIPLE_TASK. Se uma delas estiver definida no intent e a tela Recentes mostrar uma única tarefa para o app, ela será retomada de qualquer atividade que o usuário tenha invocado pela última vez.

Remover tarefas

Por padrão, uma tarefa de documento sai automaticamente da tela "Recentes" quando a atividade dela termina. É possível substituir esse comportamento com a classe ActivityManager.AppTask, com uma flag Intent ou com um atributo <activity>.

Você pode excluir uma tarefa da tela "Recentes" por completo sempre que quiser, basta definir o atributo <activity> android:excludeFromRecents como true.

Você pode definir o número máximo de tarefas que seu app pode incluir na tela Recentes definindo o atributo <activity> android:maxRecents como um valor inteiro. Quando o número máximo de tarefas é atingido, a tarefa usada menos recentemente desaparece da tela "Recentes". O padrão é 16, e o valor máximo é 50 (25 em dispositivos com pouca memória). Valores menores que 1 não são válidos.

Como usar a classe AppTask para remover tarefas

Na atividade que cria uma nova tarefa na tela "Recentes", você pode especificar quando remover a tarefa e concluir todas as atividades associadas a ela chamando o método finishAndRemoveTask():

@Composable
fun RemoveTaskButton() {
    val context = LocalContext.current
    Button(
        onClick = {
            // It is good practice to remove a document from the overview stack if not needed anymore.
            (context as? Activity)?.finishAndRemoveTask()
        }
    ) {
        Text("Remove from Recents")
    }
}

Reter tarefas concluídas

Se você quiser reter uma tarefa na tela "Recentes", mesmo que a atividade tenha terminado, transmita a flag FLAG_ACTIVITY_RETAIN_IN_RECENTS no método addFlags() do intent que inicia a atividade.

private fun newDocumentIntent() =
        Intent(this, NewDocumentActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
                    android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)
            putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, getAndIncrement())
        }

Para conseguir o mesmo efeito, defina o atributo <activity> android:autoRemoveFromRecents como false. O valor padrão é true para atividades de documentos e false para atividades regulares. O uso desse atributo modifica a sinalização FLAG_ACTIVITY_RETAIN_IN_RECENTS.

Ativar o compartilhamento de URLs recentes (somente no Pixel)

Em dispositivos Pixel com Android 12 ou versões mais recentes, os usuários podem compartilhar links para conteúdo da Web acessado recentemente diretamente na tela "Recentes". Depois de acessar o conteúdo em um app, o usuário pode deslizar até a tela "Recentes" e encontrar o app em que visualizou o conteúdo. Em seguida, toque no botão de link para copiar ou compartilhar o URL.

A tela de visão geral com um link para compartilhar conteúdo da Web visualizado recentemente.

Qualquer app pode ativar a vinculação de "Recentes" para os usuários fornecendo uma interface da Web e substituindo onProvideAssistContent(), conforme mostrado no exemplo a seguir:

class MainActivity : ComponentActivity() {

    // Track the current URL as state so the UI can update it during navigation
    private var currentWebUri by mutableStateOf("https://example.com/home")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AppTheme {
                // Pass a lambda to your Compose UI so it can update the URL state
                // as the user navigates through your app.
                MainScreen(
                    onPageChanged = { newUrl -> currentWebUri = newUrl }
                )
            }
        }
    }

    override fun onProvideAssistContent(outContent: AssistContent) {
        super.onProvideAssistContent(outContent)

        // The system calls this when the user enters the Recents screen.
        // Provide the active URI tracked by the Compose state.
        outContent.webUri = Uri.parse(currentWebUri)
    }
}