Criar um app de anotações

A criação de anotações é um recurso importante do Android que melhora a produtividade do usuário em dispositivos de tela grande. Os apps de anotações permitem que os usuários escrevam e desenham em uma janela flutuante ou em tela cheia, capturem e façam anotações no conteúdo da tela e salvem anotações para revisão e revisão posteriores.

Os usuários podem acessar os apps de anotação na tela de bloqueio ou enquanto executam outros aplicativos.

A compatibilidade com a stylus para criar anotações proporciona uma experiência de usuário excepcional.

Papel de notas

O papel RoleManager.ROLE_NOTES identifica apps de anotações e concede a eles a permissão LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE.

Para adquirir a função de notas do seu app, faça o seguinte:

  1. Chame isRoleAvailable() para verificar o status do papel.
  2. Se a função de notas estiver disponível, chame createRequestRoleIntent() para receber uma intent específica.
  3. Chame startActivityForResult() com a intent de notas para solicitar que o usuário conceda essa função ao app.

Apenas um app pode ter essa função.

O app é aberto em resposta a uma ação da intent implícita ACTION_CREATE_NOTE. Se invocado a partir da tela de bloqueio do dispositivo, o app abre a tela cheia. Se invocado enquanto a tela está desbloqueada, em uma janela flutuante.

Manifesto do app

Para se qualificar para a função de notas, o app precisa incluir a seguinte declaração no manifesto:

<activity
    android:name="YourActivityName"
    android:exported="true"
    android:showWhenLocked="true"
    android:turnScreenOn="true">
    <intent-filter>
        <action android:name="android.intent.action.CREATE_NOTE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

A declaração permite que os usuários atribuam a função de notas ao app, tornando-o o aplicativo de notas padrão:

  • ACTION_CREATE_NOTE define a ação da intent a que o app responde.

  • O showWhenLocked torna o app acessível na tela de bloqueio do dispositivo

  • O turnScreenOn permite que o app ative a tela do dispositivo quando for executado.

Recursos do app

Um app de anotações diferenciado para telas grandes oferece um conjunto completo de recursos de anotações.

Compatibilidade com stylus

Quando o app é invocado com o extra da intent EXTRA_USE_STYLUS_MODE definido como true, ele abre uma nota que aceita entrada da stylus (ou toque do dedo).

Se o extra da intent estiver definido como false, seu app precisará abrir uma nota que aceite a entrada do teclado.

Acesso à tela de bloqueio

O app precisa oferecer uma atividade em tela cheia que é executada quando ele é aberto na tela de bloqueio do dispositivo.

O app só exibirá notas históricas se o usuário tiver permitido (no estado do dispositivo desbloqueado) a exibição de notas anteriores. Caso contrário, quando aberto na tela de bloqueio, o app precisará sempre criar uma nova nota.

É possível verificar se o app foi iniciado na tela de bloqueio com KeyguardManager#isKeyguardLocked(). Para pedir que o usuário autentique e desbloqueie o dispositivo, chame KeyguardManager#requestDismissKeyguard():

Kotlin

val keyguardManager = getSystemService(KEYGUARD_SERVICE) as KeyguardManager

keyguardManager.requestDismissKeyguard(
    this,
    object : KeyguardDismissCallback() {

    override fun onDismissError() {
        // Unlock failed. Dismissing keyguard is not feasible.
    }

    override fun onDismissSucceeded() {
        // Unlock succeeded. Device is now unlocked.
    }

    override fun onDismissCancelled() {
        // Unlock failed. User cancelled operation or request otherwise cancelled.
    }
})

Java

KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);

boolean isLocked = keyguardManager.isKeyguardLocked();

keyguardManager.requestDismissKeyguard(
    this,
    new KeyguardManager.KeyguardDismissCallback() {

  @Override
  public void onDismissError() {
      // Unlock failed. Dismissing keyguard is not feasible.
  }

  @Override
  public void onDismissSucceeded() {
      // Unlock succeeded. Device is now unlocked.
  }

  @Override
  public void onDismissCancelled() {
      // Unlock failed. User cancelled operation or request otherwise cancelled.
  }
});

Janelas flutuantes

Para anotações contextuais, seu app precisa fornecer uma atividade que seja aberta em uma janela flutuante quando outro aplicativo estiver em execução.

O app precisa oferecer suporte ao modo multi-instance para que os usuários possam criar várias notas em várias janelas flutuantes, mesmo quando o app de anotações for iniciado em tela cheia ou no modo de tela dividida.

Captura de conteúdo

A captura de conteúdo é um recurso importante dos apps de anotação. Com a captura de conteúdo, os usuários podem fazer capturas de tela da tela por trás da janela flutuante do app de anotações. Os usuários podem capturar toda a tela ou parte dela, colar o conteúdo nas anotações e anotar ou destacar o conteúdo capturado.

Seu app de anotações precisa fornecer uma funcionalidade de interface que inicie uma ActivityResultLauncher criada por registerForActivityResult(). A ação da intent ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE é fornecida à tela de início diretamente ou por um ActivityResultContract.

Uma atividade do sistema captura o conteúdo, o salva no dispositivo e retorna o URI de conteúdo ao app no argumento de callback de registerForActivityResult().

O exemplo a seguir usa um contrato StartActivityForResult genérico:

Kotlin

private val startForResult = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()) {
        result: ActivityResult ->
            if (result.resultCode == Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS) {
                val uri = result.data?.data
                // Use the URI to paste the captured content into the note.
            }
    }

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        NotesTheme {
            Surface(color = MaterialTheme.colorScheme.background) {
                CaptureButton(
                    onClick = {
                        Log.i("ContentCapture", "Launching intent...")
                        startForResult.launch(Intent(ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE))
                    })
            }
        }
    }
}

@Composable
fun CaptureButton(onClick: () -> Unit) {
    Button(onClick = onClick)
    {Text("Capture Content")}
}

Java

private final ActivityResultLauncher<Intent> startForResult = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
    result -> {
        if (result.getResultCode() == Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS) {
            Uri uri = result.getData() != null ? result.getData().getData() : null;
            // Use the URI to paste the captured content into the note.
        }
    });

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button captureButton = findViewById(R.id.capture_button);

    captureButton.setOnClickListener(
        view -> {
            Log.i("ContentCapture", "Launching intent...");
            startForResult.launch(new Intent(ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE));
        });
}

Seu app precisa processar todos os códigos de resultado:

Quando a captura de conteúdo for concluída, cole a imagem capturada na nota, por exemplo:

Kotlin

registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
    result: ActivityResult ->
        if (result.resultCode == Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS) {
            val uri = result.data?data
            // Use the URI to paste the captured content into the note.
        }
}

Java

registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
    result -> {
        if (result.getResultCode() == Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS) {
            Uri uri = result.getData() != null ? result.getData().getData() : null;
            // Use the URI to paste the captured content into the note.
        }
    });

O recurso de captura de conteúdo deve ser exposto em uma funcionalidade de interface somente quando o app de anotações estiver em execução em uma janela flutuante, e não quando estiver em tela cheia, iniciado na tela de bloqueio do dispositivo. Os usuários podem fazer capturas de tela do próprio app de anotações com os recursos de captura de tela do dispositivo.

Para determinar se o app está em uma janela flutuante (ou um balão), chame estes métodos:

  • isLaunchedFromBubble() para verificar se o app de anotações não foi iniciado em tela cheia na tela de bloqueio do dispositivo.
  • isRoleHeld(RoleManager.ROLE_NOTES) para verificar se o app é o padrão para anotações. Ele pode ser executado em uma conversa ou outro tipo de balão se ele não tiver a função de notas.

Outros recursos