content: /*
   * Copyright (C) 2008 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.inputmethodservice;
  
  import android.content.Context;
  import android.util.AttributeSet;
  import android.view.inputmethod.ExtractedText;
  import android.view.inputmethod.InputMethodManager;
  import android.widget.EditText;
  
  /***
   * Specialization of {@link EditText} for showing and interacting with the
   * extracted text in a full-screen input method.
   */
  public class ExtractEditText extends EditText {
      private InputMethodService mIME;
      private int mSettingExtractedText;
  
      public ExtractEditText(Context context) {
          super(context, null);
      }
  
      public ExtractEditText(Context context, AttributeSet attrs) {
          super(context, attrs, com.android.internal.R.attr.editTextStyle);
      }
  
      public ExtractEditText(Context context, AttributeSet attrs, int defStyleAttr) {
          this(context, attrs, defStyleAttr, 0);
      }
  
      public ExtractEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
          super(context, attrs, defStyleAttr, defStyleRes);
      }
  
      void setIME(InputMethodService ime) {
          mIME = ime;
      }
  
      /**
       * Start making changes that will not be reported to the client.  That
       * is, {@link #onSelectionChanged(int, int)} will not result in sending
       * the new selection to the client
       */
      public void startInternalChanges() {
          mSettingExtractedText += 1;
      }
  
      /**
       * Finish making changes that will not be reported to the client.  That
       * is, {@link #onSelectionChanged(int, int)} will not result in sending
       * the new selection to the client
       */
      public void finishInternalChanges() {
          mSettingExtractedText -= 1;
      }
  
      /**
       * Implement just to keep track of when we are setting text from the
       * client (vs. seeing changes in ourself from the user).
       */
      @Override public void setExtractedText(ExtractedText text) {
          try {
              mSettingExtractedText++;
              super.setExtractedText(text);
          } finally {
              mSettingExtractedText--;
          }
      }
  
      /**
       * Report to the underlying text editor about selection changes.
       */
      @Override protected void onSelectionChanged(int selStart, int selEnd) {
          if (mSettingExtractedText == 0 && mIME != null && selStart >= 0 && selEnd >= 0) {
              mIME.onExtractedSelectionChanged(selStart, selEnd);
          }
      }
  
      /**
       * Redirect clicks to the IME for handling there.  First allows any
       * on click handler to run, though.
       */
      @Override public boolean performClick() {
          if (!super.performClick() && mIME != null) {
              mIME.onExtractedTextClicked();
              return true;
          }
          return false;
      }
  
      @Override public boolean onTextContextMenuItem(int id) {
          // Select all and Replace text shouldn't be handled by the original edit text, but by the
          // extracted one.
          if (id == android.R.id.selectAll || id == android.R.id.replaceText) {
              return super.onTextContextMenuItem(id);
          }
          if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
              // Mode was started on Extracted, needs to be stopped here.
              // Cut will change the text, which stops selection mode.
              if (id == android.R.id.copy || id == android.R.id.paste) stopTextActionMode();
              return true;
          }
          return super.onTextContextMenuItem(id);
      }
  
      /**
       * We are always considered to be an input method target.
       */
      @Override
      public boolean isInputMethodTarget() {
          return true;
      }
  
      /**
       * Return true if the edit text is currently showing a scroll bar.
       */
      public boolean hasVerticalScrollBar() {
          return computeVerticalScrollRange() > computeVerticalScrollExtent();
      }
  
      /**
       * Pretend like the window this view is in always has focus, so its
       * highlight and cursor will be displayed.
       */
      @Override public boolean hasWindowFocus() {
          return this.isEnabled();
      }
  
      /**
       * Pretend like this view always has focus, so its
       * highlight and cursor will be displayed.
       */
      @Override public boolean isFocused() {
          return this.isEnabled();
      }
  
      /**
       * Pretend like this view always has focus, so its
       * highlight and cursor will be displayed.
       */
      @Override public boolean hasFocus() {
          return this.isEnabled();
      }
  
      /**
       * @hide
       */
      @Override protected void viewClicked(InputMethodManager imm) {
          // As an instance of this class is supposed to be owned by IMS,
          // and it has a reference to the IMS (the current IME),
          // we just need to call back its onViewClicked() here.
          // It should be good to avoid unnecessary IPCs by doing this as well.
          if (mIME != null) {
              mIME.onViewClicked(false);
          }
      }
  
      /**
       * @hide
       */
      @Override
      public boolean isInExtractedMode() {
          return true;
      }
  
      /**
       * {@inheritDoc}
       * @hide
       */
      @Override
      protected void deleteText_internal(int start, int end) {
          // Do not call the super method.
          // This will change the source TextView instead, which will update the ExtractTextView.
          mIME.onExtractedDeleteText(start, end);
      }
  
      /**
       * {@inheritDoc}
       * @hide
       */
      @Override
      protected void replaceText_internal(int start, int end, CharSequence text) {
          // Do not call the super method.
          // This will change the source TextView instead, which will update the ExtractTextView.
          mIME.onExtractedReplaceText(start, end, text);
      }
  
      /**
       * {@inheritDoc}
       * @hide
       */
      @Override
      protected void setSpan_internal(Object span, int start, int end, int flags) {
          // Do not call the super method.
          // This will change the source TextView instead, which will update the ExtractTextView.
          mIME.onExtractedSetSpan(span, start, end, flags);
      }
  
      /**
       * {@inheritDoc}
       * @hide
       */
      @Override
      protected void setCursorPosition_internal(int start, int end) {
          // Do not call the super method.
          // This will change the source TextView instead, which will update the ExtractTextView.
          mIME.onExtractedSelectionChanged(start, end);
      }
  }
  