Экран недавних

Экран «Недавние», также называемый экраном «Обзор», списком последних задач или экраном последних приложений, представляет собой системный пользовательский интерфейс, отображающий недавно использованные действия и задачи . Пользователь может перемещаться по списку, выбирать задачу для возобновления или удалять задачу из списка, смахнув ее.

Экран «Недавние» использует документоцентричную модель , представленную в Android 5.0 (уровень API 21), в которой несколько экземпляров одной и той же активности, содержащих разные документы, могут отображаться в виде задач на экране «Недавние». Например, Google Drive может иметь задачу для каждого из нескольких документов Google. Каждый документ отображается как задача на экране «Недавние»:

На экране «Недавние записи» отображаются два документа Google Drive, каждый из которых представлен как отдельная задача.

Другой распространенный пример: пользователь использует браузер и нажимает «Поделиться» > «Gmail» . Появляется экран создания письма в приложении Gmail. Нажатие кнопки «Недавние» в этот момент показывает, что Chrome и Gmail работают как отдельные задачи:

На экране «Недавние приложения» отображаются Chrome и Gmail, запущенные как отдельные задачи.

Обычно вы позволяете системе определять, как ваши задачи и действия отображаются на экране «Недавние». Изменять это поведение не требуется. Однако ваше приложение может определять, как и когда действия будут отображаться на экране «Недавние».

Класс ActivityManager.AppTask позволяет управлять задачами, а флаги активности класса Intent позволяют указать, когда активность добавляется или удаляется с экрана «Недавние». Кроме того, атрибуты <activity> позволяют задать поведение в манифесте.

Добавить задачи на экран «Недавние».

Использование флагов класса Intent для добавления задачи дает вам больший контроль над тем, когда и как документ открывается или повторно открывается на экране «Недавние». При использовании атрибутов <activity> вы можете выбрать между постоянным открытием документа в новой задаче или повторным использованием существующей задачи для этого документа.

Используйте флаг Intent для добавления задачи.

При создании нового документа для вашей активности вы вызываете метод startActivity() , передавая ему интент, запускающий активность. Чтобы вставить логический разрыв, чтобы система рассматривала вашу активность как новую задачу на экране «Недавние», передайте флаг FLAG_ACTIVITY_NEW_DOCUMENT в метод addFlags() Intent , запускающего активность.

Если при создании нового документа установить флаг FLAG_ACTIVITY_MULTIPLE_TASK , система всегда будет создавать новую задачу с целевой активностью в качестве корневого элемента. Эта настройка позволяет открывать один и тот же документ в нескольких задачах. Следующий код демонстрирует, как основная активность делает это и запускает новую активность из вашего составного документа:

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")
    }
}

Когда основное действие запускает новое действие, система выполняет поиск среди существующих задач той, чей намерение соответствует имени компонента намерения и данным намерения для данного действия. Если задача не найдена или намерение содержит флаг FLAG_ACTIVITY_MULTIPLE_TASK , создается новая задача, корневым элементом которой является данное действие.

Если система находит задачу, чье намерение соответствует имени компонента намерения и данным намерения, она выводит эту задачу на передний план и передает новое намерение в метод onNewIntent() . Новое действие получает намерение и создает новый документ на экране «Недавние», как показано в следующем примере:

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")
    }
}

В приведенном выше коде Activity обрабатывает маршрутизацию на уровне ОС ( onCreate и onNewIntent ), в то время как функция @Composable отвечает только за отрисовку пользовательского интерфейса на основе предоставленного состояния.

Используйте атрибут активности для добавления задачи.

В манифесте активности также можно указать, что она всегда запускает новую задачу, используя атрибут android:documentLaunchMode <activity> `. Этот атрибут имеет четыре значения, которые приводят к следующим эффектам при открытии пользователем документа в приложении:

intoExisting
Данное действие повторно использует существующую задачу для документа. Это аналогично установке флага FLAG_ACTIVITY_NEW_DOCUMENT без установки флага FLAG_ACTIVITY_MULTIPLE_TASK , как описано в разделе «Использование флага Intent для добавления задачи» .
always
Данное действие создает новую задачу для документа, даже если документ уже открыт. Использование этого значения эквивалентно установке флагов FLAG_ACTIVITY_NEW_DOCUMENT и FLAG_ACTIVITY_MULTIPLE_TASK .
none
Данное действие не создает новую задачу для документа. На экране «Недавние» это действие обрабатывается так же, как и по умолчанию. Отображается одна задача для приложения, которая возобновляется с того действия, которое пользователь запустил последним.
never
Данное действие не создает новую задачу для документа. Установка этого значения переопределяет поведение флагов FLAG_ACTIVITY_NEW_DOCUMENT и FLAG_ACTIVITY_MULTIPLE_TASK . Если в намерении установлен любой из этих флагов, и на экране «Недавние» отображается одна задача для приложения, оно возобновляет работу с того действия, которое пользователь запустил последним.

Удалить задачи

По умолчанию задача документа автоматически закрывается на экране «Недавние» после завершения своей активности. Вы можете изменить это поведение с помощью класса ActivityManager.AppTask , флага Intent или атрибута <activity> .

Вы всегда можете полностью исключить задачу из списка последних заданий, установив атрибут android:excludeFromRecents в значение true для параметра <activity> .

Вы можете установить максимальное количество задач, которые ваше приложение может отображать на экране «Недавние», задав атрибут <activity> android:maxRecents в виде целого числа. Когда будет достигнуто максимальное количество задач, наименее используемая задача исчезнет с экрана «Недавние». Значение по умолчанию — 16, а максимальное значение — 50 (25 на устройствах с небольшим объемом памяти). Значения меньше 1 недопустимы.

Используйте класс AppTask для удаления задач.

В действии, создающем новую задачу на экране «Недавние», можно указать, когда следует удалить задачу и завершить все связанные с ней действия, вызвав метод 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")
    }
}

Сохраните завершенные задачи.

Если вы хотите сохранить задачу на экране «Недавние», даже если ее активность завершилась, передайте флаг FLAG_ACTIVITY_RETAIN_IN_RECENTS в метод addFlags() интента, запускающего эту активность.

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())
        }

Для достижения того же эффекта установите атрибут <activity> android:autoRemoveFromRecents в значение true false `document-activity` и false для обычных активностей. Использование этого атрибута переопределяет флаг ` FLAG_ACTIVITY_RETAIN_IN_RECENTS .

Включить возможность обмена URL-адресами последних приложений (только для Pixel)

На устройствах Pixel под управлением Android 12 и выше пользователи могут делиться ссылками на недавно просмотренный веб-контент непосредственно с экрана «Недавние». После просмотра контента в приложении пользователь может провести пальцем по экрану «Недавние», найти приложение, в котором он просматривал контент, а затем нажать кнопку ссылки, чтобы скопировать или поделиться URL-адресом.

Экран «Недавние» со ссылкой для обмена недавно просмотренным веб-контентом.

Любое приложение может включить функцию "Недавние ссылки" для пользователей, предоставив веб-интерфейс и переопределив onProvideAssistContent() , как показано в следующем примере:

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)
    }
}