<commit_before>package com.anysoftkeyboard.ime;

import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.DialogInterface;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import com.anysoftkeyboard.api.KeyCodes;
import com.anysoftkeyboard.devicespecific.Clipboard;
import com.anysoftkeyboard.keyboards.Keyboard;
import com.anysoftkeyboard.keyboards.views.KeyboardViewContainerView;
import com.anysoftkeyboard.rx.GenericOnError;
import com.menny.android.anysoftkeyboard.AnyApplication;
import com.menny.android.anysoftkeyboard.R;
import java.util.ArrayList;
import java.util.List;
import net.evendanan.pixel.GeneralDialogController;

public abstract class AnySoftKeyboardClipboard extends AnySoftKeyboardSwipeListener {

    private boolean mArrowSelectionState;
    private Clipboard mClipboard;
    protected static final int MAX_CHARS_PER_CODE_POINT = 2;
    private static final long MAX_TIME_TO_SHOW_SYNCED_CLIPBOARD_ENTRY = 15 * 1000;
    private static final long MAX_TIME_TO_SHOW_SYNCED_CLIPBOARD_HINT = 120 * 1000;
    private long mLastSyncedClipboardEntryTime = Long.MIN_VALUE;
    @Nullable private CharSequence mLastSyncedClipboardEntry;
    private boolean mLastSyncedClipboardEntryInSecureInput;

    @VisibleForTesting
    protected interface ClipboardActionOwner {
        @NonNull
        Context getContext();

        void outputClipboardText(@NonNull CharSequence text);

        void showAllClipboardOptions();
    }

    @VisibleForTesting
    protected final ClipboardActionOwner mClipboardActionOwnerImpl =
            new ClipboardActionOwner() {
                @NonNull
                @Override
                public Context getContext() {
                    return AnySoftKeyboardClipboard.this;
                }

                @Override
                public void outputClipboardText(@NonNull CharSequence text) {
                    AnySoftKeyboardClipboard.this.onText(null, text);
                    mSuggestionClipboardEntry.setAsHint(false);
                }

                @Override
                public void showAllClipboardOptions() {
                    AnySoftKeyboardClipboard.this.showAllClipboardEntries(null);
                    mSuggestionClipboardEntry.setAsHint(false);
                }
            };

    @VisibleForTesting
    protected static class ClipboardStripActionProvider
            implements KeyboardViewContainerView.StripActionProvider {
        private final ClipboardActionOwner mOwner;
        private CharSequence mEntryText;
        private View mRootView;
        private ViewGroup mParentView;
        private TextView mClipboardText;
        private Animator mHideClipboardTextAnimator;

        ClipboardStripActionProvider(@NonNull ClipboardActionOwner owner) {
            mOwner = owner;
        }

        @Override
        public View inflateActionView(ViewGroup parent) {
            mParentView = parent;
            mRootView =
                    LayoutInflater.from(mOwner.getContext())
                            .inflate(R.layout.clipboard_suggestion_action, mParentView, false);
            mHideClipboardTextAnimator =
                    AnimatorInflater.loadAnimator(
                            parent.getContext(), R.animator.clipboard_text_to_gone);
            mHideClipboardTextAnimator.addListener(
                    new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            super.onAnimationEnd(animation);
                            mClipboardText.setVisibility(View.GONE);
                        }
                    });
            mClipboardText = mRootView.findViewById(R.id.clipboard_suggestion_text);
            mRootView.setOnClickListener(
                    view -> {
                        if (mEntryText != null) mOwner.outputClipboardText(mEntryText);
                    });
            mRootView.setOnLongClickListener(
                    v -> {
                        mOwner.showAllClipboardOptions();
                        return true;
                    });

