chromium/components/payments/content/android/java/src/org/chromium/components/payments/JourneyLogger.java

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.payments;

import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;

import org.chromium.content_public.browser.WebContents;

import java.util.List;

/** A class used to record journey metrics for the Payment Request feature. */
@JNINamespace("payments")
public class JourneyLogger {
    /** Pointer to the native implementation. */
    private long mJourneyLoggerAndroid;

    private boolean mHasRecorded;

    /**
     * Creates the journey logger.
     *
     * @param webContents The web contents where PaymentRequest API is invoked. Should not be null.
     */
    public JourneyLogger(WebContents webContents) {
        assert webContents != null;
        assert !webContents.isDestroyed();
        // Note that this pointer could leak the native object. The called must call destroy() to
        // ensure that the native object is destroyed.
        mJourneyLoggerAndroid =
                JourneyLoggerJni.get().initJourneyLoggerAndroid(JourneyLogger.this, webContents);
    }

    /** Will destroy the native object. This class shouldn't be used afterwards. */
    public void destroy() {
        if (mJourneyLoggerAndroid != 0) {
            JourneyLoggerJni.get().destroy(mJourneyLoggerAndroid, JourneyLogger.this);
            mJourneyLoggerAndroid = 0;
        }
    }

    /**
     * Sets the number of suggestions shown for the specified section.
     *
     * @param section               The section for which to log.
     * @param number                The number of suggestions.
     * @param hasCompleteSuggestion Whether the section has at least one
     *                              complete suggestion.
     */
    public void setNumberOfSuggestionsShown(
            int section, int number, boolean hasCompleteSuggestion) {
        assert section < Section.MAX;
        JourneyLoggerJni.get()
                .setNumberOfSuggestionsShown(
                        mJourneyLoggerAndroid,
                        JourneyLogger.this,
                        section,
                        number,
                        hasCompleteSuggestion);
    }

    /** Records that an Opt Out experience is being offered to the user in the current UI flow. */
    public void setOptOutOffered() {
        JourneyLoggerJni.get().setOptOutOffered(mJourneyLoggerAndroid, JourneyLogger.this);
    }

    /** Records that a PaymentRequest.show() was allowed without a user activation. */
    public void setActivationlessShow() {
        JourneyLoggerJni.get().setActivationlessShow(mJourneyLoggerAndroid, JourneyLogger.this);
    }

    /** Records that a payment app has been invoked without any payment UI being shown before that. */
    public void setSkippedShow() {
        JourneyLoggerJni.get().setSkippedShow(mJourneyLoggerAndroid, JourneyLogger.this);
    }

    /** Records that a payment UI has been shown. */
    public void setShown() {
        JourneyLoggerJni.get().setShown(mJourneyLoggerAndroid, JourneyLogger.this);
    }

    /** Records that a payment app was invoked. */
    public void setPayClicked() {
        JourneyLoggerJni.get().setPayClicked(mJourneyLoggerAndroid, JourneyLogger.this);
    }

    /**
     * Records the method that has been selected and invoked.
     *
     * @param category The category of the method.
     */
    public void setSelectedMethod(@PaymentMethodCategory int category) {
        JourneyLoggerJni.get()
                .setSelectedMethod(mJourneyLoggerAndroid, JourneyLogger.this, category);
    }

    /*
     * Records what user information were requested by the merchant to complete the Payment Request.
     *
     * @param requestShipping Whether the merchant requested a shipping address.
     * @param requestEmail    Whether the merchant requested an email address.
     * @param requestPhone    Whether the merchant requested a phone number.
     * @param requestName     Whether the merchant requestes a name.
     */
    public void setRequestedInformation(
            boolean requestShipping,
            boolean requestEmail,
            boolean requestPhone,
            boolean requestName) {
        JourneyLoggerJni.get()
                .setRequestedInformation(
                        mJourneyLoggerAndroid,
                        JourneyLogger.this,
                        requestShipping,
                        requestEmail,
                        requestPhone,
                        requestName);
    }

    /*
     * Records what types of payment methods were requested by the merchant in the Payment Request.
     *
     * @param methodTypes The list of types of the payment methods, defined in
     *        {@link PaymentMethodCategories}.
     */
    public void setRequestedPaymentMethods(List<Integer> methodTypes) {
        int[] methods = new int[methodTypes.size()];
        for (int i = 0; i < methodTypes.size(); i++) {
            methods[i] = methodTypes.get(i);
        }
        JourneyLoggerJni.get()
                .setRequestedPaymentMethods(mJourneyLoggerAndroid, JourneyLogger.this, methods);
    }

