| // Copyright 2014 The Chromium 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 org.chromium.content.browser.input; |
| |
| import android.content.Context; |
| import android.content.res.TypedArray; |
| import android.graphics.Rect; |
| import android.util.TypedValue; |
| import android.view.Gravity; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.view.ViewGroup; |
| import android.view.ViewGroup.LayoutParams; |
| import android.widget.PopupWindow; |
| |
| /** |
| * Paste popup implementation based on TextView.PastePopupMenu. |
| */ |
| public class LegacyPastePopupMenu implements OnClickListener, PastePopupMenu { |
| private final View mParent; |
| private final PastePopupMenuDelegate mDelegate; |
| private final Context mContext; |
| private final PopupWindow mContainer; |
| private int mRawPositionX; |
| private int mRawPositionY; |
| private int mStatusBarHeight; |
| private View mPasteView; |
| private final int mPasteViewLayout; |
| private final int mLineOffsetY; |
| private final int mWidthOffsetX; |
| |
| public LegacyPastePopupMenu( |
| Context context, View parent, final PastePopupMenuDelegate delegate) { |
| mParent = parent; |
| mDelegate = delegate; |
| mContext = context; |
| mContainer = new PopupWindow(mContext, null, android.R.attr.textSelectHandleWindowStyle); |
| mContainer.setSplitTouchEnabled(true); |
| mContainer.setClippingEnabled(false); |
| mContainer.setAnimationStyle(0); |
| |
| mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); |
| mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); |
| |
| final int[] popupLayoutAttrs = { |
| android.R.attr.textEditPasteWindowLayout, |
| }; |
| |
| TypedArray attrs = mContext.getTheme().obtainStyledAttributes(popupLayoutAttrs); |
| mPasteViewLayout = attrs.getResourceId(attrs.getIndex(0), 0); |
| |
| attrs.recycle(); |
| |
| // Convert line offset dips to pixels. |
| mLineOffsetY = (int) TypedValue.applyDimension( |
| TypedValue.COMPLEX_UNIT_DIP, 5.0f, mContext.getResources().getDisplayMetrics()); |
| mWidthOffsetX = (int) TypedValue.applyDimension( |
| TypedValue.COMPLEX_UNIT_DIP, 30.0f, mContext.getResources().getDisplayMetrics()); |
| |
| final int statusBarHeightResourceId = |
| mContext.getResources().getIdentifier("status_bar_height", "dimen", "android"); |
| if (statusBarHeightResourceId > 0) { |
| mStatusBarHeight = |
| mContext.getResources().getDimensionPixelSize(statusBarHeightResourceId); |
| } |
| } |
| |
| @Override |
| public void show(Rect selectionRect) { |
| hide(); |
| updateContent(); |
| positionAt(selectionRect.left, selectionRect.bottom); |
| } |
| |
| @Override |
| public void hide() { |
| mContainer.dismiss(); |
| } |
| |
| @Override |
| public void onClick(View v) { |
| paste(); |
| hide(); |
| } |
| |
| private void positionAt(int x, int y) { |
| if (mRawPositionX == x && mRawPositionY == y) return; |
| mRawPositionX = x; |
| mRawPositionY = y; |
| |
| final View contentView = mContainer.getContentView(); |
| final int width = contentView.getMeasuredWidth(); |
| final int height = contentView.getMeasuredHeight(); |
| |
| int positionX = (int) (x - width / 2.0f); |
| int positionY = y - height - mLineOffsetY; |
| |
| int minOffsetY = 0; |
| if (mParent.getSystemUiVisibility() == View.SYSTEM_UI_FLAG_VISIBLE) { |
| minOffsetY = mStatusBarHeight; |
| } |
| |
| final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels; |
| if (positionY < minOffsetY) { |
| // Vertical clipping, move under edited line and to the side of insertion cursor |
| // TODO bottom clipping in case there is no system bar |
| positionY += height; |
| positionY += mLineOffsetY; |
| |
| // Move to right hand side of insertion cursor by default. TODO RTL text. |
| final int handleHalfWidth = mWidthOffsetX / 2; |
| |
| if (x + width < screenWidth) positionX += handleHalfWidth + width / 2; |
| else positionX -= handleHalfWidth + width / 2; |
| } else { |
| // Horizontal clipping |
| positionX = Math.max(0, positionX); |
| positionX = Math.min(screenWidth - width, positionX); |
| } |
| |
| // Offseting with location in window. |
| final int[] coords = new int[2]; |
| mParent.getLocationInWindow(coords); |
| positionX += coords[0]; |
| positionY += coords[1]; |
| |
| mContainer.showAtLocation(mParent, Gravity.NO_GRAVITY, positionX, positionY); |
| } |
| |
| private void updateContent() { |
| if (mPasteView == null) { |
| final LayoutInflater inflater = |
| (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
| if (inflater != null) mPasteView = inflater.inflate(mPasteViewLayout, null); |
| |
| if (mPasteView == null) { |
| throw new IllegalArgumentException("Unable to inflate TextEdit paste window"); |
| } |
| |
| final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); |
| mPasteView.setLayoutParams(new LayoutParams( |
| ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); |
| mPasteView.measure(size, size); |
| |
| mPasteView.setOnClickListener(this); |
| } |
| mContainer.setContentView(mPasteView); |
| } |
| |
| private void paste() { |
| mDelegate.paste(); |
| } |
| } |