            return mRootView;
        }

        @Override
        public void onRemoved() {
            mClipboardText = null;
            mRootView = null;
        }

        boolean isVisible() {
            return mRootView != null;
        }

        boolean isFullyVisible() {
            return mClipboardText != null && mClipboardText.getVisibility() == View.VISIBLE;
        }

        void setAsHint(boolean now) {
            if (now) {
                mClipboardText.setVisibility(View.GONE);
            } else if (mClipboardText.getVisibility() != View.GONE
                    && !mHideClipboardTextAnimator.isStarted()) {
                mClipboardText.setPivotX(mClipboardText.getWidth());
                mClipboardText.setPivotY(mClipboardText.getHeight() / 2f);<commit_msg>Fix for crash when clipboard view is removed before animation ends<commit_after>package com.anysoftkeyboard.ime;

import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.DialogInterface;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import com.anysoftkeyboard.api.KeyCodes;
import com.anysoftkeyboard.devicespecific.Clipboard;
import com.anysoftkeyboard.keyboards.Keyboard;
import com.anysoftkeyboard.keyboards.views.KeyboardViewContainerView;
import com.anysoftkeyboard.rx.GenericOnError;
import com.menny.android.anysoftkeyboard.AnyApplication;
import com.menny.android.anysoftkeyboard.R;
import java.util.ArrayList;
import java.util.List;
import net.evendanan.pixel.GeneralDialogController;

public abstract class AnySoftKeyboardClipboard extends AnySoftKeyboardSwipeListener {

    private boolean mArrowSelectionState;
    private Clipboard mClipboard;
    protected static final int MAX_CHARS_PER_CODE_POINT = 2;
    private static final long MAX_TIME_TO_SHOW_SYNCED_CLIPBOARD_ENTRY = 15 * 1000;
    private static final long MAX_TIME_TO_SHOW_SYNCED_CLIPBOARD_HINT = 120 * 1000;
    private long mLastSyncedClipboardEntryTime = Long.MIN_VALUE;
    @Nullable private CharSequence mLastSyncedClipboardEntry;
    private boolean mLastSyncedClipboardEntryInSecureInput;

    @VisibleForTesting
    protected interface ClipboardActionOwner {
        @NonNull
        Context getContext();

        void outputClipboardText(@NonNull CharSequence text);

        void showAllClipboardOptions();
    }

    @VisibleForTesting
    protected final ClipboardActionOwner mClipboardActionOwnerImpl =
            new ClipboardActionOwner() {
                @NonNull
                @Override
                public Context getContext() {
                    return AnySoftKeyboardClipboard.this;
                }

                @Override
                public void outputClipboardText(@NonNull CharSequence text) {
                    AnySoftKeyboardClipboard.this.onText(null, text);
                    mSuggestionClipboardEntry.setAsHint(false);
                }

                @Override
                public void showAllClipboardOptions() {
                    AnySoftKeyboardClipboard.this.showAllClipboardEntries(null);
                    mSuggestionClipboardEntry.setAsHint(false);
                }
            };

    @VisibleForTesting
    protected static class ClipboardStripActionProvider
            implements KeyboardViewContainerView.StripActionProvider {
        private final ClipboardActionOwner mOwner;
        private CharSequence mEntryText;
        private View mRootView;
        private ViewGroup mParentView;
        private TextView mClipboardText;
        private Animator mHideClipboardTextAnimator;

        ClipboardStripActionProvider(@NonNull ClipboardActionOwner owner) {
            mOwner = owner;
        }

        @Override
        public View inflateActionView(ViewGroup parent) {
            mParentView = parent;
            mRootView =
                    LayoutInflater.from(mOwner.getContext())
                            .inflate(R.layout.clipboard_suggestion_action, mParentView, false);
            mHideClipboardTextAnimator =
                    AnimatorInflater.loadAnimator(
                            parent.getContext(), R.animator.clipboard_text_to_gone);
            mClipboardText = mRootView.findViewById(R.id.clipboard_suggestion_text);
            mHideClipboardTextAnimator.addListener(
                    new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            super.onAnimationEnd(animation);
                            final TextView textView = mClipboardText;
                            if (textView != null) {
                                textView.setVisibility(View.GONE);
                            }
                        }
                    });
            mRootView.setOnClickListener(
                    view -> {
                        if (mEntryText != null) mOwner.outputClipboardText(mEntryText);
                    });
            mRootView.setOnLongClickListener(
                    v -> {
                        mOwner.showAllClipboardOptions();
                        return true;
                    });

            return mRootView;
        }

        @Override
        public void onRemoved() {
            if (mHideClipboardTextAnimator != null) mHideClipboardTextAnimator.cancel();
            mClipboardText = null;
            mRootView = null;
        }

        boolean isVisible() {
            return mRootView != null;
        }

        boolean isFullyVisible() {
            return mClipboardText != null && mClipboardText.getVisibility() == View.VISIBLE;
        }

        void setAsHint(boolean now) {
            if (now) {
                mClipboardText.setVisibility(View.GONE);
            } else if (mClipboardText.getVisibility() != View.GONE
                    && !mHideClipboardTextAnimator.isStarted()) {
                mClipboardText.setPivotX(mClipboardText.getWidth());
                mClipboardText.setPivotY(mClipboardText.getHeight() / 2f);
{'fraction_non_alphanumeric': 0.06613756613756613, 'fraction_numerical': 0.0026455026455026454, 'mean_word_length': 2.308343057176196, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '421030', 'n_tokens_mistral': 2930, 'n_tokens_neox': 2808, 'n_words': 526}