Java tutorial
/* * PSPDFKitModule.java * * PSPDFKit * * Copyright 2017-2019 PSPDFKit GmbH. All rights reserved. * * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT. * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES. * This notice may not be removed from this file. */ package com.pspdfkit.react; import android.app.Activity; import android.app.Application; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import com.facebook.react.bridge.ActivityEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; import com.pspdfkit.PSPDFKit; import com.pspdfkit.document.PdfDocument; import com.pspdfkit.document.image.CameraImagePickerFragment; import com.pspdfkit.document.image.GalleryImagePickerFragment; import com.pspdfkit.instant.ui.InstantPdfActivity; import com.pspdfkit.listeners.SimpleDocumentListener; import com.pspdfkit.ui.PdfActivity; import com.pspdfkit.ui.PdfFragment; import java.util.HashMap; import java.util.Map; public class PSPDFKitModule extends ReactContextBaseJavaModule implements Application.ActivityLifecycleCallbacks, ActivityEventListener { private static final String VERSION_KEY = "versionString"; private static final String FILE_SCHEME = "file:///"; private static final int REQUEST_CODE_TO_INDEX = 16; private static final int MASKED_REQUEST_CODE_TO_REAL_CODE = 0xffff; @Nullable private Activity resumedActivity; @Nullable private Runnable onPdfActivityOpenedTask; /** * Used to dispatch onActivityResult calls to our fragments. */ @NonNull private Handler activityResultHandler = new Handler(Looper.getMainLooper()); public PSPDFKitModule(ReactApplicationContext reactContext) { super(reactContext); } @Override public void initialize() { super.initialize(); getReactApplicationContext().addActivityEventListener(this); } @Override public void onCatalystInstanceDestroy() { super.onCatalystInstanceDestroy(); getReactApplicationContext().removeActivityEventListener(this); } @Override public String getName() { return "PSPDFKit"; } @ReactMethod public void present(@NonNull String document, @NonNull ReadableMap configuration) { if (getCurrentActivity() != null) { if (resumedActivity == null) { // We register an activity lifecycle callback so we can get notified of the current activity. getCurrentActivity().getApplication().registerActivityLifecycleCallbacks(this); } ConfigurationAdapter configurationAdapter = new ConfigurationAdapter(getCurrentActivity(), configuration); // This is an edge case where file scheme is missing. if (Uri.parse(document).getScheme() == null) { document = FILE_SCHEME + document; } PdfActivity.showDocument(getCurrentActivity(), Uri.parse(document), configurationAdapter.build()); } } @ReactMethod public void presentImage(@NonNull String imageDocument, @NonNull ReadableMap configuration) { if (getCurrentActivity() != null) { if (resumedActivity == null) { // We register an activity lifecycle callback so we can get notified of the current activity. getCurrentActivity().getApplication().registerActivityLifecycleCallbacks(this); } ConfigurationAdapter configurationAdapter = new ConfigurationAdapter(getCurrentActivity(), configuration); // This is an edge case where file scheme is missing. if (Uri.parse(imageDocument).getScheme() == null) { imageDocument = FILE_SCHEME + imageDocument; } PdfActivity.showImage(getCurrentActivity(), Uri.parse(imageDocument), configurationAdapter.build()); } } @ReactMethod public void presentInstant(@NonNull String serverUrl, @NonNull String jwt, @NonNull ReadableMap configuration) { if (getCurrentActivity() != null) { if (resumedActivity == null) { // We register an activity lifecycle callback so we can get notified of the current activity. getCurrentActivity().getApplication().registerActivityLifecycleCallbacks(this); } ConfigurationAdapter configurationAdapter = new ConfigurationAdapter(getCurrentActivity(), configuration); InstantPdfActivity.showInstantDocument(getCurrentActivity(), serverUrl, jwt, configurationAdapter.build()); } } @ReactMethod public synchronized void setPageIndex(final int pageIndex, final boolean animated) { if (resumedActivity instanceof PdfActivity) { final PdfActivity activity = (PdfActivity) resumedActivity; activity.runOnUiThread(new Runnable() { @Override public void run() { if (activity.getDocument() != null) { // If the document is loaded we can instantly set the page index. activity.setPageIndex(pageIndex, animated); } else { activity.getPdfFragment().addDocumentListener(new SimpleDocumentListener() { @Override public void onDocumentLoaded(@NonNull PdfDocument document) { // Once the document is loaded set the page index. activity.setPageIndex(pageIndex, animated); activity.getPdfFragment().removeDocumentListener(this); } }); } } }); } else { // Queue up a runnable to set the page index as soon as a PdfActivity is available. onPdfActivityOpenedTask = new Runnable() { @Override public void run() { setPageIndex(pageIndex, animated); } }; } } @NonNull @Override public Map<String, Object> getConstants() { final Map<String, Object> constants = new HashMap<>(); constants.put(VERSION_KEY, PSPDFKit.VERSION); return constants; } @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } @Override public void onActivityStarted(Activity activity) { } @Override public synchronized void onActivityResumed(Activity activity) { resumedActivity = activity; if (resumedActivity instanceof PdfActivity && onPdfActivityOpenedTask != null) { // Run our queued up task when a PdfActivity is displayed. onPdfActivityOpenedTask.run(); onPdfActivityOpenedTask = null; } } @Override public synchronized void onActivityPaused(Activity activity) { if (activity == resumedActivity) { resumedActivity = null; } } @Override public void onActivityStopped(Activity activity) { } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public synchronized void onActivityDestroyed(Activity activity) { if (activity == resumedActivity) { resumedActivity = null; } } @Override public void onActivityResult(Activity activity, final int requestCode, final int resultCode, final Intent data) { if (activity instanceof FragmentActivity) { // Forward the result to all our fragments. FragmentActivity fragmentActivity = (FragmentActivity) activity; for (final Fragment fragment : fragmentActivity.getSupportFragmentManager().getFragments()) { if (fragment instanceof PdfFragment || fragment instanceof GalleryImagePickerFragment || fragment instanceof CameraImagePickerFragment) { // When starting an intent from a fragment its request code is shifted to make it unique, // we undo it here manually since react by default eats all activity results. int requestIndex = requestCode >> REQUEST_CODE_TO_INDEX; if (requestIndex != 0) { // We need to wait until the next frame with delivering the result to the fragment, // otherwise the app will crash since the fragment won't be ready. activityResultHandler.post(new Runnable() { @Override public void run() { fragment.onActivityResult(requestCode & MASKED_REQUEST_CODE_TO_REAL_CODE, resultCode, data); } }); } } } } } @Override public void onNewIntent(Intent intent) { // Not required right now. } }