    /**
     * Records that the Payment Request was completed successfully. Also starts the logging of
     * all the journey logger metrics.
     */
    public void setCompleted() {
        assert !mHasRecorded;

        if (!mHasRecorded) {
            mHasRecorded = true;
            JourneyLoggerJni.get().setCompleted(mJourneyLoggerAndroid, JourneyLogger.this);
        }
    }

    /**
     * Records that the Payment Request was aborted. This counts as a completion, starting the
     * logging of all the journey metrics.
     *
     * @param reason An int indicating why the payment request was aborted.
     */
    public void setAborted(int reason) {
        assert reason < AbortReason.MAX;

        // The abort reasons on Android cascade into each other, so only the first one should be
        // recorded.
        if (!mHasRecorded) {
            mHasRecorded = true;
            JourneyLoggerJni.get().setAborted(mJourneyLoggerAndroid, JourneyLogger.this, reason);
        }
    }

    /** Records that the Payment Request was not shown to the user. */
    public void setNotShown() {
        assert !mHasRecorded;

        if (!mHasRecorded) {
            mHasRecorded = true;
            JourneyLoggerJni.get().setNotShown(mJourneyLoggerAndroid, JourneyLogger.this);
        }
    }

    /** Records that the No Matching Credentials UX was shown to the user. */
    public void setNoMatchingCredentialsShown() {
        JourneyLoggerJni.get()
                .setNoMatchingCredentialsShown(mJourneyLoggerAndroid, JourneyLogger.this);
    }

    /**
     * Records that the payment request has entered the given checkout step.
     * @param step An int indicating the step to be recorded.
     */
    public void recordCheckoutStep(int step) {
        JourneyLoggerJni.get().recordCheckoutStep(mJourneyLoggerAndroid, JourneyLogger.this, step);
    }

    /**
     * Sets the ukm source id of payment app.
     * @param sourceId A long indicating the ukm source id of the invoked payment app.
     */
    public void setPaymentAppUkmSourceId(long sourceId) {
        JourneyLoggerJni.get()
                .setPaymentAppUkmSourceId(mJourneyLoggerAndroid, JourneyLogger.this, sourceId);
    }

    @NativeMethods
    interface Natives {
        long initJourneyLoggerAndroid(JourneyLogger caller, WebContents webContents);

        void destroy(long nativeJourneyLoggerAndroid, JourneyLogger caller);

        void setNumberOfSuggestionsShown(
                long nativeJourneyLoggerAndroid,
                JourneyLogger caller,
                int section,
                int number,
                boolean hasCompleteSuggestion);

        void setOptOutOffered(long nativeJourneyLoggerAndroid, JourneyLogger caller);

        void setActivationlessShow(long nativeJourneyLoggerAndroid, JourneyLogger caller);

        void setSkippedShow(long nativeJourneyLoggerAndroid, JourneyLogger caller);

        void setShown(long nativeJourneyLoggerAndroid, JourneyLogger caller);

        void setPayClicked(long nativeJourneyLoggerAndroid, JourneyLogger caller);

        void setSelectedMethod(
                long nativeJourneyLoggerAndroid, JourneyLogger caller, int paymentMethodCategory);

        void setRequestedInformation(
                long nativeJourneyLoggerAndroid,
                JourneyLogger caller,
                boolean requestShipping,
                boolean requestEmail,
                boolean requestPhone,
                boolean requestName);

        void setRequestedPaymentMethods(
                long nativeJourneyLoggerAndroid, JourneyLogger caller, int[] methodTypes);

        void setCompleted(long nativeJourneyLoggerAndroid, JourneyLogger caller);

        void setAborted(long nativeJourneyLoggerAndroid, JourneyLogger caller, int reason);

        void setNotShown(long nativeJourneyLoggerAndroid, JourneyLogger caller);

        void setNoMatchingCredentialsShown(long nativeJourneyLoggerAndroid, JourneyLogger caller);

        void recordCheckoutStep(long nativeJourneyLoggerAndroid, JourneyLogger caller, int step);

        void setPaymentAppUkmSourceId(
                long nativeJourneyLoggerAndroid, JourneyLogger caller, long sourceId);
    }
}