Экран «Недавние», также называемый экраном «Обзор», списком последних задач или экраном последних приложений, представляет собой системный пользовательский интерфейс, отображающий недавно использованные действия и задачи . Пользователь может перемещаться по списку, выбирать задачу для возобновления или удалять задачу из списка, смахнув ее.
Экран «Недавние» использует документоцентричную модель , представленную в Android 5.0 (уровень API 21), в которой несколько экземпляров одной и той же активности, содержащих разные документы, могут отображаться в виде задач на экране «Недавние». Например, Google Drive может иметь задачу для каждого из нескольких документов Google. Каждый документ отображается как задача на экране «Недавние»:
Другой распространенный пример: пользователь использует браузер и нажимает «Поделиться» > «Gmail» . Появляется экран создания письма в приложении 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) } }