Shows annotations in overlay mode.


/*
* Copyright © 2018-2026 PSPDFKit GmbH. All rights reserved.
*
* The PSPDFKit Sample applications are licensed with a modified BSD license.
* Please see License for details. This notice may not be removed from this file.
*/
package com.pspdfkit.catalog.examples.java;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import com.pspdfkit.annotations.Annotation;
import com.pspdfkit.annotations.AnnotationType;
import com.pspdfkit.catalog.R;
import com.pspdfkit.catalog.SdkExample;
import com.pspdfkit.catalog.tasks.ExtractAssetTask;
import com.pspdfkit.catalog.ui.CustomAnnotationEditingToolbarGroupingRule;
import com.pspdfkit.configuration.activity.PdfActivityConfiguration;
import com.pspdfkit.ui.PdfActivity;
import com.pspdfkit.ui.PdfActivityIntentBuilder;
import com.pspdfkit.ui.rendering.AnnotationOverlayRenderStrategy;
import com.pspdfkit.ui.toolbar.AnnotationEditingToolbar;
import com.pspdfkit.ui.toolbar.ContextualToolbar;
import com.pspdfkit.ui.toolbar.ContextualToolbarMenuItem;
import com.pspdfkit.ui.toolbar.ToolbarCoordinatorLayout;
import java.util.EnumSet;
import java.util.List;
/** Showcases how to display annotations in overlay mode. */
public class AnnotationOverlayExample extends SdkExample {
public AnnotationOverlayExample(@NonNull final Context context) {
super(context, R.string.annotationOverlayExampleTitle, R.string.annotationOverlayExampleDescription);
}
@Override
public void launchExample(
@NonNull final Context context, @NonNull final PdfActivityConfiguration.Builder configuration) {
// Extract the document from the assets.
ExtractAssetTask.extract(ANNOTATIONS_EXAMPLE, getTitle(), context, documentFile -> {
// To start the example create a launch intent using the builder.
final Intent intent = PdfActivityIntentBuilder.fromUri(context, Uri.fromFile(documentFile))
.configuration(configuration.build())
.activityClass(AnnotationOverlayActivity.class)
.build();
context.startActivity(intent);
});
}
/** Showcases how to enable overlay mode for annotations. */
public static class AnnotationOverlayActivity extends PdfActivity
implements ToolbarCoordinatorLayout.OnContextualToolbarLifecycleListener {
/** Holds annotation types that should be rendered in overlay while overlay is disabled. */
@NonNull
private final EnumSet<AnnotationType> manuallyOverlaidAnnotationTypes = EnumSet.noneOf(AnnotationType.class);
/** Current strategy used for rendering annotations in overlay. */
@NonNull
private AnnotationOverlayRenderStrategy.Strategy currentOverlayRenderingStrategy =
AnnotationOverlayRenderStrategy.Strategy.AP_STREAM_RENDERING;
/** Flag indicating whether annotation overlay is enabled. */
private boolean annotationOverlayEnabled = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// We'll set overlay render strategy that just returns currently configured strategy for all
// annotations.
requirePdfFragment().setAnnotationOverlayRenderStrategy(annotation -> currentOverlayRenderingStrategy);
// We'll enable overlay for all supported annotation types immediately after activity
// creation.
enableOverlayForSupportedAnnotationTypes();
invalidateOptionsMenu();
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
final int itemId = item.getItemId();
if (itemId == R.id.toggle_annotation_overlay) {
if (annotationOverlayEnabled) {
disableOverlayForAllAnnotationTypes();
} else {
enableOverlayForSupportedAnnotationTypes();
}
invalidateOptionsMenu();
return true;
} else if (itemId == R.id.fire_low_memory_notification) { // We fire low memory notification manually
// to showcase how annotation overlay mode
// behaves when system is low on memory.
requirePdfFragment().onLowMemory();
return true;
} else if (itemId == R.id.toggle_overlay_rendering_strategy) {
// Toggle the current overlay rendering strategy.
if (currentOverlayRenderingStrategy == AnnotationOverlayRenderStrategy.Strategy.AP_STREAM_RENDERING) {
currentOverlayRenderingStrategy = AnnotationOverlayRenderStrategy.Strategy.PLATFORM_RENDERING;
} else {
currentOverlayRenderingStrategy = AnnotationOverlayRenderStrategy.Strategy.AP_STREAM_RENDERING;
}
// Invalidate options to change button text to current state.
invalidateOptionsMenu();
return true;
}
return super.onOptionsItemSelected(item);
}
private void disableOverlayForAllAnnotationTypes() {
annotationOverlayEnabled = false;
applyManuallyOverlaidAnnotationTypes();
}
private void enableOverlayForSupportedAnnotationTypes() {
// Passing all annotation types enables overlay mode for all types that support overlay.
requirePdfFragment().setOverlaidAnnotationTypes(EnumSet.allOf(AnnotationType.class));
annotationOverlayEnabled = true;
}
private void applyManuallyOverlaidAnnotationTypes() {
final EnumSet<AnnotationType> overlayTypes = EnumSet.noneOf(AnnotationType.class);
overlayTypes.addAll(manuallyOverlaidAnnotationTypes);
requirePdfFragment().setOverlaidAnnotationTypes(overlayTypes);
}
@Override
protected void onResume() {
super.onResume();
// Register toolbar listener so we can add custom buttons for enabling/disabling overlay for
// selected annotations.
setOnContextualToolbarLifecycleListener(this);
}
@Override
protected void onPause() {
super.onPause();
setOnContextualToolbarLifecycleListener(null);
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
super.onCreateOptionsMenu(menu);
if (annotationOverlayEnabled) {
menu.add(0, R.id.toggle_annotation_overlay, 0, "Disable annotation overlay");
// Toggling overlay strategy does not make sense when annotation overlay is disabled.
if (currentOverlayRenderingStrategy == AnnotationOverlayRenderStrategy.Strategy.PLATFORM_RENDERING) {
menu.add(0, R.id.toggle_overlay_rendering_strategy, 0, "Use AP stream rendering");
} else {
menu.add(0, R.id.toggle_overlay_rendering_strategy, 0, "Use platform rendering");
}
} else {
menu.add(0, R.id.toggle_annotation_overlay, 0, "Enable annotation overlay");
}
menu.add(0, R.id.fire_low_memory_notification, 0, "Fire low memory notification");
return true;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
List<Annotation> selectedAnnotations = requirePdfFragment().getSelectedAnnotations();
if (selectedAnnotations.size() != 1) return;
final AnnotationType annotationType = selectedAnnotations.get(0).getType();
if (manuallyOverlaidAnnotationTypes.contains(annotationType)) {
menu.add(0, R.id.toggle_annotation_overlay, 0, "Disable overlay");
} else {
menu.add(0, R.id.toggle_annotation_overlay, 0, "Enable overlay");
}
}
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
if (requirePdfFragment().getSelectedAnnotations().size() != 1) {
return super.onContextItemSelected(item);
}
final Annotation annotation =
requirePdfFragment().getSelectedAnnotations().get(0);
final AnnotationType annotationType = annotation.getType();
final int itemId = item.getItemId();
if (itemId == R.id.toggle_annotation_overlay) {
if (manuallyOverlaidAnnotationTypes.contains(annotationType)) {
manuallyOverlaidAnnotationTypes.remove(annotationType);
} else {
manuallyOverlaidAnnotationTypes.add(annotationType);
}
applyManuallyOverlaidAnnotationTypes();
return true;
}
return super.onContextItemSelected(item);
}
@Override
public void onPrepareContextualToolbar(@NonNull ContextualToolbar toolbar) {
// Add item to annotation editing toolbar that shows our context menu for toggling
// overlay for selected annotation if the annotation overlay is disabled.
if (annotationOverlayEnabled) return;
if (toolbar instanceof AnnotationEditingToolbar) {
if (requirePdfFragment().getSelectedAnnotations().size() != 1) return;
// Set custom grouping rule for our extended editing toolbar.
toolbar.setMenuItemGroupingRule(new CustomAnnotationEditingToolbarGroupingRule(this));
// Get the existing menu items so we can add our item later.
final List<ContextualToolbarMenuItem> menuItems = ((AnnotationEditingToolbar) toolbar).getMenuItems();
// Create custom menu item.
final ContextualToolbarMenuItem customItem = ContextualToolbarMenuItem.createSingleItem(
this,
R.id.pspdf_menu_custom,
ContextCompat.getDrawable(this, R.drawable.ic_settings),
"Annotation Overlay",
Color.WHITE,
Color.WHITE,
ContextualToolbarMenuItem.Position.END,
false);
// Registers a context menu to be shown for the custom item.
registerForContextMenu(customItem);
// Add the custom item to our toolbar.
menuItems.add(customItem);
toolbar.setMenuItems(menuItems);
// Add a click listener so we can handle clicks on custom item.
toolbar.setOnMenuItemClickListener((toolbar1, menuItem) -> {
if (menuItem.getId() == R.id.pspdf_menu_custom) {
menuItem.showContextMenu();
return true;
}
return false;
});
}
}
@Override
public void onDisplayContextualToolbar(@NonNull ContextualToolbar toolbar) {
// no-op
}
@Override
public void onRemoveContextualToolbar(@NonNull ContextualToolbar toolbar) {
// no-op
}
}
}

This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.