Cómo crear una app para tomar notas

Tomar notas es una función principal de Android que mejora la productividad del usuario en dispositivos con pantalla grande. Las apps para tomar notas les permiten a los usuarios escribir y dibujar en una ventana flotante o en la pantalla completa, capturar y anotar el contenido de la pantalla, y guardar notas para revisarlas y revisarlas más tarde.

Los usuarios pueden acceder a las apps para tomar notas desde la pantalla de bloqueo o mientras ejecutan otras apps.

La compatibilidad con la pluma stylus para tomar notas ofrece una experiencia del usuario excepcional.

Rol de notas

El rol de RoleManager.ROLE_NOTES identifica a las apps para tomar notas y les otorga el permiso LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE.

Para adquirir el rol de notas para tu app, haz lo siguiente:

  1. Llama a isRoleAvailable() para verificar el estado del rol.
  2. Si el rol de notas está disponible, llama a createRequestRoleIntent() para obtener un intent específico para notas.
  3. Llama a startActivityForResult() con el intent de notas para solicitarle al usuario que le otorgue el rol de notas a tu app.

Solo una app puede tener el rol de notas.

La app se abre en respuesta a una acción de intent ACTION_CREATE_NOTE implícita. Si se invoca desde la pantalla de bloqueo del dispositivo, la app abre la pantalla completa; si se invoca mientras la pantalla está desbloqueada, lo hace en una ventana flotante.

Manifiesto de la app

Para cumplir con los requisitos para el rol de notas, tu app debe incluir la siguiente declaración en su manifiesto:

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

La declaración permite que los usuarios le asignen el rol de notas a tu app, lo que la convierte en la aplicación predeterminada para tomar notas:

  • ACTION_CREATE_NOTE establece la acción de intent a la que responde tu app.

  • showWhenLocked permite que se pueda acceder a tu app desde la pantalla de bloqueo del dispositivo.

  • turnScreenOn permite que tu app encienda la pantalla del dispositivo cuando se ejecuta.

Funciones de la app

Una app diferenciada para tomar notas en una pantalla grande ofrece un complemento completo de las funciones para tomar notas.

Compatibilidad con pluma stylus

Cuando se invoca tu app con el intent adicional EXTRA_USE_STYLUS_MODE establecido en true, la app debe abrir una nota que acepte la entrada de la pluma stylus (o entradas táctiles).

Si el intent adicional se establece en false, tu app debe abrir una nota que acepte la entrada del teclado.

Acceso a la pantalla de bloqueo

Tu app debe proporcionar una actividad de pantalla completa que se ejecute cuando se abra desde la pantalla de bloqueo del dispositivo.

Tu app solo debe mostrar notas históricas si el usuario dio su consentimiento (en el estado de dispositivo desbloqueado) para mostrar notas anteriores. De lo contrario, cuando se abra desde la pantalla de bloqueo, la app siempre deberá crear una nota nueva.

Puedes verificar si tu app se inició desde la pantalla de bloqueo con KeyguardManager#isKeyguardLocked(). Para pedirle al usuario que se autentique y desbloquee el dispositivo, llama a 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.
  }
});

Ventanas flotantes

Para tomar notas contextuales, tu app debe proporcionar una actividad que se abra en una ventana flotante cuando se esté ejecutando otra aplicación.

Tu app debería admitir el modo multi-instance para que los usuarios puedan crear varias notas en múltiples ventanas flotantes, incluso cuando tu app para tomar notas se inicie en pantalla completa o en el modo de pantalla dividida.

Captura de contenido

La captura de contenido es una función clave de las apps para tomar notas. Con la captura de contenido, los usuarios pueden tomar capturas de pantalla de la pantalla detrás de la ventana flotante de la app para tomar notas. Los usuarios pueden capturar toda la pantalla o parte de ella, pegar el contenido en su nota y realizar anotaciones o destacar el contenido capturado.

Tu app para tomar notas debe proporcionar una indicación visual de la IU que inicie un objeto ActivityResultLauncher creado por registerForActivityResult(). La acción de intent ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE se le proporciona al selector directamente o a través de un ActivityResultContract.

Una actividad del sistema captura el contenido, lo guarda en el dispositivo y le muestra el URI de contenido a tu app en el argumento de devolución de llamada de registerForActivityResult().

En el siguiente ejemplo, se usa un 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));
        });
}

Tu app debe controlar todos los códigos de resultado:

Cuando la captura de contenido se realice de forma correcta, pega la imagen capturada en la nota, por ejemplo:

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

La función de captura de contenido debe exponerse a través de una indicación visual de la IU solo cuando tu app para tomar notas se ejecuta en una ventana flotante, no cuando se ejecuta en pantalla completa desde la pantalla de bloqueo del dispositivo. (los usuarios pueden tomar capturas de pantalla de la misma app para tomar notas con las funciones de captura de pantalla del dispositivo).

Para determinar si tu app está en una ventana flotante (o un cuadro), llama a los siguientes métodos:

  • isLaunchedFromBubble() para comprobar que la app para tomar notas no se inició en pantalla completa desde la pantalla de bloqueo del dispositivo
  • isRoleHeld(RoleManager.ROLE_NOTES) para verificar que tu app es la predeterminada para tomar notas (se puede ejecutar en una conversación o en otro tipo de cuadro si no tiene el rol de notas)

Recursos adicionales