<commit_before>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugin.platform;

import static android.view.MotionEvent.PointerCoords;
import static android.view.MotionEvent.PointerProperties;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.annotation.UiThread;
import android.util.DisplayMetrics;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.systemchannels.PlatformViewsChannel;
import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.view.AccessibilityBridge;
import io.flutter.view.TextureRegistry;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Manages platform views.
 * <p>
 * Each {@link io.flutter.app.FlutterPluginRegistry} has a single platform views controller.
 * A platform views controller can be attached to at most one Flutter view.
 */
public class PlatformViewsController implements PlatformViewsAccessibilityDelegate {
    private static final String TAG = "PlatformViewsController";

    // API level 20 is required for VirtualDisplay#setSurface which we use when resizing a platform view.
    private static final int MINIMAL_SDK = Build.VERSION_CODES.KITKAT_WATCH;

    private final PlatformViewRegistryImpl registry;

    // The context of the Activity or Fragment hosting the render target for the Flutter engine.
    private Context context;

    // The texture registry maintaining the textures into which the embedded views will be rendered.
    private TextureRegistry textureRegistry;

    private TextInputPlugin textInputPlugin;

    // The system channel used to communicate with the framework about platform views.
    private PlatformViewsChannel platformViewsChannel;

    // The accessibility bridge to which accessibility events form the platform views will be dispatched.
    private final AccessibilityEventsDelegate accessibilityEventsDelegate;

    private final HashMap<Integer, VirtualDisplayController> vdControllers;

    // Maps a virtual display's context to the platform view hosted in this virtual display.
    // Since each virtual display has it's unique context this allows associating any view with the platform view that
    // it is associated with(e.g if a platform view creates other views in the same virtual display.
    private final HashMap<Context, View> contextToPlatformView;

    private final PlatformViewsChannel.PlatformViewsHandler channelHandler = new PlatformViewsChannel.PlatformViewsHandler() {
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        @Override
        public long createPlatformView(@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
            ensureValidAndroidVersion();

            if (!validateDirection(request.direction)) {
                throw new IllegalStateException("Trying to create a view with unknown direction value: "
                    + request.direction + "(view id: " + request.viewId + ")");
            }

            if (vdControllers.containsKey(request.viewId)) {
                throw new IllegalStateException("Trying to create an already created platform view, view id: "
                    + request.viewId);
            }

            PlatformViewFactory viewFactory = registry.getFactory(request.viewType);
            if (viewFactory == null) {
                throw new IllegalStateException("Trying to create a platform view of unregistered type: "
                    + request.viewType);
            }

            Object createParams = null;
            if (request.params != null) {
                createParams = viewFactory.getCreateArgsCodec().decodeMessage(request.params);
            }

            int physicalWidth = toPhysicalPixels(request.logicalWidth);
            int physicalHeight = toPhysicalPixels(request.logicalHeight);
            validateVirtualDisplayDimensions(physicalWidth, physicalHeight);

            TextureRegistry.SurfaceTextureEntry textureEntry = textureRegistry.createSurfaceTexture();
            VirtualDisplayController vdController = VirtualDisplayController.create(
                    context,
                    accessibilityEventsDelegate,
                    viewFactory,
                    textureEntry,
                    physicalWidth,
                    physicalHeight,
                    request.viewId,
                    createParams,
                    (view, hasFocus) -> {
                        if (hasFocus) {
                            platformViewsChannel.invokeViewFocused(request.viewId);
                        }
                    }
            );

            if (vdController == null) {
                throw new IllegalStateException("Failed creating virtual display for a "
                    + request.viewType + " with id: " + request.viewId);
            }

            vdControllers.put(request.viewId, vdController);
            View platformView = vdController.getView();
            platformView.setLayoutDirection(request.direction);
            contextToPlatformView.put(platformView.getContext(), platformView);

            // TODO(amirh): copy accessibility nodes to the FlutterView's accessibility tree.

            return textureEntry.id();
        }

        @Override
        public void disposePlatformView(int viewId) {
            ensureValidAndroidVersion();

            VirtualDisplayController vdController = vdControllers.get(viewId);
            if (vdController == null) {
                throw new IllegalStateException("Trying to dispose a platform view with unknown id: "
                    + viewId);
            }

            if (textInputPlugin != null) {
                textInputPlugin.clearPlatformViewClient(viewId);
            }

            contextToPlatformView.remove(vdController.getView().getContext());
            vdController.dispose();
            vdControllers.remove(viewId);
        }

        @Override
        public void resizePlatformView(@NonNull PlatformViewsChannel.PlatformViewResizeRequest request, @NonNull Runnable onComplete) {
            ensureValidAndroidVersion();

            final VirtualDisplayController vdController = vdControllers.get(request.viewId);
            if (vdController == null) {
                throw new IllegalStateException("Trying to resize a platform view with unknown id: "
                    + request.viewId);
            }

            int physicalWidth = toPhysicalPixels(request.newLogicalWidth);
            int physicalHeight = toPhysicalPixels(request.newLogicalHeight);
            validateVirtualDisplayDimensions(physicalWidth, physicalHeight);

            // Resizing involved moving the platform view to a new virtual display. Doing so
            // potentially results in losing an active input connection. To make sure we preserve
            // the input connection when resizing we lock it here and unlock after the resize is
            // complete.
            lockInputConnection(vdController);
            vdController.resize(
                    physicalWidth,
                    physicalHeight,
                    new Runnable() {
                        @Override
                        public void run() {
                            unlockInputConnection(vdController);
                            onComplete.run();
                        }
                    }
            );
        }

        @Override
        public void onTouch(@NonNull PlatformViewsChannel.PlatformViewTouch touch) {
            ensureValidAndroidVersion();

            float density = context.getResources().getDisplayMetrics().density;
            PointerProperties[] pointerProperties =
                parsePointerPropertiesList(touch.rawPointerPropertiesList)
                    .toArray(new PointerProperties[touch.pointerCount]);
            PointerCoords[] pointerCoords =
                parsePointerCoordsList(touch.rawPointerCoords, density)
                    .toArray(new PointerCoords[touch.pointerCount]);

            if (!vdControllers.containsKey(touch.viewId)) {
                throw new IllegalStateException("Sending touch to an unknown view with id: " + touch.viewId);
            }
            View view = vdControllers.get(touch.viewId).getView();

            MotionEvent event = MotionEvent.obtain(
                touch.downTime.longValue(),
                touch.eventTime.longValue(),
                touch.action,
                touch.pointerCount,
                pointerProperties,
                pointerCoords,
                touch.metaState,
                touch.buttonState,
                touch.xPrecision,
                touch.yPrecision,
                touch.deviceId,
                touch.edgeFlags,
                touch.source,
                touch.flags
            );

            view.dispatchTouchEvent(event);
        }

        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        @Override
        public void setDirection(int viewId, int direction) {
            ensureValidAndroidVersion();

            if (!validateDirection(direction)) {
                throw new IllegalStateException("Trying to set unknown direction value: " + direction
                    + "(view id: " + viewId + ")");
            }

            View view = vdControllers.get(viewId).getView();
            if (view == null) {
                throw new IllegalStateException("Sending touch to an unknown view with id: "
                    + direction);
            }

            view.setLayoutDirection(direction);
        }

        @Override
        public void clearFocus(int viewId) {
            View view = vdControllers.get(viewId).getView();
            view.clearFocus();
        }

        private void ensureValidAndroidVersion() {
            if (Build.VERSION.SDK_INT < MINIMAL_SDK) {
                Log.e(TAG, "Trying to use platform views with API " + Build.VERSION.SDK_INT
                    + ", required API level is: " + MINIMAL_SDK);
                throw new IllegalStateException("An attempt was made to use platform views on a"
                    + " version of Android that platform views does not support.");
            }<commit_msg>Write MINIMAL_SDK to exception message (#11345)<commit_after>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugin.platform;

import static android.view.MotionEvent.PointerCoords;
import static android.view.MotionEvent.PointerProperties;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.annotation.UiThread;
import android.util.DisplayMetrics;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.systemchannels.PlatformViewsChannel;
import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.view.AccessibilityBridge;
import io.flutter.view.TextureRegistry;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Manages platform views.
 * <p>
 * Each {@link io.flutter.app.FlutterPluginRegistry} has a single platform views controller.
 * A platform views controller can be attached to at most one Flutter view.
 */
public class PlatformViewsController implements PlatformViewsAccessibilityDelegate {
    private static final String TAG = "PlatformViewsController";

    // API level 20 is required for VirtualDisplay#setSurface which we use when resizing a platform view.
    private static final int MINIMAL_SDK = Build.VERSION_CODES.KITKAT_WATCH;

    private final PlatformViewRegistryImpl registry;

    // The context of the Activity or Fragment hosting the render target for the Flutter engine.
    private Context context;

    // The texture registry maintaining the textures into which the embedded views will be rendered.
    private TextureRegistry textureRegistry;

    private TextInputPlugin textInputPlugin;

    // The system channel used to communicate with the framework about platform views.
    private PlatformViewsChannel platformViewsChannel;

    // The accessibility bridge to which accessibility events form the platform views will be dispatched.
    private final AccessibilityEventsDelegate accessibilityEventsDelegate;

    private final HashMap<Integer, VirtualDisplayController> vdControllers;

    // Maps a virtual display's context to the platform view hosted in this virtual display.
    // Since each virtual display has it's unique context this allows associating any view with the platform view that
    // it is associated with(e.g if a platform view creates other views in the same virtual display.
    private final HashMap<Context, View> contextToPlatformView;

    private final PlatformViewsChannel.PlatformViewsHandler channelHandler = new PlatformViewsChannel.PlatformViewsHandler() {
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        @Override
        public long createPlatformView(@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
            ensureValidAndroidVersion();

            if (!validateDirection(request.direction)) {
                throw new IllegalStateException("Trying to create a view with unknown direction value: "
                    + request.direction + "(view id: " + request.viewId + ")");
            }

            if (vdControllers.containsKey(request.viewId)) {
                throw new IllegalStateException("Trying to create an already created platform view, view id: "
                    + request.viewId);
            }

            PlatformViewFactory viewFactory = registry.getFactory(request.viewType);
            if (viewFactory == null) {
                throw new IllegalStateException("Trying to create a platform view of unregistered type: "
                    + request.viewType);
            }

            Object createParams = null;
            if (request.params != null) {
                createParams = viewFactory.getCreateArgsCodec().decodeMessage(request.params);
            }

            int physicalWidth = toPhysicalPixels(request.logicalWidth);
            int physicalHeight = toPhysicalPixels(request.logicalHeight);
            validateVirtualDisplayDimensions(physicalWidth, physicalHeight);

            TextureRegistry.SurfaceTextureEntry textureEntry = textureRegistry.createSurfaceTexture();
            VirtualDisplayController vdController = VirtualDisplayController.create(
                    context,
                    accessibilityEventsDelegate,
                    viewFactory,
                    textureEntry,
                    physicalWidth,
                    physicalHeight,
                    request.viewId,
                    createParams,
                    (view, hasFocus) -> {
                        if (hasFocus) {
                            platformViewsChannel.invokeViewFocused(request.viewId);
                        }
                    }
            );

            if (vdController == null) {
                throw new IllegalStateException("Failed creating virtual display for a "
                    + request.viewType + " with id: " + request.viewId);
            }

            vdControllers.put(request.viewId, vdController);
            View platformView = vdController.getView();
            platformView.setLayoutDirection(request.direction);
            contextToPlatformView.put(platformView.getContext(), platformView);

            // TODO(amirh): copy accessibility nodes to the FlutterView's accessibility tree.

            return textureEntry.id();
        }

        @Override
        public void disposePlatformView(int viewId) {
            ensureValidAndroidVersion();

            VirtualDisplayController vdController = vdControllers.get(viewId);
            if (vdController == null) {
                throw new IllegalStateException("Trying to dispose a platform view with unknown id: "
                    + viewId);
            }

            if (textInputPlugin != null) {
                textInputPlugin.clearPlatformViewClient(viewId);
            }

            contextToPlatformView.remove(vdController.getView().getContext());
            vdController.dispose();
            vdControllers.remove(viewId);
        }

        @Override
        public void resizePlatformView(@NonNull PlatformViewsChannel.PlatformViewResizeRequest request, @NonNull Runnable onComplete) {
            ensureValidAndroidVersion();

            final VirtualDisplayController vdController = vdControllers.get(request.viewId);
            if (vdController == null) {
                throw new IllegalStateException("Trying to resize a platform view with unknown id: "
                    + request.viewId);
            }

            int physicalWidth = toPhysicalPixels(request.newLogicalWidth);
            int physicalHeight = toPhysicalPixels(request.newLogicalHeight);
            validateVirtualDisplayDimensions(physicalWidth, physicalHeight);

            // Resizing involved moving the platform view to a new virtual display. Doing so
            // potentially results in losing an active input connection. To make sure we preserve
            // the input connection when resizing we lock it here and unlock after the resize is
            // complete.
            lockInputConnection(vdController);
            vdController.resize(
                    physicalWidth,
                    physicalHeight,
                    new Runnable() {
                        @Override
                        public void run() {
                            unlockInputConnection(vdController);
                            onComplete.run();
                        }
                    }
            );
        }

        @Override
        public void onTouch(@NonNull PlatformViewsChannel.PlatformViewTouch touch) {
            ensureValidAndroidVersion();

            float density = context.getResources().getDisplayMetrics().density;
            PointerProperties[] pointerProperties =
                parsePointerPropertiesList(touch.rawPointerPropertiesList)
                    .toArray(new PointerProperties[touch.pointerCount]);
            PointerCoords[] pointerCoords =
                parsePointerCoordsList(touch.rawPointerCoords, density)
                    .toArray(new PointerCoords[touch.pointerCount]);

            if (!vdControllers.containsKey(touch.viewId)) {
                throw new IllegalStateException("Sending touch to an unknown view with id: " + touch.viewId);
            }
            View view = vdControllers.get(touch.viewId).getView();

            MotionEvent event = MotionEvent.obtain(
                touch.downTime.longValue(),
                touch.eventTime.longValue(),
                touch.action,
                touch.pointerCount,
                pointerProperties,
                pointerCoords,
                touch.metaState,
                touch.buttonState,
                touch.xPrecision,
                touch.yPrecision,
                touch.deviceId,
                touch.edgeFlags,
                touch.source,
                touch.flags
            );

            view.dispatchTouchEvent(event);
        }

        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        @Override
        public void setDirection(int viewId, int direction) {
            ensureValidAndroidVersion();

            if (!validateDirection(direction)) {
                throw new IllegalStateException("Trying to set unknown direction value: " + direction
                    + "(view id: " + viewId + ")");
            }

            View view = vdControllers.get(viewId).getView();
            if (view == null) {
                throw new IllegalStateException("Sending touch to an unknown view with id: "
                    + direction);
            }

            view.setLayoutDirection(direction);
        }

        @Override
        public void clearFocus(int viewId) {
            View view = vdControllers.get(viewId).getView();
            view.clearFocus();
        }

        private void ensureValidAndroidVersion() {
            if (Build.VERSION.SDK_INT < MINIMAL_SDK) {
                throw new IllegalStateException("Trying to use platform views with API "
                    + Build.VERSION.SDK_INT + ", required API level is: " + MINIMAL_SDK);
            }
{'fraction_non_alphanumeric': 0.06963788300835655, 'fraction_numerical': 0.0010085486504658534, 'mean_word_length': 2.1578707916287536, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, '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': '3439506', 'n_tokens_mistral': 4866, 'n_tokens_neox': 4598, 'n_words': 1309}