/* * Copyright (C) 2006 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.view; import static android.content.res.Resources.ID_NULL; import static android.os.Trace.TRACE_TAG_APP; import static android.os.Trace.TRACE_TAG_VIEW; import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION; import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH; import static android.view.Surface.FRAME_RATE_CATEGORY_LOW; import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL; import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE; import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN; import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH; import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE; import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API; import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY; import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API; import static android.view.flags.Flags.enableUseMeasureCacheDuringForceLayout; import static android.view.flags.Flags.sensitiveContentAppProtection; import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly; import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly; import static android.view.flags.Flags.toolkitFrameRateSmallUsesPercentReadOnly; import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly; import static android.view.flags.Flags.toolkitFrameRateViewEnablingReadOnly; import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision; import static android.view.flags.Flags.toolkitSetFrameRateReadOnly; import static android.view.flags.Flags.viewVelocityApi; import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR; import static android.view.inputmethod.Flags.initiationWithoutInputConnection; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; import static com.android.window.flags.Flags.FLAG_DELEGATE_UNHANDLED_DRAGS; import static java.lang.Math.max; import android.animation.AnimatorInflater; import android.animation.StateListAnimator; import android.annotation.AttrRes; import android.annotation.CallSuper; import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IdRes; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.LayoutRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.Size; import android.annotation.StyleRes; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UiContext; import android.annotation.UiThread; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.AutofillOptions; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentSender; import android.content.res.ColorStateList; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.credentials.CredentialManager; import android.credentials.CredentialOption; import android.credentials.GetCredentialException; import android.credentials.GetCredentialRequest; import android.credentials.GetCredentialResponse; import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Insets; import android.graphics.Interpolator; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.RenderEffect; import android.graphics.RenderNode; import android.graphics.Shader; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.hardware.display.DisplayManagerGlobal; import android.hardware.input.InputManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.OutcomeReceiver; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; import android.os.Vibrator; import android.os.vibrator.Flags; import android.service.credentials.CredentialProviderService; import android.sysprop.DisplayProperties; import android.text.InputType; import android.text.TextUtils; import android.util.ArraySet; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.FloatProperty; import android.util.LayoutDirection; import android.util.Log; import android.util.LongSparseArray; import android.util.LongSparseLongArray; import android.util.Pair; import android.util.Pools.SynchronizedPool; import android.util.Property; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.StateSet; import android.util.SuperNotCalledException; import android.util.TimeUtils; import android.util.TypedValue; import android.view.AccessibilityIterators.CharacterTextSegmentIterator; import android.view.AccessibilityIterators.ParagraphTextSegmentIterator; import android.view.AccessibilityIterators.TextSegmentIterator; import android.view.AccessibilityIterators.WordTextSegmentIterator; import android.view.ContextMenu.ContextMenuInfo; import android.view.InputDevice.InputSourceClass; import android.view.Window.OnContentApplyWindowInsetsListener; import android.view.WindowInsets.Type; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeIdManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityWindowInfo; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Transformation; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureSession; import android.view.displayhash.DisplayHash; import android.view.displayhash.DisplayHashManager; import android.view.displayhash.DisplayHashResultCallback; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.inspector.InspectableProperty; import android.view.inspector.InspectableProperty.EnumEntry; import android.view.inspector.InspectableProperty.FlagEntry; import android.view.translation.TranslationCapability; import android.view.translation.TranslationSpec.DataFormat; import android.view.translation.ViewTranslationCallback; import android.view.translation.ViewTranslationRequest; import android.view.translation.ViewTranslationResponse; import android.widget.Checkable; import android.widget.ScrollBarDrawable; import android.window.OnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.Preconditions; import com.android.internal.view.ScrollCaptureInternal; import com.android.internal.view.TooltipPopup; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.widget.ScrollBarUtils; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Predicate; /** *
* This class represents the basic building block for user interface components. A View * occupies a rectangular area on the screen and is responsible for drawing and * event handling. View is the base class for widgets, which are * used to create interactive UI components (buttons, text fields, etc.). The * {@link android.view.ViewGroup} subclass is the base class for layouts, which * are invisible containers that hold other Views (or other ViewGroups) and define * their layout properties. *
* *For information about using this class to develop your application's user interface, * read the User Interface developer guide. *
* All of the views in a window are arranged in a single tree. You can add views * either from code or by specifying a tree of views in one or more XML layout * files. There are many specialized subclasses of views that act as controls or * are capable of displaying text, images, or other content. *
** Once you have created a tree of views, there are typically a few types of * common operations you may wish to perform: *
* Note: The Android framework is responsible for measuring, laying out and * drawing views. You should not call methods that perform these actions on * views yourself unless you are actually implementing a * {@link android.view.ViewGroup}. *
* * ** To implement a custom view, you will usually begin by providing overrides for * some of the standard methods that the framework calls on all views. You do * not need to override all of these methods. In fact, you can start by just * overriding {@link #onDraw(android.graphics.Canvas)}. *
Category | Methods | Description |
---|---|---|
Creation | *Constructors | *There is a form of the constructor that are called when the view * is created from code and a form that is called when the view is * inflated from a layout file. The second form should parse and apply * any attributes defined in the layout file. * | *
{@link #onFinishInflate()} |
* Called after a view and all of its children has been inflated * from XML. | *|
Layout | *{@link #onMeasure(int, int)} |
* Called to determine the size requirements for this view and all * of its children. * | *
{@link #onLayout(boolean, int, int, int, int)} |
* Called when this view should assign a size and position to all * of its children. * | *|
{@link #onSizeChanged(int, int, int, int)} |
* Called when the size of this view has changed. * | *|
Drawing | *{@link #onDraw(android.graphics.Canvas)} |
* Called when the view should render its content. * | *
Event processing | *{@link #onKeyDown(int, KeyEvent)} |
* Called when a new hardware key event occurs. * | *
{@link #onKeyUp(int, KeyEvent)} |
* Called when a hardware key up event occurs. * | *|
{@link #onTrackballEvent(MotionEvent)} |
* Called when a trackball motion event occurs. * | *|
{@link #onTouchEvent(MotionEvent)} |
* Called when a touch screen motion event occurs. * | *|
{@link #onGenericMotionEvent(MotionEvent)} |
* Called when a generic motion event occurs. * | *|
{@link #onHoverEvent(MotionEvent)} |
* Called when a hover motion event occurs. * | *|
Focus | *{@link #onFocusChanged(boolean, int, android.graphics.Rect)} |
* Called when the view gains or loses focus. * | *
{@link #onWindowFocusChanged(boolean)} |
* Called when the window containing the view gains or loses focus. * | *|
Attaching | *{@link #onAttachedToWindow()} |
* Called when the view is attached to a window. * | *
{@link #onDetachedFromWindow} |
* Called when the view is detached from its window. * | *|
{@link #onWindowVisibilityChanged(int)} |
* Called when the visibility of the window containing the view * has changed. * | *
* <Button * android:id="@+id/my_button" * android:layout_width="wrap_content" * android:layout_height="wrap_content" * android:text="@string/my_button_text"/> *
* Button myButton = findViewById(R.id.my_button); *
* View IDs need not be unique throughout the tree, but it is good practice to * ensure that they are at least unique within the part of the tree you are * searching. *
* * ** The geometry of a view is that of a rectangle. A view has a location, * expressed as a pair of left and top coordinates, and * two dimensions, expressed as a width and a height. The unit for location * and dimensions is the pixel. *
* ** It is possible to retrieve the location of a view by invoking the methods * {@link #getLeft()} and {@link #getTop()}. The former returns the left, or X, * coordinate of the rectangle representing the view. The latter returns the * top, or Y, coordinate of the rectangle representing the view. These methods * both return the location of the view relative to its parent. For instance, * when getLeft() returns 20, that means the view is located 20 pixels to the * right of the left edge of its direct parent. *
* *
* In addition, several convenience methods are offered to avoid unnecessary
* computations, namely {@link #getRight()} and {@link #getBottom()}.
* These methods return the coordinates of the right and bottom edges of the
* rectangle representing the view. For instance, calling {@link #getRight()}
* is similar to the following computation: getLeft() + getWidth()
* (see Size for more information about the width.)
*
* The size of a view is expressed with a width and a height. A view actually * possess two pairs of width and height values. *
* ** The first pair is known as measured width and * measured height. These dimensions define how big a view wants to be * within its parent (see Layout for more details.) The * measured dimensions can be obtained by calling {@link #getMeasuredWidth()} * and {@link #getMeasuredHeight()}. *
* ** The second pair is simply known as width and height, or * sometimes drawing width and drawing height. These * dimensions define the actual size of the view on screen, at drawing time and * after layout. These values may, but do not have to, be different from the * measured width and height. The width and height can be obtained by calling * {@link #getWidth()} and {@link #getHeight()}. *
* ** To measure its dimensions, a view takes into account its padding. The padding * is expressed in pixels for the left, top, right and bottom parts of the view. * Padding can be used to offset the content of the view by a specific amount of * pixels. For instance, a left padding of 2 will push the view's content by * 2 pixels to the right of the left edge. Padding can be set using the * {@link #setPadding(int, int, int, int)} or {@link #setPaddingRelative(int, int, int, int)} * method and queried by calling {@link #getPaddingLeft()}, {@link #getPaddingTop()}, * {@link #getPaddingRight()}, {@link #getPaddingBottom()}, {@link #getPaddingStart()}, * {@link #getPaddingEnd()}. *
* ** Even though a view can define a padding, it does not provide any support for * margins. However, view groups provide such a support. Refer to * {@link android.view.ViewGroup} and * {@link android.view.ViewGroup.MarginLayoutParams} for further information. *
* * ** Layout is a two pass process: a measure pass and a layout pass. The measuring * pass is implemented in {@link #measure(int, int)} and is a top-down traversal * of the view tree. Each view pushes dimension specifications down the tree * during the recursion. At the end of the measure pass, every view has stored * its measurements. The second pass happens in * {@link #layout(int,int,int,int)} and is also top-down. During * this pass each parent is responsible for positioning all of its children * using the sizes computed in the measure pass. *
* ** When a view's measure() method returns, its {@link #getMeasuredWidth()} and * {@link #getMeasuredHeight()} values must be set, along with those for all of * that view's descendants. A view's measured width and measured height values * must respect the constraints imposed by the view's parents. This guarantees * that at the end of the measure pass, all parents accept all of their * children's measurements. A parent view may call measure() more than once on * its children. For example, the parent may measure each child once with * unspecified dimensions to find out how big they want to be, then call * measure() on them again with actual numbers if the sum of all the children's * unconstrained sizes is too big or too small. *
* ** The measure pass uses two classes to communicate dimensions. The * {@link MeasureSpec} class is used by views to tell their parents how they * want to be measured and positioned. The base LayoutParams class just * describes how big the view wants to be for both width and height. For each * dimension, it can specify one of: *
* MeasureSpecs are used to push requirements down the tree from parent to * child. A MeasureSpec can be in one of three modes: *
* To initiate a layout, call {@link #requestLayout}. This method is typically * called by a view on itself when it believes that it can no longer fit within * its current bounds. *
* * ** Drawing is handled by walking the tree and recording the drawing commands of * any View that needs to update. After this, the drawing commands of the * entire tree are issued to screen, clipped to the newly damaged area. *
* *
* The tree is largely recorded and drawn in order, with parents drawn before
* (i.e., behind) their children, with siblings drawn in the order they appear
* in the tree. If you set a background drawable for a View, then the View will
* draw it before calling back to its onDraw()
method. The child
* drawing order can be overridden with
* {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean) custom child drawing order}
* in a ViewGroup, and with {@link #setZ(float)} custom Z values} set on Views.
*
* To force a view to draw, call {@link #invalidate()}. *
* * ** The basic cycle of a view is as follows: *
Note: The entire view tree is single threaded. You must always be on * the UI thread when calling any method on any view. * If you are doing work on other threads and want to update the state of a view * from that thread, you should use a {@link Handler}. *
* * ** The framework will handle routine focus movement in response to user input. * This includes changing the focus as views are removed or hidden, or as new * views become available. Views indicate their willingness to take focus * through the {@link #isFocusable} method. To change whether a view can take * focus, call {@link #setFocusable(boolean)}. When in touch mode (see notes below) * views indicate whether they still would like focus via {@link #isFocusableInTouchMode} * and can change this via {@link #setFocusableInTouchMode(boolean)}. *
** Focus movement is based on an algorithm which finds the nearest neighbor in a * given direction. In rare cases, the default algorithm may not match the * intended behavior of the developer. In these situations, you can provide * explicit overrides by using these XML attributes in the layout file: *
* nextFocusDown * nextFocusLeft * nextFocusRight * nextFocusUp ** * * *
* To get a particular view to take focus, call {@link #requestFocus()}. *
* * ** When a user is navigating a user interface via directional keys such as a D-pad, it is * necessary to give focus to actionable items such as buttons so the user can see * what will take input. If the device has touch capabilities, however, and the user * begins interacting with the interface by touching it, it is no longer necessary to * always highlight, or give focus to, a particular view. This motivates a mode * for interaction named 'touch mode'. *
** For a touch capable device, once the user touches the screen, the device * will enter touch mode. From this point onward, only views for which * {@link #isFocusableInTouchMode} is true will be focusable, such as text editing widgets. * Other views that are touchable, like buttons, will not take focus when touched; they will * only fire the on click listeners. *
** Any time a user hits a directional key, such as a D-pad direction, the view device will * exit touch mode, and find a view to take focus, so that the user may resume interacting * with the user interface without touching the screen again. *
** The touch mode state is maintained across {@link android.app.Activity}s. Call * {@link #isInTouchMode} to see whether the device is currently in touch mode. *
* * ** The framework provides basic support for views that wish to internally * scroll their content. This includes keeping track of the X and Y scroll * offset as well as mechanisms for drawing scrollbars. See * {@link #scrollBy(int, int)}, {@link #scrollTo(int, int)}, and * {@link #awakenScrollBars()} for more details. *
* * ** Unlike IDs, tags are not used to identify views. Tags are essentially an * extra piece of information that can be associated with a view. They are most * often used as a convenience to store data related to views in the views * themselves rather than by putting them in a separate structure. *
*
* Tags may be specified with character sequence values in layout XML as either
* a single tag using the {@link android.R.styleable#View_tag android:tag}
* attribute or multiple tags using the {@code
* <View ...
* android:tag="@string/mytag_value" />
* <View ...>
* <tag android:id="@+id/mytag"
* android:value="@string/mytag_value" />
* </View>
*
*
* Tags may also be specified with arbitrary objects from code using * {@link #setTag(Object)} or {@link #setTag(int, Object)}. *
* * ** By default, Views are created using the theme of the Context object supplied * to their constructor; however, a different theme may be specified by using * the {@link android.R.styleable#View_theme android:theme} attribute in layout * XML or by passing a {@link ContextThemeWrapper} to the constructor from * code. *
** When the {@link android.R.styleable#View_theme android:theme} attribute is * used in XML, the specified theme is applied on top of the inflation * context's theme (see {@link LayoutInflater}) and used for the view itself as * well as any child elements. *
** In the following example, both views will be created using the Material dark * color scheme; however, because an overlay theme is used which only defines a * subset of attributes, the value of * {@link android.R.styleable#Theme_colorAccent android:colorAccent} defined on * the inflation context's theme (e.g. the Activity theme) will be preserved. *
* <LinearLayout * ... * android:theme="@android:theme/ThemeOverlay.Material.Dark"> * <View ...> * </LinearLayout> ** * * *
* The View class exposes an {@link #ALPHA} property, as well as several transform-related * properties, such as {@link #TRANSLATION_X} and {@link #TRANSLATION_Y}. These properties are * available both in the {@link Property} form as well as in similarly-named setter/getter * methods (such as {@link #setAlpha(float)} for {@link #ALPHA}). These properties can * be used to set persistent state associated with these rendering-related properties on the view. * The properties and methods can also be used in conjunction with * {@link android.animation.Animator Animator}-based animations, described more in the * Animation section. *
* * ** Starting with Android 3.0, the preferred way of animating views is to use the * {@link android.animation} package APIs. These {@link android.animation.Animator Animator}-based * classes change actual properties of the View object, such as {@link #setAlpha(float) alpha} and * {@link #setTranslationX(float) translationX}. This behavior is contrasted to that of the pre-3.0 * {@link android.view.animation.Animation Animation}-based classes, which instead animate only * how the view is drawn on the display. In particular, the {@link ViewPropertyAnimator} class * makes animating these View properties particularly easy and efficient. *
** Alternatively, you can use the pre-3.0 animation classes to animate how Views are rendered. * You can attach an {@link Animation} object to a view using * {@link #setAnimation(Animation)} or * {@link #startAnimation(Animation)}. The animation can alter the scale, * rotation, translation and alpha of a view over time. If the animation is * attached to a view that has children, the animation will affect the entire * subtree rooted by that node. When an animation is started, the framework will * take care of redrawing the appropriate views until the animation completes. *
* * ** Sometimes it is essential that an application be able to verify that an action * is being performed with the full knowledge and consent of the user, such as * granting a permission request, making a purchase or clicking on an advertisement. * Unfortunately, a malicious application could try to spoof the user into * performing these actions, unaware, by concealing the intended purpose of the view. * As a remedy, the framework offers a touch filtering mechanism that can be used to * improve the security of views that provide access to sensitive functionality. *
* To enable touch filtering, call {@link #setFilterTouchesWhenObscured(boolean)} or set the * android:filterTouchesWhenObscured layout attribute to true. When enabled, the framework * will discard touches that are received whenever the view's window is obscured by * another visible window at the touched location. As a result, the view will not receive touches * whenever the touch passed through a toast, dialog or other window that appears above the view's * window. *
* For more fine-grained control over security, consider overriding the * {@link #onFilterTouchEventForSecurity(MotionEvent)} method to implement your own * security policy. See also {@link MotionEvent#FLAG_WINDOW_IS_OBSCURED}. *
* * @attr ref android.R.styleable#View_accessibilityHeading * @attr ref android.R.styleable#View_allowClickWhenDisabled * @attr ref android.R.styleable#View_alpha * @attr ref android.R.styleable#View_background * @attr ref android.R.styleable#View_clickable * @attr ref android.R.styleable#View_clipToOutline * @attr ref android.R.styleable#View_contentDescription * @attr ref android.R.styleable#View_drawingCacheQuality * @attr ref android.R.styleable#View_duplicateParentState * @attr ref android.R.styleable#View_id * @attr ref android.R.styleable#View_requiresFadingEdge * @attr ref android.R.styleable#View_fadeScrollbars * @attr ref android.R.styleable#View_fadingEdgeLength * @attr ref android.R.styleable#View_filterTouchesWhenObscured * @attr ref android.R.styleable#View_fitsSystemWindows * @attr ref android.R.styleable#View_isScrollContainer * @attr ref android.R.styleable#View_focusable * @attr ref android.R.styleable#View_focusableInTouchMode * @attr ref android.R.styleable#View_focusedByDefault * @attr ref android.R.styleable#View_hapticFeedbackEnabled * @attr ref android.R.styleable#View_keepScreenOn * @attr ref android.R.styleable#View_keyboardNavigationCluster * @attr ref android.R.styleable#View_layerType * @attr ref android.R.styleable#View_layoutDirection * @attr ref android.R.styleable#View_longClickable * @attr ref android.R.styleable#View_minHeight * @attr ref android.R.styleable#View_minWidth * @attr ref android.R.styleable#View_nextClusterForward * @attr ref android.R.styleable#View_nextFocusDown * @attr ref android.R.styleable#View_nextFocusLeft * @attr ref android.R.styleable#View_nextFocusRight * @attr ref android.R.styleable#View_nextFocusUp * @attr ref android.R.styleable#View_onClick * @attr ref android.R.styleable#View_outlineSpotShadowColor * @attr ref android.R.styleable#View_outlineAmbientShadowColor * @attr ref android.R.styleable#View_padding * @attr ref android.R.styleable#View_paddingHorizontal * @attr ref android.R.styleable#View_paddingVertical * @attr ref android.R.styleable#View_paddingBottom * @attr ref android.R.styleable#View_paddingLeft * @attr ref android.R.styleable#View_paddingRight * @attr ref android.R.styleable#View_paddingTop * @attr ref android.R.styleable#View_paddingStart * @attr ref android.R.styleable#View_paddingEnd * @attr ref android.R.styleable#View_saveEnabled * @attr ref android.R.styleable#View_rotation * @attr ref android.R.styleable#View_rotationX * @attr ref android.R.styleable#View_rotationY * @attr ref android.R.styleable#View_scaleX * @attr ref android.R.styleable#View_scaleY * @attr ref android.R.styleable#View_scrollX * @attr ref android.R.styleable#View_scrollY * @attr ref android.R.styleable#View_scrollbarSize * @attr ref android.R.styleable#View_scrollbarStyle * @attr ref android.R.styleable#View_scrollbars * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade * @attr ref android.R.styleable#View_scrollbarFadeDuration * @attr ref android.R.styleable#View_scrollbarTrackHorizontal * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * @attr ref android.R.styleable#View_scrollbarThumbVertical * @attr ref android.R.styleable#View_scrollbarTrackVertical * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack * @attr ref android.R.styleable#View_stateListAnimator * @attr ref android.R.styleable#View_transitionName * @attr ref android.R.styleable#View_soundEffectsEnabled * @attr ref android.R.styleable#View_tag * @attr ref android.R.styleable#View_textAlignment * @attr ref android.R.styleable#View_textDirection * @attr ref android.R.styleable#View_transformPivotX * @attr ref android.R.styleable#View_transformPivotY * @attr ref android.R.styleable#View_translationX * @attr ref android.R.styleable#View_translationY * @attr ref android.R.styleable#View_translationZ * @attr ref android.R.styleable#View_visibility * @attr ref android.R.styleable#View_theme * * @see android.view.ViewGroup */ @UiThread public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private static final boolean DBG = false; /** @hide */ public static boolean DEBUG_DRAW = false; /** * The logging tag used by this class with android.util.Log. */ protected static final String VIEW_LOG_TAG = "View"; /** * The logging tag used by this class when logging verbose, autofill-related messages. */ // NOTE: We cannot use android.view.autofill.Helper.sVerbose because that variable is not // set if a session is not started. private static final String AUTOFILL_LOG_TAG = "View.Autofill"; /** * The logging tag used by this class when logging content capture-related messages. */ private static final String CONTENT_CAPTURE_LOG_TAG = "View.ContentCapture"; private static final boolean DEBUG_CONTENT_CAPTURE = false; /** * When set to true, this view will save its attribute data. * * @hide */ public static boolean sDebugViewAttributes = false; /** * When set to this application package view will save its attribute data. * * @hide */ public static String sDebugViewAttributesApplicationPackage; /** * Used to mark a View that has no ID. */ public static final int NO_ID = -1; /** * Last ID that is given to Views that are no part of activities. * * {@hide} */ public static final int LAST_APP_AUTOFILL_ID = Integer.MAX_VALUE / 2; /** * Attribute to find the autofilled highlight * * @see #getAutofilledDrawable() */ private static final int[] AUTOFILL_HIGHLIGHT_ATTR = new int[]{android.R.attr.autofilledHighlight}; /** * Signals that compatibility booleans have been initialized according to * target SDK versions. */ private static boolean sCompatibilityDone = false; /** @hide */ public HapticScrollFeedbackProvider mScrollFeedbackProvider = null; /** * Ignore an optimization that skips unnecessary EXACTLY layout passes. */ private static boolean sAlwaysRemeasureExactly = false; /** * When true makes it possible to use onMeasure caches also when the force layout flag is * enabled. This helps avoiding multiple measures in the same frame with the same dimensions. */ private static boolean sUseMeasureCacheDuringForceLayoutFlagValue; /** * Allow setForeground/setBackground to be called (and ignored) on a textureview, * without throwing */ static boolean sTextureViewIgnoresDrawableSetters = false; /** * Prior to N, some ViewGroups would not convert LayoutParams properly even though both extend * MarginLayoutParams. For instance, converting LinearLayout.LayoutParams to * RelativeLayout.LayoutParams would lose margin information. This is fixed on N but target API * check is implemented for backwards compatibility. * * {@hide} */ protected static boolean sPreserveMarginParamsInLayoutParamConversion; /** * Prior to N, when drag enters into child of a view that has already received an * ACTION_DRAG_ENTERED event, the parent doesn't get a ACTION_DRAG_EXITED event. * ACTION_DRAG_LOCATION and ACTION_DROP were delivered to the parent of a view that returned * false from its event handler for these events. * Starting from N, the parent will get ACTION_DRAG_EXITED event before the child gets its * ACTION_DRAG_ENTERED. ACTION_DRAG_LOCATION and ACTION_DROP are never propagated to the parent. * sCascadedDragDrop is true for pre-N apps for backwards compatibility implementation. */ static boolean sCascadedDragDrop; /** * Prior to O, auto-focusable didn't exist and widgets such as ListView use hasFocusable * to determine things like whether or not to permit item click events. We can't break * apps that do this just because more things (clickable things) are now auto-focusable * and they would get different results, so give old behavior to old apps. */ static boolean sHasFocusableExcludeAutoFocusable; /** * Prior to O, auto-focusable didn't exist and views marked as clickable weren't implicitly * made focusable by default. As a result, apps could (incorrectly) change the clickable * setting of views off the UI thread. Now that clickable can effect the focusable state, * changing the clickable attribute off the UI thread will cause an exception (since changing * the focusable state checks). In order to prevent apps from crashing, we will handle this * specific case and just not notify parents on new focusables resulting from marking views * clickable from outside the UI thread. */ private static boolean sAutoFocusableOffUIThreadWontNotifyParents; /** * Prior to P things like setScaleX() allowed passing float values that were bogus such as * Float.NaN. If the app is targetting P or later then passing these values will result in an * exception being thrown. If the app is targetting an earlier SDK version, then we will * silently clamp these values to avoid crashes elsewhere when the rendering code hits * these bogus values. */ private static boolean sThrowOnInvalidFloatProperties; /** * Prior to P, {@code #startDragAndDrop} accepts a builder which produces an empty drag shadow. * Currently zero size SurfaceControl cannot be created thus we create a 1x1 surface instead. */ private static boolean sAcceptZeroSizeDragShadow; /** * When true, measure and layout passes of all the newly attached views will be logged with * {@link Trace}, so we can better debug jank due to complex view hierarchies. */ private static boolean sTraceLayoutSteps; /** * When not null, emits a {@link Trace} instant event and the stacktrace every time a relayout * of a class having this name happens. */ private static String sTraceRequestLayoutClass; @Nullable private ViewCredentialHandler mViewCredentialHandler; /** Used to avoid computing the full strings each time when layout tracing is enabled. */ @Nullable private ViewTraversalTracingStrings mTracingStrings; /** * Prior to R, {@link #dispatchApplyWindowInsets} had an issue: *The modified insets changed by {@link #onApplyWindowInsets} were passed to the * entire view hierarchy in prefix order, including siblings as well as siblings of parents * further down the hierarchy. This violates the basic concepts of the view hierarchy, and * thus, the hierarchical dispatching mechanism was hard to use for apps. *
* In order to make window inset dispatching work properly, we dispatch window insets * in the view hierarchy in a proper hierarchical manner if this flag is set to {@code false}. */ static boolean sBrokenInsetsDispatch; /** * Prior to Q, calling * {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)} * did not call update the window format so the opacity of the background was not correctly * applied to the window. Some applications rely on this misbehavior to work properly. *
* From Q, {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)} is * the same as {@link com.android.internal.policy.DecorView#setWindowBackground(Drawable)} * which updates the window format. * @hide */ protected static boolean sBrokenWindowBackground; /** * Prior to R, we were always forcing a layout of the entire hierarchy when insets changed from * the server. This is inefficient and not all apps use it. Instead, we want to rely on apps * calling {@link #requestLayout} when they need to relayout based on an insets change. */ static boolean sForceLayoutWhenInsetsChanged; /** @hide */ @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO}) @Retention(RetentionPolicy.SOURCE) public @interface Focusable {} /** * This view does not want keystrokes. *
* Use with {@link #setFocusable(int)} and {@code
* android:focusable}.
*/
public static final int NOT_FOCUSABLE = 0x00000000;
/**
* This view wants keystrokes.
*
* Use with {@link #setFocusable(int)} and {@code
* android:focusable}.
*/
public static final int FOCUSABLE = 0x00000001;
/**
* This view determines focusability automatically. This is the default.
*
* Use with {@link #setFocusable(int)} and {@code
* android:focusable}.
*/
public static final int FOCUSABLE_AUTO = 0x00000010;
/**
* Mask for use with setFlags indicating bits used for focus.
*/
private static final int FOCUSABLE_MASK = 0x00000011;
/**
* This view will adjust its padding to fit system windows (e.g. status bar)
*/
private static final int FITS_SYSTEM_WINDOWS = 0x00000002;
/** @hide */
@IntDef({VISIBLE, INVISIBLE, GONE})
@Retention(RetentionPolicy.SOURCE)
public @interface Visibility {}
/**
* This view is visible.
* Use with {@link #setVisibility} and {@code
* android:visibility}.
*/
public static final int VISIBLE = 0x00000000;
/**
* This view is invisible, but it still takes up space for layout purposes.
* Use with {@link #setVisibility} and {@code
* android:visibility}.
*/
public static final int INVISIBLE = 0x00000004;
/**
* This view is invisible, and it doesn't take any space for layout
* purposes. Use with {@link #setVisibility} and {@code
* android:visibility}.
*/
public static final int GONE = 0x00000008;
/**
* Mask for use with setFlags indicating bits used for visibility.
* {@hide}
*/
static final int VISIBILITY_MASK = 0x0000000C;
private static final int[] VISIBILITY_FLAGS = {VISIBLE, INVISIBLE, GONE};
/**
* Hint indicating that this view can be autofilled with an email address.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
/**
* Hint indicating that this view can be autofilled with a user's real name.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_NAME = "name";
/**
* Hint indicating that this view can be autofilled with a username.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_USERNAME = "username";
/**
* Hint indicating that this view can be autofilled with a password.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_PASSWORD = "password";
/**
* Hint indicating that this view can be autofilled with a phone number.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_PHONE = "phone";
/**
* Hint indicating that this view can be autofilled with a postal address.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
/**
* Hint indicating that this view can be autofilled with a postal code.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
/**
* Hint indicating that this view can be autofilled with a credit card number.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
/**
* Hint indicating that this view can be autofilled with a credit card security code.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
/**
* Hint indicating that this view can be autofilled with a credit card expiration date.
*
* It should be used when the credit card expiration date is represented by just one view;
* if it is represented by more than one (for example, one view for the month and another view
* for the year), then each of these views should use the hint specific for the unit
* ({@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH},
* or {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}).
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be When annotating a view with this hint, it's recommended to use a date autofill value to
* avoid ambiguity when the autofill service provides a value for it. To understand why a
* value can be ambiguous, consider "April of 2020", which could be represented as either of
* the following options:
*
* You define a date autofill value for the view by overriding the following methods:
*
* See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE =
"creditCardExpirationDate";
/**
* Hint indicating that this view can be autofilled with a credit card expiration month.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be When annotating a view with this hint, it's recommended to use a text autofill value
* whose value is the numerical representation of the month, starting on {@code 1} to avoid
* ambiguity when the autofill service provides a value for it. To understand why a
* value can be ambiguous, consider "January", which could be represented as either of
*
* Another recommended approach is to use a date autofill value - see
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE} for more details.
*
* See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH =
"creditCardExpirationMonth";
/**
* Hint indicating that this view can be autofilled with a credit card expiration year.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR =
"creditCardExpirationYear";
/**
* Hint indicating that this view can be autofilled with a credit card expiration day.
*
* Can be used with either {@link #setAutofillHints(String[])} or
* {@code android:autofillHint} (in which case the
* value should be See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
/**
* A hint indicating that this view can be autofilled with a password.
*
* This is a heuristic-based hint that is meant to be used by UI Toolkit developers when a
* view is a password field but doesn't specify a
* Typically used when the view is read-only; for example, a text label.
*
* @see #getAutofillType()
*/
public static final int AUTOFILL_TYPE_NONE = 0;
/**
* Autofill type for a text field, which is filled by a {@link CharSequence}.
*
* {@link AutofillValue} instances for autofilling a {@link View} can be obtained through
* {@link AutofillValue#forText(CharSequence)}, and the value passed to autofill a
* {@link View} can be fetched through {@link AutofillValue#getTextValue()}.
*
* @see #getAutofillType()
*/
public static final int AUTOFILL_TYPE_TEXT = 1;
/**
* Autofill type for a togglable field, which is filled by a {@code boolean}.
*
* {@link AutofillValue} instances for autofilling a {@link View} can be obtained through
* {@link AutofillValue#forToggle(boolean)}, and the value passed to autofill a
* {@link View} can be fetched through {@link AutofillValue#getToggleValue()}.
*
* @see #getAutofillType()
*/
public static final int AUTOFILL_TYPE_TOGGLE = 2;
/**
* Autofill type for a selection list field, which is filled by an {@code int}
* representing the element index inside the list (starting at {@code 0}).
*
* {@link AutofillValue} instances for autofilling a {@link View} can be obtained through
* {@link AutofillValue#forList(int)}, and the value passed to autofill a
* {@link View} can be fetched through {@link AutofillValue#getListValue()}.
*
* The available options in the selection list are typically provided by
* {@link android.app.assist.AssistStructure.ViewNode#getAutofillOptions()}.
*
* @see #getAutofillType()
*/
public static final int AUTOFILL_TYPE_LIST = 3;
/**
* Autofill type for a field that contains a date, which is represented by a long representing
* the number of milliseconds since the standard base time known as "the epoch", namely
* January 1, 1970, 00:00:00 GMT (see {@link java.util.Date#getTime()}.
*
* {@link AutofillValue} instances for autofilling a {@link View} can be obtained through
* {@link AutofillValue#forDate(long)}, and the values passed to
* autofill a {@link View} can be fetched through {@link AutofillValue#getDateValue()}.
*
* @see #getAutofillType()
*/
public static final int AUTOFILL_TYPE_DATE = 4;
/** @hide */
@IntDef(prefix = { "IMPORTANT_FOR_AUTOFILL_" }, value = {
IMPORTANT_FOR_AUTOFILL_AUTO,
IMPORTANT_FOR_AUTOFILL_YES,
IMPORTANT_FOR_AUTOFILL_NO,
IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutofillImportance {}
/**
* Automatically determine whether a view is important for autofill.
*
* @see #isImportantForAutofill()
* @see #setImportantForAutofill(int)
*/
public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0;
/**
* The view is important for autofill, and its children (if any) will be traversed.
*
* @see #isImportantForAutofill()
* @see #setImportantForAutofill(int)
*/
public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1;
/**
* The view is not important for autofill, but its children (if any) will be traversed.
*
* @see #isImportantForAutofill()
* @see #setImportantForAutofill(int)
*/
public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2;
/**
* The view is important for autofill, but its children (if any) will not be traversed.
*
* @see #isImportantForAutofill()
* @see #setImportantForAutofill(int)
*/
public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 0x4;
/**
* The view is not important for autofill, and its children (if any) will not be traversed.
*
* @see #isImportantForAutofill()
* @see #setImportantForAutofill(int)
*/
public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 0x8;
/** @hide */
@IntDef(flag = true, prefix = { "AUTOFILL_FLAG_" }, value = {
AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutofillFlags {}
/**
* Flag requesting you to add views that are marked as not important for autofill
* (see {@link #setImportantForAutofill(int)}) to a {@link ViewStructure}.
*/
public static final int AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x1;
/** @hide */
@IntDef(prefix = { "IMPORTANT_FOR_CONTENT_CAPTURE_" }, value = {
IMPORTANT_FOR_CONTENT_CAPTURE_AUTO,
IMPORTANT_FOR_CONTENT_CAPTURE_YES,
IMPORTANT_FOR_CONTENT_CAPTURE_NO,
IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
})
@Retention(RetentionPolicy.SOURCE)
public @interface ContentCaptureImportance {}
/**
* Automatically determine whether a view is important for content capture.
*
* @see #isImportantForContentCapture()
* @see #setImportantForContentCapture(int)
*/
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0x0;
/**
* The view is important for content capture, and its children (if any) will be traversed.
*
* @see #isImportantForContentCapture()
* @see #setImportantForContentCapture(int)
*/
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 0x1;
/**
* The view is not important for content capture, but its children (if any) will be traversed.
*
* @see #isImportantForContentCapture()
* @see #setImportantForContentCapture(int)
*/
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 0x2;
/**
* The view is important for content capture, but its children (if any) will not be traversed.
*
* @see #isImportantForContentCapture()
* @see #setImportantForContentCapture(int)
*/
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 0x4;
/**
* The view is not important for content capture, and its children (if any) will not be
* traversed.
*
* @see #isImportantForContentCapture()
* @see #setImportantForContentCapture(int)
*/
public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 0x8;
/** {@hide} */
@IntDef(flag = true, prefix = {"SCROLL_CAPTURE_HINT_"},
value = {
SCROLL_CAPTURE_HINT_AUTO,
SCROLL_CAPTURE_HINT_EXCLUDE,
SCROLL_CAPTURE_HINT_INCLUDE,
SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS
})
@Retention(RetentionPolicy.SOURCE)
public @interface ScrollCaptureHint {}
/**
* The content of this view will be considered for scroll capture if scrolling is possible.
*
* @see #getScrollCaptureHint()
* @see #setScrollCaptureHint(int)
*/
public static final int SCROLL_CAPTURE_HINT_AUTO = 0;
/**
* Explicitly exclude this view as a potential scroll capture target. The system will not
* consider it. Mutually exclusive with {@link #SCROLL_CAPTURE_HINT_INCLUDE}, which this flag
* takes precedence over.
*
* @see #getScrollCaptureHint()
* @see #setScrollCaptureHint(int)
*/
public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 0x1;
/**
* Explicitly include this view as a potential scroll capture target. When locating a scroll
* capture target, this view will be prioritized before others without this flag. Mutually
* exclusive with {@link #SCROLL_CAPTURE_HINT_EXCLUDE}, which takes precedence.
*
* @see #getScrollCaptureHint()
* @see #setScrollCaptureHint(int)
*/
public static final int SCROLL_CAPTURE_HINT_INCLUDE = 0x2;
/**
* Explicitly exclude all children of this view as potential scroll capture targets. This view
* is unaffected. Note: Excluded children are not considered, regardless of {@link
* #SCROLL_CAPTURE_HINT_INCLUDE}.
*
* @see #getScrollCaptureHint()
* @see #setScrollCaptureHint(int)
*/
public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 0x4;
/**
* This view is enabled. Interpretation varies by subclass.
* Use with ENABLED_MASK when calling setFlags.
* {@hide}
*/
static final int ENABLED = 0x00000000;
/**
* This view is disabled. Interpretation varies by subclass.
* Use with ENABLED_MASK when calling setFlags.
* {@hide}
*/
static final int DISABLED = 0x00000020;
/**
* Mask for use with setFlags indicating bits used for indicating whether
* this view is enabled
* {@hide}
*/
static final int ENABLED_MASK = 0x00000020;
/**
* This view won't draw. {@link #onDraw(android.graphics.Canvas)} won't be
* called and further optimizations will be performed. It is okay to have
* this flag set and a background. Use with DRAW_MASK when calling setFlags.
* {@hide}
*/
static final int WILL_NOT_DRAW = 0x00000080;
/**
* Mask for use with setFlags indicating bits used for indicating whether
* this view is will draw
* {@hide}
*/
static final int DRAW_MASK = 0x00000080;
/**
* This view doesn't show scrollbars. This view shows horizontal scrollbars. This view shows vertical scrollbars. Mask for use with setFlags indicating bits used for indicating which
* scrollbars are enabled.
* This is only used for support library as of Android R. The framework now uses
* {@link #PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS} such that it can skip the legacy
* insets path that loses insets information.
*/
static final int OPTIONAL_FITS_SYSTEM_WINDOWS = 0x00000800;
/**
* This view doesn't show fading edges. This view shows horizontal fading edges. This view shows vertical fading edges. Mask for use with setFlags indicating bits used for indicating which
* fading edges are enabled. Indicates this view can be clicked. When clickable, a View reacts
* to clicks by notifying the OnClickListener.
* {@hide}
*/
static final int CLICKABLE = 0x00004000;
/**
* Indicates this view is caching its drawing into a bitmap. Indicates that no icicle should be saved for this view.
* {@hide}
*/
static final int SAVE_DISABLED = 0x000010000;
/**
* Mask for use with setFlags indicating bits used for the saveEnabled
* property. Indicates that no drawing cache should ever be created for this view.
* {@hide}
*/
static final int WILL_NOT_CACHE_DRAWING = 0x000020000;
/**
* Indicates this view can take / keep focus when int touch mode. Enables low quality mode for the drawing cache. Enables high quality mode for the drawing cache. Enables automatic quality mode for the drawing cache. Mask for use with setFlags indicating bits used for the cache
* quality property.
* Indicates this view can be long clicked. When long clickable, a View
* reacts to long clicks by notifying the OnLongClickListener or showing a
* context menu.
* Indicates that this view gets its drawable states from its direct parent
* and ignores its original internal states.
* Indicates this view can be context clicked. When context clickable, a View reacts to a
* context click (e.g. a primary stylus button press or right mouse click) by notifying the
* OnContextClickListener.
* Indicates that the view hierarchy should stop saving state when
* it reaches this view. If state saving is initiated immediately at
* the view, it will be allowed.
* {@hide}
*/
static final int PARENT_SAVE_DISABLED = 0x20000000;
/**
* Mask for use with setFlags indicating bits used for PARENT_SAVE_DISABLED. Indicates this view can display a tooltip on hover or long press.
* {@link #AUTOFILL_HINT_USERNAME},
* {@link #AUTOFILL_HINT_PASSWORD},
* {@link #AUTOFILL_HINT_CREDIT_CARD_NUMBER},
* {@link #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE},
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE},
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH},
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}
* are considered sensitive hints by the framework, and the list may include more hints
* in the future.
*
* The window hosting a sensitive view will be marked as secure during an active media
* projection session. This would be equivalent to applying
* {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
*
* @see #getContentSensitivity()
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
public static final int CONTENT_SENSITIVITY_AUTO = 0x0;
/**
* The view displays sensitive content.
*
* The window hosting a sensitive view will be marked as secure during an active media
* projection session. This would be equivalent to applying
* {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
*
* @see #getContentSensitivity()
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
public static final int CONTENT_SENSITIVITY_SENSITIVE = 0x1;
/**
* The view doesn't display sensitive content.
*
* @see #getContentSensitivity()
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
public static final int CONTENT_SENSITIVITY_NOT_SENSITIVE = 0x2;
/** @hide */
@IntDef(flag = true, prefix = { "FOCUSABLES_" }, value = {
FOCUSABLES_ALL,
FOCUSABLES_TOUCH_MODE
})
@Retention(RetentionPolicy.SOURCE)
public @interface FocusableMode {}
/**
* View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
* should add all focusable Views regardless if they are focusable in touch mode.
*/
public static final int FOCUSABLES_ALL = 0x00000000;
/**
* View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
* should add only Views focusable in touch mode.
*/
public static final int FOCUSABLES_TOUCH_MODE = 0x00000001;
/** @hide */
@IntDef(prefix = { "FOCUS_" }, value = {
FOCUS_BACKWARD,
FOCUS_FORWARD,
FOCUS_LEFT,
FOCUS_UP,
FOCUS_RIGHT,
FOCUS_DOWN
})
@Retention(RetentionPolicy.SOURCE)
public @interface FocusDirection {}
/** @hide */
@IntDef(prefix = { "FOCUS_" }, value = {
FOCUS_LEFT,
FOCUS_UP,
FOCUS_RIGHT,
FOCUS_DOWN
})
@Retention(RetentionPolicy.SOURCE)
public @interface FocusRealDirection {} // Like @FocusDirection, but without forward/backward
/**
* Use with {@link #focusSearch(int)}. Move focus to the previous selectable
* item.
*/
public static final int FOCUS_BACKWARD = 0x00000001;
/**
* Use with {@link #focusSearch(int)}. Move focus to the next selectable
* item.
*/
public static final int FOCUS_FORWARD = 0x00000002;
/**
* Use with {@link #focusSearch(int)}. Move focus to the left.
*/
public static final int FOCUS_LEFT = 0x00000011;
/**
* Use with {@link #focusSearch(int)}. Move focus up.
*/
public static final int FOCUS_UP = 0x00000021;
/**
* Use with {@link #focusSearch(int)}. Move focus to the right.
*/
public static final int FOCUS_RIGHT = 0x00000042;
/**
* Use with {@link #focusSearch(int)}. Move focus down.
*/
public static final int FOCUS_DOWN = 0x00000082;
/**
* Bits of {@link #getMeasuredWidthAndState()} and
* {@link #getMeasuredWidthAndState()} that provide the actual measured size.
*/
public static final int MEASURED_SIZE_MASK = 0x00ffffff;
/**
* Bits of {@link #getMeasuredWidthAndState()} and
* {@link #getMeasuredWidthAndState()} that provide the additional state bits.
*/
public static final int MEASURED_STATE_MASK = 0xff000000;
/**
* Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits
* for functions that combine both width and height into a single int,
* such as {@link #getMeasuredState()} and the childState argument of
* {@link #resolveSizeAndState(int, int, int)}.
*/
public static final int MEASURED_HEIGHT_STATE_SHIFT = 16;
/**
* Bit of {@link #getMeasuredWidthAndState()} and
* {@link #getMeasuredWidthAndState()} that indicates the measured size
* is smaller that the space the view would like to have.
*/
public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
/**
* Base View state sets
*/
// Singles
/**
* Indicates the view has no states set. States are used with
* {@link android.graphics.drawable.Drawable} to change the drawing of the
* view depending on its state.
*
* @see android.graphics.drawable.Drawable
* @see #getDrawableState()
*/
protected static final int[] EMPTY_STATE_SET;
/**
* Indicates the view is enabled. States are used with
* {@link android.graphics.drawable.Drawable} to change the drawing of the
* view depending on its state.
*
* @see android.graphics.drawable.Drawable
* @see #getDrawableState()
*/
protected static final int[] ENABLED_STATE_SET;
/**
* Indicates the view is focused. States are used with
* {@link android.graphics.drawable.Drawable} to change the drawing of the
* view depending on its state.
*
* @see android.graphics.drawable.Drawable
* @see #getDrawableState()
*/
protected static final int[] FOCUSED_STATE_SET;
/**
* Indicates the view is selected. States are used with
* {@link android.graphics.drawable.Drawable} to change the drawing of the
* view depending on its state.
*
* @see android.graphics.drawable.Drawable
* @see #getDrawableState()
*/
protected static final int[] SELECTED_STATE_SET;
/**
* Indicates the view is pressed. States are used with
* {@link android.graphics.drawable.Drawable} to change the drawing of the
* view depending on its state.
*
* @see android.graphics.drawable.Drawable
* @see #getDrawableState()
*/
protected static final int[] PRESSED_STATE_SET;
/**
* Indicates the view's window has focus. States are used with
* {@link android.graphics.drawable.Drawable} to change the drawing of the
* view depending on its state.
*
* @see android.graphics.drawable.Drawable
* @see #getDrawableState()
*/
protected static final int[] WINDOW_FOCUSED_STATE_SET;
// Doubles
/**
* Indicates the view is enabled and has the focus.
*
* @see #ENABLED_STATE_SET
* @see #FOCUSED_STATE_SET
*/
protected static final int[] ENABLED_FOCUSED_STATE_SET;
/**
* Indicates the view is enabled and selected.
*
* @see #ENABLED_STATE_SET
* @see #SELECTED_STATE_SET
*/
protected static final int[] ENABLED_SELECTED_STATE_SET;
/**
* Indicates the view is enabled and that its window has focus.
*
* @see #ENABLED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is focused and selected.
*
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
*/
protected static final int[] FOCUSED_SELECTED_STATE_SET;
/**
* Indicates the view has the focus and that its window has the focus.
*
* @see #FOCUSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is selected and that its window has the focus.
*
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET;
// Triples
/**
* Indicates the view is enabled, focused and selected.
*
* @see #ENABLED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
*/
protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET;
/**
* Indicates the view is enabled, focused and its window has the focus.
*
* @see #ENABLED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is enabled, selected and its window has the focus.
*
* @see #ENABLED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is focused, selected and its window has the focus.
*
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is enabled, focused, selected and its window
* has the focus.
*
* @see #ENABLED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed and its window has the focus.
*
* @see #PRESSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed and selected.
*
* @see #PRESSED_STATE_SET
* @see #SELECTED_STATE_SET
*/
protected static final int[] PRESSED_SELECTED_STATE_SET;
/**
* Indicates the view is pressed, selected and its window has the focus.
*
* @see #PRESSED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed and focused.
*
* @see #PRESSED_STATE_SET
* @see #FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed, focused and its window has the focus.
*
* @see #PRESSED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed, focused and selected.
*
* @see #PRESSED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_FOCUSED_SELECTED_STATE_SET;
/**
* Indicates the view is pressed, focused, selected and its window has the focus.
*
* @see #PRESSED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed and enabled.
*
* @see #PRESSED_STATE_SET
* @see #ENABLED_STATE_SET
*/
protected static final int[] PRESSED_ENABLED_STATE_SET;
/**
* Indicates the view is pressed, enabled and its window has the focus.
*
* @see #PRESSED_STATE_SET
* @see #ENABLED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed, enabled and selected.
*
* @see #PRESSED_STATE_SET
* @see #ENABLED_STATE_SET
* @see #SELECTED_STATE_SET
*/
protected static final int[] PRESSED_ENABLED_SELECTED_STATE_SET;
/**
* Indicates the view is pressed, enabled, selected and its window has the
* focus.
*
* @see #PRESSED_STATE_SET
* @see #ENABLED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed, enabled and focused.
*
* @see #PRESSED_STATE_SET
* @see #ENABLED_STATE_SET
* @see #FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_ENABLED_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed, enabled, focused and its window has the
* focus.
*
* @see #PRESSED_STATE_SET
* @see #ENABLED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET;
/**
* Indicates the view is pressed, enabled, focused and selected.
*
* @see #PRESSED_STATE_SET
* @see #ENABLED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET;
/**
* Indicates the view is pressed, enabled, focused, selected and its window
* has the focus.
*
* @see #PRESSED_STATE_SET
* @see #ENABLED_STATE_SET
* @see #SELECTED_STATE_SET
* @see #FOCUSED_STATE_SET
* @see #WINDOW_FOCUSED_STATE_SET
*/
protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
/**
* This indicates that the frame rate category was chosen for an unknown reason.
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_UNKNOWN = 0x0000_0000;
/**
* This indicates that the frame rate category was chosen because it was a small area update.
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_SMALL = 0x0100_0000;
/**
* This indicates that the frame rate category was chosen because it was an intermittent update.
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_INTERMITTENT = 0x0200_0000;
/**
* This indicates that the frame rate category was chosen because it was a large View.
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_LARGE = 0x03000000;
/**
* This indicates that the frame rate category was chosen because it was requested.
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_REQUESTED = 0x0400_0000;
/**
* This indicates that the frame rate category was chosen because an invalid frame rate was
* requested.
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_INVALID = 0x0500_0000;
/**
* This indicates that the frame rate category was chosen because the view has a velocity
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_VELOCITY = 0x0600_0000;
/**
* This indicates that the frame rate category was chosen because it is currently boosting.
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_BOOST = 0x0800_0000;
/**
* This indicates that the frame rate category was chosen because it is currently having
* touch boost.
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_TOUCH = 0x0900_0000;
/**
* This indicates that the frame rate category was chosen because it is currently having
* touch boost.
* @hide
*/
public static final int FRAME_RATE_CATEGORY_REASON_CONFLICTED = 0x0A00_0000;
private static final int FRAME_RATE_CATEGORY_REASON_MASK = 0xFFFF_0000;
/**
* @hide
*/
protected static boolean sToolkitSetFrameRateReadOnlyFlagValue;
private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
private static final boolean sToolkitFrameRateDefaultNormalReadOnlyFlagValue =
toolkitFrameRateDefaultNormalReadOnly();
private static final boolean sToolkitFrameRateBySizeReadOnlyFlagValue =
toolkitFrameRateBySizeReadOnly();
private static final boolean sToolkitFrameRateSmallUsesPercentReadOnlyFlagValue =
toolkitFrameRateSmallUsesPercentReadOnly();
private static final boolean sToolkitFrameRateViewEnablingReadOnlyFlagValue =
toolkitFrameRateViewEnablingReadOnly();
private static boolean sToolkitFrameRateVelocityMappingReadOnlyFlagValue =
toolkitFrameRateVelocityMappingReadOnly();
// Used to set frame rate compatibility.
@Surface.FrameRateCompatibility int mFrameRateCompatibility =
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
static {
EMPTY_STATE_SET = StateSet.get(0);
WINDOW_FOCUSED_STATE_SET = StateSet.get(StateSet.VIEW_STATE_WINDOW_FOCUSED);
SELECTED_STATE_SET = StateSet.get(StateSet.VIEW_STATE_SELECTED);
SELECTED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED);
FOCUSED_STATE_SET = StateSet.get(StateSet.VIEW_STATE_FOCUSED);
FOCUSED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_FOCUSED);
FOCUSED_SELECTED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_SELECTED | StateSet.VIEW_STATE_FOCUSED);
FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED
| StateSet.VIEW_STATE_FOCUSED);
ENABLED_STATE_SET = StateSet.get(StateSet.VIEW_STATE_ENABLED);
ENABLED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_ENABLED);
ENABLED_SELECTED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_SELECTED | StateSet.VIEW_STATE_ENABLED);
ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED
| StateSet.VIEW_STATE_ENABLED);
ENABLED_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_FOCUSED | StateSet.VIEW_STATE_ENABLED);
ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_FOCUSED
| StateSet.VIEW_STATE_ENABLED);
ENABLED_FOCUSED_SELECTED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_SELECTED | StateSet.VIEW_STATE_FOCUSED
| StateSet.VIEW_STATE_ENABLED);
ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED
| StateSet.VIEW_STATE_FOCUSED| StateSet.VIEW_STATE_ENABLED);
PRESSED_STATE_SET = StateSet.get(StateSet.VIEW_STATE_PRESSED);
PRESSED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_PRESSED);
PRESSED_SELECTED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_SELECTED | StateSet.VIEW_STATE_PRESSED);
PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED
| StateSet.VIEW_STATE_PRESSED);
PRESSED_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_FOCUSED | StateSet.VIEW_STATE_PRESSED);
PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_FOCUSED
| StateSet.VIEW_STATE_PRESSED);
PRESSED_FOCUSED_SELECTED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_SELECTED | StateSet.VIEW_STATE_FOCUSED
| StateSet.VIEW_STATE_PRESSED);
PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED
| StateSet.VIEW_STATE_FOCUSED | StateSet.VIEW_STATE_PRESSED);
PRESSED_ENABLED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED);
PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_ENABLED
| StateSet.VIEW_STATE_PRESSED);
PRESSED_ENABLED_SELECTED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_SELECTED | StateSet.VIEW_STATE_ENABLED
| StateSet.VIEW_STATE_PRESSED);
PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED
| StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED);
PRESSED_ENABLED_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_FOCUSED | StateSet.VIEW_STATE_ENABLED
| StateSet.VIEW_STATE_PRESSED);
PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_FOCUSED
| StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED);
PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_SELECTED | StateSet.VIEW_STATE_FOCUSED
| StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED);
PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = StateSet.get(
StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED
| StateSet.VIEW_STATE_FOCUSED| StateSet.VIEW_STATE_ENABLED
| StateSet.VIEW_STATE_PRESSED);
sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision();
sUseMeasureCacheDuringForceLayoutFlagValue = enableUseMeasureCacheDuringForceLayout();
}
/**
* Accessibility event types that are dispatched for text population.
*/
private static final int POPULATING_ACCESSIBILITY_EVENT_TYPES =
AccessibilityEvent.TYPE_VIEW_CLICKED
| AccessibilityEvent.TYPE_VIEW_LONG_CLICKED
| AccessibilityEvent.TYPE_VIEW_SELECTED
| AccessibilityEvent.TYPE_VIEW_FOCUSED
| AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
| AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
| AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
| AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
| AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
| AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
static final int DEBUG_CORNERS_COLOR = Color.rgb(63, 127, 255);
static final int DEBUG_CORNERS_SIZE_DIP = 8;
/**
* Temporary Rect currently for use in setBackground(). This will probably
* be extended in the future to hold our own class with more than just
* a Rect. :)
*/
static final ThreadLocal
* Use with {@link #setAccessibilityLiveRegion(int)}.
*/
public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0x00000000;
/**
* Live region mode specifying that accessibility services should notify users of changes to
* this view.
*
* Use with {@link #setAccessibilityLiveRegion(int)}.
*/
public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001;
/**
* Live region mode specifying that accessibility services should immediately notify users of
* changes to this view. For example, a screen reader may interrupt ongoing speech to
* immediately announce these changes.
*
* Use with {@link #setAccessibilityLiveRegion(int)}.
*/
public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002;
/**
* The default whether the view is important for accessibility.
*/
static final int ACCESSIBILITY_LIVE_REGION_DEFAULT = ACCESSIBILITY_LIVE_REGION_NONE;
/**
* Mask for obtaining the bits which specify a view's accessibility live
* region mode.
*/
static final int PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK = (ACCESSIBILITY_LIVE_REGION_NONE
| ACCESSIBILITY_LIVE_REGION_POLITE | ACCESSIBILITY_LIVE_REGION_ASSERTIVE)
<< PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT;
/**
* Flag indicating whether a view has accessibility focus.
*/
static final int PFLAG2_ACCESSIBILITY_FOCUSED = 0x04000000;
/**
* Flag whether the accessibility state of the subtree rooted at this view changed.
*/
static final int PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED = 0x08000000;
/**
* Flag indicating whether a view failed the quickReject() check in draw(). This condition
* is used to check whether later changes to the view's transform should invalidate the
* view to force the quickReject test to run again.
*/
static final int PFLAG2_VIEW_QUICK_REJECTED = 0x10000000;
/**
* Flag indicating that start/end padding has been resolved into left/right padding
* for use in measurement, layout, drawing, etc. This is set by {@link #resolvePadding()}
* and checked by {@link #measure(int, int)} to determine if padding needs to be resolved
* during measurement. In some special cases this is required such as when an adapter-based
* view measures prospective children without attaching them to a window.
*/
static final int PFLAG2_PADDING_RESOLVED = 0x20000000;
/**
* Flag indicating that the start/end drawables has been resolved into left/right ones.
*/
static final int PFLAG2_DRAWABLE_RESOLVED = 0x40000000;
/**
* Indicates that the view is tracking some sort of transient state
* that the app should not need to be aware of, but that the framework
* should take special care to preserve.
*/
static final int PFLAG2_HAS_TRANSIENT_STATE = 0x80000000;
/**
* Group of bits indicating that RTL properties resolution is done.
*/
static final int ALL_RTL_PROPERTIES_RESOLVED = PFLAG2_LAYOUT_DIRECTION_RESOLVED |
PFLAG2_TEXT_DIRECTION_RESOLVED |
PFLAG2_TEXT_ALIGNMENT_RESOLVED |
PFLAG2_PADDING_RESOLVED |
PFLAG2_DRAWABLE_RESOLVED;
// There are a couple of flags left in mPrivateFlags2
/* End of masks for mPrivateFlags2 */
/*
* Masks for mPrivateFlags3, as generated by dumpFlags():
*
* |-------|-------|-------|-------|
* 1 PFLAG3_VIEW_IS_ANIMATING_TRANSFORM
* 1 PFLAG3_VIEW_IS_ANIMATING_ALPHA
* 1 PFLAG3_IS_LAID_OUT
* 1 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT
* 1 PFLAG3_CALLED_SUPER
* 1 PFLAG3_APPLYING_INSETS
* 1 PFLAG3_FITTING_SYSTEM_WINDOWS
* 1 PFLAG3_NESTED_SCROLLING_ENABLED
* 1 PFLAG3_SCROLL_INDICATOR_TOP
* 1 PFLAG3_SCROLL_INDICATOR_BOTTOM
* 1 PFLAG3_SCROLL_INDICATOR_LEFT
* 1 PFLAG3_SCROLL_INDICATOR_RIGHT
* 1 PFLAG3_SCROLL_INDICATOR_START
* 1 PFLAG3_SCROLL_INDICATOR_END
* 1 PFLAG3_ASSIST_BLOCKED
* 1 PFLAG3_CLUSTER
* 1 PFLAG3_IS_AUTOFILLED
* 1 PFLAG3_FINGER_DOWN
* 1 PFLAG3_FOCUSED_BY_DEFAULT
* 1111 PFLAG3_IMPORTANT_FOR_AUTOFILL
* 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
* 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
* 1 PFLAG3_TEMPORARY_DETACH
* 1 PFLAG3_NO_REVEAL_ON_FOCUS
* 1 PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT
* 1 PFLAG3_SCREEN_READER_FOCUSABLE
* 1 PFLAG3_AGGREGATED_VISIBLE
* 1 PFLAG3_AUTOFILLID_EXPLICITLY_SET
* 1 PFLAG3_ACCESSIBILITY_HEADING
* |-------|-------|-------|-------|
*/
/**
* Flag indicating that view has a transform animation set on it. This is used to track whether
* an animation is cleared between successive frames, in order to tell the associated
* DisplayList to clear its animation matrix.
*/
static final int PFLAG3_VIEW_IS_ANIMATING_TRANSFORM = 0x1;
/**
* Flag indicating that view has an alpha animation set on it. This is used to track whether an
* animation is cleared between successive frames, in order to tell the associated
* DisplayList to restore its alpha value.
*/
static final int PFLAG3_VIEW_IS_ANIMATING_ALPHA = 0x2;
/**
* Flag indicating that the view has been through at least one layout since it
* was last attached to a window.
*/
static final int PFLAG3_IS_LAID_OUT = 0x4;
/**
* Flag indicating that a call to measure() was skipped and should be done
* instead when layout() is invoked.
*/
static final int PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT = 0x8;
/**
* Flag indicating that an overridden method correctly called down to
* the superclass implementation as required by the API spec.
*/
static final int PFLAG3_CALLED_SUPER = 0x10;
/**
* Flag indicating that we're in the process of applying window insets.
*/
static final int PFLAG3_APPLYING_INSETS = 0x20;
/**
* Flag indicating that we're in the process of fitting system windows using the old method.
*/
static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x40;
/**
* Flag indicating that nested scrolling is enabled for this view.
* The view will optionally cooperate with views up its parent chain to allow for
* integrated nested scrolling along the same axis.
*/
static final int PFLAG3_NESTED_SCROLLING_ENABLED = 0x80;
/**
* Flag indicating that the bottom scroll indicator should be displayed
* when this view can scroll up.
*/
static final int PFLAG3_SCROLL_INDICATOR_TOP = 0x0100;
/**
* Flag indicating that the bottom scroll indicator should be displayed
* when this view can scroll down.
*/
static final int PFLAG3_SCROLL_INDICATOR_BOTTOM = 0x0200;
/**
* Flag indicating that the left scroll indicator should be displayed
* when this view can scroll left.
*/
static final int PFLAG3_SCROLL_INDICATOR_LEFT = 0x0400;
/**
* Flag indicating that the right scroll indicator should be displayed
* when this view can scroll right.
*/
static final int PFLAG3_SCROLL_INDICATOR_RIGHT = 0x0800;
/**
* Flag indicating that the start scroll indicator should be displayed
* when this view can scroll in the start direction.
*/
static final int PFLAG3_SCROLL_INDICATOR_START = 0x1000;
/**
* Flag indicating that the end scroll indicator should be displayed
* when this view can scroll in the end direction.
*/
static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
static final int SCROLL_INDICATORS_NONE = 0x0000;
/**
* Mask for use with setFlags indicating bits used for indicating which
* scroll indicators are enabled.
*/
static final int SCROLL_INDICATORS_PFLAG3_MASK = PFLAG3_SCROLL_INDICATOR_TOP
| PFLAG3_SCROLL_INDICATOR_BOTTOM | PFLAG3_SCROLL_INDICATOR_LEFT
| PFLAG3_SCROLL_INDICATOR_RIGHT | PFLAG3_SCROLL_INDICATOR_START
| PFLAG3_SCROLL_INDICATOR_END;
/**
* Left-shift required to translate between public scroll indicator flags
* and internal PFLAGS3 flags. When used as a right-shift, translates
* PFLAGS3 flags to public flags.
*/
static final int SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT = 8;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "SCROLL_INDICATOR_" }, value = {
SCROLL_INDICATOR_TOP,
SCROLL_INDICATOR_BOTTOM,
SCROLL_INDICATOR_LEFT,
SCROLL_INDICATOR_RIGHT,
SCROLL_INDICATOR_START,
SCROLL_INDICATOR_END,
})
public @interface ScrollIndicators {}
/**
* Scroll indicator direction for the top edge of the view.
*
* @see #setScrollIndicators(int)
* @see #setScrollIndicators(int, int)
* @see #getScrollIndicators()
*/
public static final int SCROLL_INDICATOR_TOP =
PFLAG3_SCROLL_INDICATOR_TOP >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
/**
* Scroll indicator direction for the bottom edge of the view.
*
* @see #setScrollIndicators(int)
* @see #setScrollIndicators(int, int)
* @see #getScrollIndicators()
*/
public static final int SCROLL_INDICATOR_BOTTOM =
PFLAG3_SCROLL_INDICATOR_BOTTOM >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
/**
* Scroll indicator direction for the left edge of the view.
*
* @see #setScrollIndicators(int)
* @see #setScrollIndicators(int, int)
* @see #getScrollIndicators()
*/
public static final int SCROLL_INDICATOR_LEFT =
PFLAG3_SCROLL_INDICATOR_LEFT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
/**
* Scroll indicator direction for the right edge of the view.
*
* @see #setScrollIndicators(int)
* @see #setScrollIndicators(int, int)
* @see #getScrollIndicators()
*/
public static final int SCROLL_INDICATOR_RIGHT =
PFLAG3_SCROLL_INDICATOR_RIGHT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
/**
* Scroll indicator direction for the starting edge of the view.
*
* Resolved according to the view's layout direction, see
* {@link #getLayoutDirection()} for more information.
*
* @see #setScrollIndicators(int)
* @see #setScrollIndicators(int, int)
* @see #getScrollIndicators()
*/
public static final int SCROLL_INDICATOR_START =
PFLAG3_SCROLL_INDICATOR_START >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
/**
* Scroll indicator direction for the ending edge of the view.
*
* Resolved according to the view's layout direction, see
* {@link #getLayoutDirection()} for more information.
*
* @see #setScrollIndicators(int)
* @see #setScrollIndicators(int, int)
* @see #getScrollIndicators()
*/
public static final int SCROLL_INDICATOR_END =
PFLAG3_SCROLL_INDICATOR_END >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
/**
* Indicates that we are allowing {@link ViewStructure} to traverse
* into this view.
*/
static final int PFLAG3_ASSIST_BLOCKED = 0x4000;
/**
* Flag indicating that the view is a root of a keyboard navigation cluster.
*
* @see #isKeyboardNavigationCluster()
* @see #setKeyboardNavigationCluster(boolean)
*/
private static final int PFLAG3_CLUSTER = 0x8000;
/**
* Flag indicating that the view is autofilled
*
* @see #isAutofilled()
* @see #setAutofilled(boolean, boolean)
*/
private static final int PFLAG3_IS_AUTOFILLED = 0x10000;
/**
* Indicates that the user is currently touching the screen.
* Currently used for the tooltip positioning only.
*/
private static final int PFLAG3_FINGER_DOWN = 0x20000;
/**
* Flag indicating that this view is the default-focus view.
*
* @see #isFocusedByDefault()
* @see #setFocusedByDefault(boolean)
*/
private static final int PFLAG3_FOCUSED_BY_DEFAULT = 0x40000;
/**
* Shift for the bits in {@link #mPrivateFlags3} related to the
* "importantForAutofill" attribute.
*/
static final int PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT = 19;
/**
* Mask for obtaining the bits which specify how to determine
* whether a view is important for autofill.
*/
static final int PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK = (IMPORTANT_FOR_AUTOFILL_AUTO
| IMPORTANT_FOR_AUTOFILL_YES | IMPORTANT_FOR_AUTOFILL_NO
| IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS
| IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS)
<< PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
/**
* Whether this view has rendered elements that overlap (see {@link
* #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and
* {@link #getHasOverlappingRendering()} ). The value in this bit is only valid when
* PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED has been set. Otherwise, the value is
* determined by whatever {@link #hasOverlappingRendering()} returns.
*/
private static final int PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE = 0x800000;
/**
* Whether {@link #forceHasOverlappingRendering(boolean)} has been called. When true, value
* in PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE is valid.
*/
private static final int PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED = 0x1000000;
/**
* Flag indicating that the view is temporarily detached from the parent view.
*
* @see #onStartTemporaryDetach()
* @see #onFinishTemporaryDetach()
*/
static final int PFLAG3_TEMPORARY_DETACH = 0x2000000;
/**
* Flag indicating that the view does not wish to be revealed within its parent
* hierarchy when it gains focus. Expressed in the negative since the historical
* default behavior is to reveal on focus; this flag suppresses that behavior.
*
* @see #setRevealOnFocusHint(boolean)
* @see #getRevealOnFocusHint()
*/
private static final int PFLAG3_NO_REVEAL_ON_FOCUS = 0x4000000;
/**
* Flag indicating that when layout is completed we should notify
* that the view was entered for autofill purposes. To minimize
* showing autofill for views not visible to the user we evaluate
* user visibility which cannot be done until the view is laid out.
*/
static final int PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT = 0x8000000;
/**
* Works like focusable for screen readers, but without the side effects on input focus.
* @see #setScreenReaderFocusable(boolean)
*/
private static final int PFLAG3_SCREEN_READER_FOCUSABLE = 0x10000000;
/**
* The last aggregated visibility. Used to detect when it truly changes.
*/
private static final int PFLAG3_AGGREGATED_VISIBLE = 0x20000000;
/**
* Used to indicate that {@link #mAutofillId} was explicitly set through
* {@link #setAutofillId(AutofillId)}.
*/
private static final int PFLAG3_AUTOFILLID_EXPLICITLY_SET = 0x40000000;
/**
* Indicates if the View is a heading for accessibility purposes
*/
private static final int PFLAG3_ACCESSIBILITY_HEADING = 0x80000000;
/* End of masks for mPrivateFlags3 */
/*
* Masks for mPrivateFlags4, as generated by dumpFlags():
*
* |-------|-------|-------|-------|
* 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
* 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
* 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
* 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
* 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE
* 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK
* 1 PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS
* 1 PFLAG4_AUTOFILL_HIDE_HIGHLIGHT
* 11 PFLAG4_SCROLL_CAPTURE_HINT_MASK
* 1 PFLAG4_ALLOW_CLICK_WHEN_DISABLED
* 1 PFLAG4_DETACHED
* 1 PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE
* 1 PFLAG4_DRAG_A11Y_STARTED
* 1 PFLAG4_AUTO_HANDWRITING_INITIATION_ENABLED
* 1 PFLAG4_IMPORTANT_FOR_CREDENTIAL_MANAGER
* 1 PFLAG4_TRAVERSAL_TRACING_ENABLED
* 1 PFLAG4_RELAYOUT_TRACING_ENABLED
* 1 PFLAG4_ROTARY_HAPTICS_DETERMINED
* 1 PFLAG4_ROTARY_HAPTICS_ENABLED
* 1 PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT
* 1 PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT
* 11 PFLAG4_CONTENT_SENSITIVITY_MASK
* 1 PFLAG4_IS_COUNTED_AS_SENSITIVE
* 1 PFLAG4_HAS_DRAWN
* 1 PFLAG4_HAS_MOVED
* 1 PFLAG4_HAS_VIEW_PROPERTY_INVALIDATION
* |-------|-------|-------|-------|
*/
/**
* Mask for obtaining the bits which specify how to determine
* whether a view is important for autofill.
*
* NOTE: the important for content capture values were the first flags added and are set in
* the rightmost position, so we don't need to shift them
*/
private static final int PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK =
IMPORTANT_FOR_CONTENT_CAPTURE_AUTO | IMPORTANT_FOR_CONTENT_CAPTURE_YES
| IMPORTANT_FOR_CONTENT_CAPTURE_NO
| IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
| IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS;
/*
* Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods
* should be called.
*
* The idea is to call notifyAppeared() after the view is layout and visible, then call
* notifyDisappeared() when it's gone (without known when it was removed from the parent).
*/
private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
/*
* Flags used to cache the value returned by isImportantForContentCapture while the view
* hierarchy is being traversed.
*/
private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED = 0x40;
private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE = 0x80;
private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK =
PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
| PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
/**
* @see #OPTIONAL_FITS_SYSTEM_WINDOWS
*/
static final int PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS = 0x000000100;
/**
* Flag indicating the field should not have yellow highlight when autofilled.
*/
private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x200;
/**
* Shift for the bits in {@link #mPrivateFlags4} related to scroll capture.
*/
static final int PFLAG4_SCROLL_CAPTURE_HINT_SHIFT = 10;
static final int PFLAG4_SCROLL_CAPTURE_HINT_MASK = (SCROLL_CAPTURE_HINT_INCLUDE
| SCROLL_CAPTURE_HINT_EXCLUDE | SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS)
<< PFLAG4_SCROLL_CAPTURE_HINT_SHIFT;
/**
* Indicates if the view can receive click events when disabled.
*/
private static final int PFLAG4_ALLOW_CLICK_WHEN_DISABLED = 0x000001000;
/**
* Indicates if the view is just detached.
*/
private static final int PFLAG4_DETACHED = 0x000002000;
/**
* Indicates that the view has transient state because the system is translating it.
*/
private static final int PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE = 0x000004000;
/**
* Indicates that the view has started a drag with {@link AccessibilityAction#ACTION_DRAG_START}
*/
private static final int PFLAG4_DRAG_A11Y_STARTED = 0x000008000;
/**
* Indicates that the view enables auto handwriting initiation.
*/
private static final int PFLAG4_AUTO_HANDWRITING_ENABLED = 0x000010000;
/**
* Indicates that the view is important for Credential Manager.
*/
private static final int PFLAG4_IMPORTANT_FOR_CREDENTIAL_MANAGER = 0x000020000;
/**
* When set, measure and layout passes of this view will be logged with {@link Trace}, so we
* can better debug jank due to complex view hierarchies.
*/
private static final int PFLAG4_TRAVERSAL_TRACING_ENABLED = 0x000040000;
/**
* When set, emits a {@link Trace} instant event and stacktrace every time a requestLayout of
* this class happens.
*/
private static final int PFLAG4_RELAYOUT_TRACING_ENABLED = 0x000080000;
/** Indicates if rotary scroll haptics support for the view has been determined. */
private static final int PFLAG4_ROTARY_HAPTICS_DETERMINED = 0x100000;
/**
* Indicates if rotary scroll haptics is enabled for this view.
* The source of truth for this info is a ViewConfiguration API; this bit only caches the value.
*/
private static final int PFLAG4_ROTARY_HAPTICS_ENABLED = 0x200000;
/** Indicates if there has been a scroll event since the last rotary input. */
private static final int PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT = 0x400000;
/**
* Indicates if there has been a rotary input that may generate a scroll event.
* This flag is important so that a scroll event can be properly attributed to a rotary input.
*/
private static final int PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT = 0x800000;
private static final int PFLAG4_CONTENT_SENSITIVITY_SHIFT = 24;
/**
* Mask for obtaining the bits which specify how to determine whether a view
* displays sensitive content or not.
*/
private static final int PFLAG4_CONTENT_SENSITIVITY_MASK =
(CONTENT_SENSITIVITY_AUTO | CONTENT_SENSITIVITY_SENSITIVE
| CONTENT_SENSITIVITY_NOT_SENSITIVE) << PFLAG4_CONTENT_SENSITIVITY_SHIFT;
/**
* Whether this view has been counted as a sensitive view or not.
*
* @see AttachInfo#mSensitiveViewsCount
*/
private static final int PFLAG4_IS_COUNTED_AS_SENSITIVE = 0x4000000;
/**
* Whether this view has been drawn once with updateDisplayListIfDirty() or not.
* Used by VRR to for quick detection of scrolling.
*/
private static final int PFLAG4_HAS_DRAWN = 0x8000000;
/**
* Whether this view has been moved with either setTranslationX/Y or setLeft/Top.
* Used by VRR to for quick detection of scrolling.
*/
private static final int PFLAG4_HAS_MOVED = 0x10000000;
/**
* Whether the invalidateViewProperty is involked at current frame.
*/
private static final int PFLAG4_HAS_VIEW_PROPERTY_INVALIDATION = 0x20000000;
/* End of masks for mPrivateFlags4 */
/** @hide */
protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0;
/** @hide */
protected static final int VIEW_STRUCTURE_FOR_AUTOFILL = 1;
/** @hide */
protected static final int VIEW_STRUCTURE_FOR_CONTENT_CAPTURE = 2;
/** @hide */
@IntDef(flag = true, prefix = { "VIEW_STRUCTURE_FOR" }, value = {
VIEW_STRUCTURE_FOR_ASSIST,
VIEW_STRUCTURE_FOR_AUTOFILL,
VIEW_STRUCTURE_FOR_CONTENT_CAPTURE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ViewStructureType {}
/**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
* @see #getOverScrollMode()
* @see #setOverScrollMode(int)
*/
public static final int OVER_SCROLL_ALWAYS = 0;
/**
* Allow a user to over-scroll this view only if the content is large
* enough to meaningfully scroll, provided it is a view that can scroll.
*
* @see #getOverScrollMode()
* @see #setOverScrollMode(int)
*/
public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
/**
* Never allow a user to over-scroll this view.
*
* @see #getOverScrollMode()
* @see #setOverScrollMode(int)
*/
public static final int OVER_SCROLL_NEVER = 2;
/**
* Special constant for {@link #setSystemUiVisibility(int)}: View has
* requested the system UI (status bar) to be visible (the default).
*
* @see #setSystemUiVisibility(int)
* @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController}
* instead.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_VISIBLE = 0;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View has requested the
* system UI to enter an unobtrusive "low profile" mode.
*
* This is for use in games, book readers, video players, or any other
* "immersive" application where the usual system chrome is deemed too distracting.
*
* In low profile mode, the status bar and/or navigation icons may dim.
*
* @see #setSystemUiVisibility(int)
* @deprecated Low profile mode is deprecated. Hide the system bars instead if the application
* needs to be in a unobtrusive mode. Use {@link WindowInsetsController#hide(int)} with
* {@link Type#systemBars()}.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View has requested that the
* system navigation be temporarily hidden.
*
* This is an even less obtrusive state than that called for by
* {@link #SYSTEM_UI_FLAG_LOW_PROFILE}; on devices that draw essential navigation controls
* (Home, Back, and the like) on screen, There is a limitation: because navigation controls are so important, the least user
* interaction will cause them to reappear immediately. When this happens, both
* this flag and {@link #SYSTEM_UI_FLAG_FULLSCREEN} will be cleared automatically,
* so that both elements reappear at the same time.
*
* @see #setSystemUiVisibility(int)
* @deprecated Use {@link WindowInsetsController#hide(int)} with {@link Type#navigationBars()}
* instead.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View has requested to go
* into the normal fullscreen mode so that its content can take over the screen
* while still allowing the user to interact with the application.
*
* This has the same visual effect as
* {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN
* WindowManager.LayoutParams.FLAG_FULLSCREEN},
* meaning that non-critical screen decorations (such as the status bar) will be
* hidden while the user is in the View's window, focusing the experience on
* that content. Unlike the window flag, if you are using ActionBar in
* overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY
* Window.FEATURE_ACTION_BAR_OVERLAY}, then enabling this flag will also
* hide the action bar.
*
* This approach to going fullscreen is best used over the window flag when
* it is a transient state -- that is, the application does this at certain
* points in its user interaction where it wants to allow the user to focus
* on content, but not as a continuous state. For situations where the application
* would like to simply stay full screen the entire time (such as a game that
* wants to take over the screen), the
* {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN window flag}
* is usually a better approach. The state set here will be removed by the system
* in various situations (such as the user moving to another application) like
* the other system UI states.
*
* When using this flag, the application should provide some easy facility
* for the user to go out of it. A common example would be in an e-book
* reader, where tapping on the screen brings back whatever screen and UI
* decorations that had been hidden while the user was immersed in reading
* the book.
*
* @see #setSystemUiVisibility(int)
* @deprecated Use {@link WindowInsetsController#hide(int)} with {@link Type#statusBars()}
* instead.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
/**
* Flag for {@link #setSystemUiVisibility(int)}: When using other layout
* flags, we would like a stable view of the content insets given to
* {@link #fitSystemWindows(Rect)}. This means that the insets seen there
* will always represent the worst case that the application can expect
* as a continuous state. In the stock Android UI this is the space for
* the system bar, nav bar, and status bar, but not more transient elements
* such as an input method.
*
* The stable layout your UI sees is based on the system UI modes you can
* switch to. That is, if you specify {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}
* then you will get a stable layout for changes of the
* {@link #SYSTEM_UI_FLAG_FULLSCREEN} mode; if you specify
* {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} and
* {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}, then you can transition
* to {@link #SYSTEM_UI_FLAG_FULLSCREEN} and {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}
* with a stable layout. (Note that you should avoid using
* {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION} by itself.)
*
* If you have set the window flag {@link WindowManager.LayoutParams#FLAG_FULLSCREEN}
* to hide the status bar (instead of using {@link #SYSTEM_UI_FLAG_FULLSCREEN}),
* then a hidden status bar will be considered a "stable" state for purposes
* here. This allows your UI to continually hide the status bar, while still
* using the system UI flags to hide the action bar while still retaining
* a stable layout. Note that changing the window fullscreen flag will never
* provide a stable layout for a clean transition.
*
* If you are using ActionBar in
* overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY
* Window.FEATURE_ACTION_BAR_OVERLAY}, this flag will also impact the
* insets it adds to those given to the application.
*
* @deprecated Use {@link WindowInsets#getInsetsIgnoringVisibility(int)} instead to retrieve
* insets that don't change when system bars change visibility state.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View would like its window
* to be laid out as if it has requested
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, even if it currently hasn't. This
* allows it to avoid artifacts when switching in and out of that mode, at
* the expense that some of its user interface may be covered by screen
* decorations when they are shown. You can perform layout of your inner
* UI elements to account for the navigation system UI through the
* {@link #fitSystemWindows(Rect)} method.
*
* @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
* {@link Type#navigationBars()}. For non-floating windows that fill the screen, call
* {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false}.
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View would like its window
* to be laid out as if it has requested
* {@link #SYSTEM_UI_FLAG_FULLSCREEN}, even if it currently hasn't. This
* allows it to avoid artifacts when switching in and out of that mode, at
* the expense that some of its user interface may be covered by screen
* decorations when they are shown. You can perform layout of your inner
* UI elements to account for non-fullscreen system UI through the
* {@link #fitSystemWindows(Rect)} method.
*
* Note: on displays that have a {@link DisplayCutout}, the window may still be placed
* differently than if {@link #SYSTEM_UI_FLAG_FULLSCREEN} was set, if the
* window's {@link WindowManager.LayoutParams#layoutInDisplayCutoutMode
* layoutInDisplayCutoutMode} is
* {@link WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
* LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}. To avoid this, use either of the other modes.
*
* @see WindowManager.LayoutParams#layoutInDisplayCutoutMode
* @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
* @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
* @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
*
* @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
* {@link Type#statusBars()} ()}. For non-floating windows that fill the screen, call
* {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false}.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
/**
* Flag for {@link #setSystemUiVisibility(int)}: View would like to remain interactive when
* hiding the navigation bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}. If this flag is
* not set, {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} will be force cleared by the system on any
* user interaction.
* Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only
* has an effect when used in combination with that flag. When system bars are hidden in immersive mode, they can be revealed temporarily with
* system gestures, such as swiping from the top of the screen. These transient system bars
* will overlay app's content, may have some degree of transparency, and will automatically
* hide after a short timeout.
* Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_FULLSCREEN} and
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only has an effect when used in combination
* with one or both of those flags. For this to take effect, the window must request
* {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
* FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} but not
* {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS
* FLAG_TRANSLUCENT_STATUS}.
*
* @see android.R.attr#windowLightStatusBar
* @deprecated Use {@link WindowInsetsController#APPEARANCE_LIGHT_STATUS_BARS} instead.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
/**
* This flag was previously used for a private API. DO NOT reuse it for a public API as it might
* trigger undefined behavior on older platforms with apps compiled against a new SDK.
*/
private static final int SYSTEM_UI_RESERVED_LEGACY1 = 0x00004000;
/**
* This flag was previously used for a private API. DO NOT reuse it for a public API as it might
* trigger undefined behavior on older platforms with apps compiled against a new SDK.
*/
private static final int SYSTEM_UI_RESERVED_LEGACY2 = 0x00010000;
/**
* Flag for {@link #setSystemUiVisibility(int)}: Requests the navigation bar to draw in a mode
* that is compatible with light navigation bar backgrounds.
*
* For this to take effect, the window must request
* {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
* FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} but not
* {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_NAVIGATION
* FLAG_TRANSLUCENT_NAVIGATION}.
*
* @see android.R.attr#windowLightNavigationBar
* @deprecated Use {@link WindowInsetsController#APPEARANCE_LIGHT_NAVIGATION_BARS} instead.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 0x00000010;
/**
* @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead.
*/
@Deprecated
public static final int STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE;
/**
* @deprecated Use {@link #SYSTEM_UI_FLAG_VISIBLE} instead.
*/
@Deprecated
public static final int STATUS_BAR_VISIBLE = SYSTEM_UI_FLAG_VISIBLE;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to make the status bar not expandable. Unless you also
* set {@link #STATUS_BAR_DISABLE_NOTIFICATION_ICONS}, new notifications will continue to show.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final int STATUS_BAR_DISABLE_EXPAND = 0x00010000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide notification icons and scrolling ticker text.
*/
public static final int STATUS_BAR_DISABLE_NOTIFICATION_ICONS = 0x00020000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to disable incoming notification alerts. This will not block
* icons, but it will block sound, vibrating and other visual or aural notifications.
*/
public static final int STATUS_BAR_DISABLE_NOTIFICATION_ALERTS = 0x00040000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the scrolling ticker. Note that
* {@link #STATUS_BAR_DISABLE_NOTIFICATION_ICONS} implies
* {@link #STATUS_BAR_DISABLE_NOTIFICATION_TICKER}.
*/
public static final int STATUS_BAR_DISABLE_NOTIFICATION_TICKER = 0x00080000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide the center system info area.
*/
public static final int STATUS_BAR_DISABLE_SYSTEM_INFO = 0x00100000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the home button. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
*/
@UnsupportedAppUsage
public static final int STATUS_BAR_DISABLE_HOME = 0x00200000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the back button. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
*/
@UnsupportedAppUsage
public static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the clock. You might use this if your activity has
* its own clock making the status bar's clock redundant.
*/
public static final int STATUS_BAR_DISABLE_CLOCK = 0x00800000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the recent apps button. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
*/
@UnsupportedAppUsage
public static final int STATUS_BAR_DISABLE_RECENT = 0x01000000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to disable the global search gesture. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
*/
public static final int STATUS_BAR_DISABLE_SEARCH = 0x02000000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to disable the ongoing call chip.
*/
public static final int STATUS_BAR_DISABLE_ONGOING_CALL_CHIP = 0x04000000;
/**
* @hide
*/
public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00003FF7;
/**
* These are the system UI flags that can be cleared by events outside
* of an application. Currently this is just the ability to tap on the
* screen while hiding the navigation bar to have it return.
* @hide
*/
public static final int SYSTEM_UI_CLEARABLE_FLAGS =
SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION
| SYSTEM_UI_FLAG_FULLSCREEN;
/**
* Flags that can impact the layout in relation to system UI.
*
* @deprecated System UI layout flags are deprecated.
*/
@Deprecated
public static final int SYSTEM_UI_LAYOUT_FLAGS =
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
/** @hide */
@IntDef(flag = true, prefix = { "FIND_VIEWS_" }, value = {
FIND_VIEWS_WITH_TEXT,
FIND_VIEWS_WITH_CONTENT_DESCRIPTION
})
@Retention(RetentionPolicy.SOURCE)
public @interface FindViewFlags {}
/**
* Find views that render the specified text.
*
* @see #findViewsWithText(ArrayList, CharSequence, int)
*/
public static final int FIND_VIEWS_WITH_TEXT = 0x00000001;
/**
* Find find views that contain the specified content description.
*
* @see #findViewsWithText(ArrayList, CharSequence, int)
*/
public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 0x00000002;
/**
* Find views that contain {@link AccessibilityNodeProvider}. Such
* a View is a root of virtual view hierarchy and may contain the searched
* text. If this flag is set Views with providers are automatically
* added and it is a responsibility of the client to call the APIs of
* the provider to determine whether the virtual tree rooted at this View
* contains the text, i.e. getting the list of {@link AccessibilityNodeInfo}s
* representing the virtual views with this text.
*
* @see #findViewsWithText(ArrayList, CharSequence, int)
*
* @hide
*/
public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004;
/**
* The undefined cursor position.
*
* @hide
*/
public static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1;
/**
* Indicates that the screen has changed state and is now off.
*
* @see #onScreenStateChanged(int)
*/
public static final int SCREEN_STATE_OFF = 0x0;
/**
* Indicates that the screen has changed state and is now on.
*
* @see #onScreenStateChanged(int)
*/
public static final int SCREEN_STATE_ON = 0x1;
/**
* Indicates no axis of view scrolling.
*/
public static final int SCROLL_AXIS_NONE = 0;
/**
* Indicates scrolling along the horizontal axis.
*/
public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0;
/**
* Indicates scrolling along the vertical axis.
*/
public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
/**
* Controls the over-scroll mode for this view.
* See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
* {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
* and {@link #OVER_SCROLL_NEVER}.
*/
private int mOverScrollMode;
/**
* The parent this view is attached to.
* {@hide}
*
* @see #getParent()
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected ViewParent mParent;
/**
* {@hide}
*
* Not available for general use. If you need help, hang up and then dial one of the following
* public APIs:
*
* @see #isAttachedToWindow() for current attach state
* @see #onAttachedToWindow() for subclasses performing work when becoming attached
* @see #onDetachedFromWindow() for subclasses performing work when becoming detached
* @see OnAttachStateChangeListener for other code performing work on attach/detach
* @see #getHandler() for posting messages to this view's UI thread/looper
* @see #getParent() for interacting with the parent chain
* @see #getWindowToken() for the current window token
* @see #getRootView() for the view at the root of the attached hierarchy
* @see #getDisplay() for the Display this view is presented on
* @see #getRootWindowInsets() for the current insets applied to the whole attached window
* @see #hasWindowFocus() for whether the attached window is currently focused
* @see #getWindowVisibility() for checking the visibility of the attached window
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
AttachInfo mAttachInfo;
/**
* {@hide}
*/
@ViewDebug.ExportedProperty(flagMapping = {
@ViewDebug.FlagToString(mask = PFLAG_FORCE_LAYOUT, equals = PFLAG_FORCE_LAYOUT,
name = "FORCE_LAYOUT"),
@ViewDebug.FlagToString(mask = PFLAG_LAYOUT_REQUIRED, equals = PFLAG_LAYOUT_REQUIRED,
name = "LAYOUT_REQUIRED"),
@ViewDebug.FlagToString(mask = PFLAG_DRAWING_CACHE_VALID, equals = PFLAG_DRAWING_CACHE_VALID,
name = "DRAWING_CACHE_INVALID", outputIf = false),
@ViewDebug.FlagToString(mask = PFLAG_DRAWN, equals = PFLAG_DRAWN, name = "DRAWN", outputIf = true),
@ViewDebug.FlagToString(mask = PFLAG_DRAWN, equals = PFLAG_DRAWN, name = "NOT_DRAWN", outputIf = false),
@ViewDebug.FlagToString(mask = PFLAG_DIRTY_MASK, equals = PFLAG_DIRTY, name = "DIRTY")
}, formatToHexString = true)
/* @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769414)
public int mPrivateFlags;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768943)
int mPrivateFlags2;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 129147060)
int mPrivateFlags3;
private int mPrivateFlags4;
/**
* This view's request for the visibility of the status bar.
* @hide
*/
@ViewDebug.ExportedProperty(flagMapping = {
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LOW_PROFILE,
equals = SYSTEM_UI_FLAG_LOW_PROFILE,
name = "LOW_PROFILE"),
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_HIDE_NAVIGATION,
equals = SYSTEM_UI_FLAG_HIDE_NAVIGATION,
name = "HIDE_NAVIGATION"),
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_FULLSCREEN,
equals = SYSTEM_UI_FLAG_FULLSCREEN,
name = "FULLSCREEN"),
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LAYOUT_STABLE,
equals = SYSTEM_UI_FLAG_LAYOUT_STABLE,
name = "LAYOUT_STABLE"),
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,
equals = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,
name = "LAYOUT_HIDE_NAVIGATION"),
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
equals = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
name = "LAYOUT_FULLSCREEN"),
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_IMMERSIVE,
equals = SYSTEM_UI_FLAG_IMMERSIVE,
name = "IMMERSIVE"),
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_IMMERSIVE_STICKY,
equals = SYSTEM_UI_FLAG_IMMERSIVE_STICKY,
name = "IMMERSIVE_STICKY"),
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
equals = SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
name = "LIGHT_STATUS_BAR"),
@ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
equals = SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
name = "LIGHT_NAVIGATION_BAR"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_EXPAND,
equals = STATUS_BAR_DISABLE_EXPAND,
name = "STATUS_BAR_DISABLE_EXPAND"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_NOTIFICATION_ICONS,
equals = STATUS_BAR_DISABLE_NOTIFICATION_ICONS,
name = "STATUS_BAR_DISABLE_NOTIFICATION_ICONS"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_NOTIFICATION_ALERTS,
equals = STATUS_BAR_DISABLE_NOTIFICATION_ALERTS,
name = "STATUS_BAR_DISABLE_NOTIFICATION_ALERTS"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_NOTIFICATION_TICKER,
equals = STATUS_BAR_DISABLE_NOTIFICATION_TICKER,
name = "STATUS_BAR_DISABLE_NOTIFICATION_TICKER"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SYSTEM_INFO,
equals = STATUS_BAR_DISABLE_SYSTEM_INFO,
name = "STATUS_BAR_DISABLE_SYSTEM_INFO"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_HOME,
equals = STATUS_BAR_DISABLE_HOME,
name = "STATUS_BAR_DISABLE_HOME"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_BACK,
equals = STATUS_BAR_DISABLE_BACK,
name = "STATUS_BAR_DISABLE_BACK"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_CLOCK,
equals = STATUS_BAR_DISABLE_CLOCK,
name = "STATUS_BAR_DISABLE_CLOCK"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_RECENT,
equals = STATUS_BAR_DISABLE_RECENT,
name = "STATUS_BAR_DISABLE_RECENT"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SEARCH,
equals = STATUS_BAR_DISABLE_SEARCH,
name = "STATUS_BAR_DISABLE_SEARCH"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_ONGOING_CALL_CHIP,
equals = STATUS_BAR_DISABLE_ONGOING_CALL_CHIP,
name = "STATUS_BAR_DISABLE_ONGOING_CALL_CHIP")
}, formatToHexString = true)
@SystemUiVisibility
int mSystemUiVisibility;
/**
* @hide
*/
@IntDef(flag = true, prefix = "", value = {
SYSTEM_UI_FLAG_LOW_PROFILE,
SYSTEM_UI_FLAG_HIDE_NAVIGATION,
SYSTEM_UI_FLAG_FULLSCREEN,
SYSTEM_UI_FLAG_LAYOUT_STABLE,
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
SYSTEM_UI_FLAG_IMMERSIVE,
SYSTEM_UI_FLAG_IMMERSIVE_STICKY,
SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
STATUS_BAR_DISABLE_EXPAND,
STATUS_BAR_DISABLE_NOTIFICATION_ICONS,
STATUS_BAR_DISABLE_NOTIFICATION_ALERTS,
STATUS_BAR_DISABLE_NOTIFICATION_TICKER,
STATUS_BAR_DISABLE_SYSTEM_INFO,
STATUS_BAR_DISABLE_HOME,
STATUS_BAR_DISABLE_BACK,
STATUS_BAR_DISABLE_CLOCK,
STATUS_BAR_DISABLE_RECENT,
STATUS_BAR_DISABLE_SEARCH,
STATUS_BAR_DISABLE_ONGOING_CALL_CHIP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SystemUiVisibility {}
/**
* Reference count for transient state.
* @see #setHasTransientState(boolean)
*/
int mTransientStateCount = 0;
/**
* Count of how many windows this view has been attached to.
*/
int mWindowAttachCount;
/**
* The layout parameters associated with this view and used by the parent
* {@link android.view.ViewGroup} to determine how this view should be
* laid out.
*
* The field should not be used directly. Instead {@link #getLayoutParams()} and {@link
* #setLayoutParams(ViewGroup.LayoutParams)} should be used. The setter guarantees internal
* state correctness of the class.
* {@hide}
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected ViewGroup.LayoutParams mLayoutParams;
/**
* The view flags hold various views states.
*
* Use {@link #setTransitionVisibility(int)} to change the visibility of this view without
* triggering updates.
* {@hide}
*/
@ViewDebug.ExportedProperty(formatToHexString = true)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
int mViewFlags;
static class TransformationInfo {
/**
* The transform matrix for the View. This transform is calculated internally
* based on the translation, rotation, and scale properties.
*
* Do *not* use this variable directly; instead call getMatrix(), which will
* load the value from the View's RenderNode.
*/
private final Matrix mMatrix = new Matrix();
/**
* The inverse transform matrix for the View. This transform is calculated
* internally based on the translation, rotation, and scale properties.
*
* Do *not* use this variable directly; instead call getInverseMatrix(),
* which will load the value from the View's RenderNode.
*/
private Matrix mInverseMatrix;
/**
* The opacity of the View. This is a value from 0 to 1, where 0 means
* completely transparent and 1 means completely opaque.
*/
@ViewDebug.ExportedProperty
private float mAlpha = 1f;
/**
* The opacity of the view as manipulated by the Fade transition. This is a
* property only used by transitions, which is composited with the other alpha
* values to calculate the final visual alpha value.
*/
float mTransitionAlpha = 1f;
}
/** @hide */
@UnsupportedAppUsage
public TransformationInfo mTransformationInfo;
/**
* Current clip bounds. to which all drawing of this view are constrained.
*/
@ViewDebug.ExportedProperty(category = "drawing")
Rect mClipBounds = null;
private boolean mLastIsOpaque;
/**
* The distance in pixels from the left edge of this view's parent
* to the left edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mLeft;
/**
* The mLeft from the previous frame. Used for detecting movement for purposes of variable
* refresh rate.
*/
private int mLastFrameLeft;
/**
* The distance in pixels from the left edge of this view's parent
* to the right edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mRight;
/**
* The distance in pixels from the top edge of this view's parent
* to the top edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mTop;
/**
* The mTop from the previous frame. Used for detecting movement for purposes of variable
* refresh rate.
*/
private int mLastFrameTop;
/**
* The distance in pixels from the top edge of this view's parent
* to the bottom edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mBottom;
/**
* The offset, in pixels, by which the content of this view is scrolled
* horizontally.
* Please use {@link View#getScrollX()} and {@link View#setScrollX(int)} instead of
* accessing these directly.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "scrolling")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mScrollX;
/**
* The offset, in pixels, by which the content of this view is scrolled
* vertically.
* Please use {@link View#getScrollY()} and {@link View#setScrollY(int)} instead of
* accessing these directly.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "scrolling")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected int mScrollY;
/**
* The final computed left padding in pixels that is used for drawing. This is the distance in
* pixels between the left edge of this view and the left edge of its content.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "padding")
@UnsupportedAppUsage
protected int mPaddingLeft = 0;
/**
* The final computed right padding in pixels that is used for drawing. This is the distance in
* pixels between the right edge of this view and the right edge of its content.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "padding")
@UnsupportedAppUsage
protected int mPaddingRight = 0;
/**
* The final computed top padding in pixels that is used for drawing. This is the distance in
* pixels between the top edge of this view and the top edge of its content.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "padding")
@UnsupportedAppUsage
protected int mPaddingTop;
/**
* The final computed bottom padding in pixels that is used for drawing. This is the distance in
* pixels between the bottom edge of this view and the bottom edge of its content.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "padding")
@UnsupportedAppUsage
protected int mPaddingBottom;
/**
* The amount of pixel offset applied to the left edge of this view's handwriting bounds.
*/
private float mHandwritingBoundsOffsetLeft;
/**
* The amount of pixel offset applied to the top edge of this view's handwriting bounds.
*/
private float mHandwritingBoundsOffsetTop;
/**
* The amount of pixel offset applied to the right edge of this view's handwriting bounds.
*/
private float mHandwritingBoundsOffsetRight;
/**
* The amount of pixel offset applied to the bottom edge of this view's handwriting bounds.
*/
private float mHandwritingBoundsOffsetBottom;
/**
* The layout insets in pixels, that is the distance in pixels between the
* visible edges of this view its bounds.
*/
private Insets mLayoutInsets;
/**
* Briefly describes the state of the view and is primarily used for accessibility support.
*/
private CharSequence mStateDescription;
/**
* Briefly describes the view and is primarily used for accessibility support.
*/
private CharSequence mContentDescription;
/**
* If this view represents a distinct part of the window, it can have a title that labels the
* area.
*/
private CharSequence mAccessibilityPaneTitle;
/**
* Describes whether this view should only allow interactions from
* {@link android.accessibilityservice.AccessibilityService}s with the
* {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
* set to true.
*/
private int mExplicitAccessibilityDataSensitive = ACCESSIBILITY_DATA_SENSITIVE_AUTO;
/** Used to calculate and cache {@link #isAccessibilityDataSensitive()}. */
private int mInferredAccessibilityDataSensitive = ACCESSIBILITY_DATA_SENSITIVE_AUTO;
/**
* Specifies the id of a view for which this view serves as a label for
* accessibility purposes.
*/
private int mLabelForId = View.NO_ID;
/**
* Predicate for matching labeled view id with its label for
* accessibility purposes.
*/
private MatchLabelForPredicate mMatchLabelForPredicate;
/**
* Specifies a view before which this one is visited in accessibility traversal.
*/
private int mAccessibilityTraversalBeforeId = NO_ID;
/**
* Specifies a view after which this one is visited in accessibility traversal.
*/
private int mAccessibilityTraversalAfterId = NO_ID;
/**
* Predicate for matching a view by its id.
*/
private MatchIdPredicate mMatchIdPredicate;
/**
* The right padding after RTL resolution, but before taking account of scroll bars.
*
* @hide
*/
@ViewDebug.ExportedProperty(category = "padding")
protected int mUserPaddingRight;
/**
* The resolved bottom padding before taking account of scroll bars.
*
* @hide
*/
@ViewDebug.ExportedProperty(category = "padding")
protected int mUserPaddingBottom;
/**
* The left padding after RTL resolution, but before taking account of scroll bars.
*
* @hide
*/
@ViewDebug.ExportedProperty(category = "padding")
protected int mUserPaddingLeft;
/**
* Cache the paddingStart set by the user to append to the scrollbar's size.
*
*/
@ViewDebug.ExportedProperty(category = "padding")
int mUserPaddingStart;
/**
* Cache the paddingEnd set by the user to append to the scrollbar's size.
*
*/
@ViewDebug.ExportedProperty(category = "padding")
int mUserPaddingEnd;
/**
* The left padding as set by a setter method, a background's padding, or via XML property
* resolution. This value is the padding before LTR resolution or taking account of scrollbars.
*
* @hide
*/
int mUserPaddingLeftInitial;
/**
* The right padding as set by a setter method, a background's padding, or via XML property
* resolution. This value is the padding before LTR resolution or taking account of scrollbars.
*
* @hide
*/
int mUserPaddingRightInitial;
/**
* Default undefined padding
*/
private static final int UNDEFINED_PADDING = Integer.MIN_VALUE;
/**
* Cache if a left padding has been defined explicitly via padding, horizontal padding,
* or leftPadding in XML, or by setPadding(...) or setRelativePadding(...)
*/
private boolean mLeftPaddingDefined = false;
/**
* Cache if a right padding has been defined explicitly via padding, horizontal padding,
* or rightPadding in XML, or by setPadding(...) or setRelativePadding(...)
*/
private boolean mRightPaddingDefined = false;
/**
* @hide
*/
int mOldWidthMeasureSpec = Integer.MIN_VALUE;
/**
* @hide
*/
int mOldHeightMeasureSpec = Integer.MIN_VALUE;
private LongSparseLongArray mMeasureCache;
@ViewDebug.ExportedProperty(deepExport = true, prefix = "bg_")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Drawable mBackground;
private TintInfo mBackgroundTint;
@ViewDebug.ExportedProperty(deepExport = true, prefix = "fg_")
private ForegroundInfo mForegroundInfo;
private Drawable mScrollIndicatorDrawable;
/**
* RenderNode used for backgrounds.
*
* When non-null and valid, this is expected to contain an up-to-date copy
* of the background drawable. It is cleared on temporary detach, and reset
* on cleanup.
* @hide
*/
RenderNode mBackgroundRenderNode;
@UnsupportedAppUsage
private int mBackgroundResource;
private boolean mBackgroundSizeChanged;
/** The default focus highlight.
* @see #mDefaultFocusHighlightEnabled
* @see Drawable#hasFocusStateSpecified()
*/
private Drawable mDefaultFocusHighlight;
private Drawable mDefaultFocusHighlightCache;
private boolean mDefaultFocusHighlightSizeChanged;
/**
* True if the default focus highlight is needed on the target device.
*/
private static boolean sUseDefaultFocusHighlight;
/**
* True if zero-sized views can be focused.
*/
private static boolean sCanFocusZeroSized;
/**
* Always assign focus if a focusable View is available.
*/
private static boolean sAlwaysAssignFocus;
private String mTransitionName;
static class TintInfo {
ColorStateList mTintList;
BlendMode mBlendMode;
boolean mHasTintMode;
boolean mHasTintList;
}
private static class ForegroundInfo {
private Drawable mDrawable;
private TintInfo mTintInfo;
private int mGravity = Gravity.FILL;
private boolean mInsidePadding = true;
private boolean mBoundsChanged = true;
private final Rect mSelfBounds = new Rect();
private final Rect mOverlayBounds = new Rect();
}
static class ListenerInfo {
@UnsupportedAppUsage
ListenerInfo() {
}
/**
* Listener used to dispatch focus change events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
@UnsupportedAppUsage
protected OnFocusChangeListener mOnFocusChangeListener;
/**
* Listeners for layout change events.
*/
private ArrayList If this is the only flag set, then the drag recipient will only have access to text data
* and intents contained in the {@link ClipData} object. Access to URIs contained in the
* {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags Indicates that the view has a software layer. A software layer is backed
* by a bitmap and causes the view to be rendered using Android's software
* rendering pipeline, even if hardware acceleration is enabled. Software layers have various usages: When the application is not using hardware acceleration, a software layer
* is useful to apply a specific color filter and/or blending mode and/or
* translucency to a view and all its children. When the application is using hardware acceleration, a software layer
* is useful to render drawing primitives not supported by the hardware
* accelerated pipeline. It can also be used to cache a complex view tree
* into a texture and reduce the complexity of drawing operations. For instance,
* when animating a complex view tree with a translation, a software layer can
* be used to render the view tree only once. Software layers should be avoided when the affected view tree updates
* often. Every update will require to re-render the software layer, which can
* potentially be slow (particularly when hardware acceleration is turned on
* since the layer will have to be uploaded into a hardware texture after every
* update.) Indicates that the view has a hardware layer. A hardware layer is backed
* by a hardware specific texture (generally Frame Buffer Objects or FBO on
* OpenGL hardware) and causes the view to be rendered using Android's hardware
* rendering pipeline, but only if hardware acceleration is turned on for the
* view hierarchy. When hardware acceleration is turned off, hardware layers
* behave exactly as {@link #LAYER_TYPE_SOFTWARE software layers}. A hardware layer is useful to apply a specific color filter and/or
* blending mode and/or translucency to a view and all its children. A hardware layer can be used to cache a complex view tree into a
* texture and reduce the complexity of drawing operations. For instance,
* when animating a complex view tree with a translation, a hardware layer can
* be used to render the view tree only once. A hardware layer can also be used to increase the rendering quality when
* rotation transformations are applied on a view. It can also be used to
* prevent potential clipping issues when applying 3D transforms on a view.
* When non-null and valid, this is expected to contain an up-to-date copy
* of the View content. Its DisplayList content is cleared on temporary detach and reset on
* cleanup.
*/
@UnsupportedAppUsage
final RenderNode mRenderNode;
/**
* Set to true when the view is sending hover accessibility events because it
* is the innermost hovered view.
*/
private boolean mSendingHoverAccessibilityEvents;
/**
* Delegate for injecting accessibility functionality.
*/
@UnsupportedAppUsage
AccessibilityDelegate mAccessibilityDelegate;
/**
* The view's overlay layer. Developers get a reference to the overlay via getOverlay()
* and add/remove objects to/from the overlay directly through the Overlay methods.
*/
ViewOverlay mOverlay;
/**
* The currently active parent view for receiving delegated nested scrolling events.
* This is set by {@link #startNestedScroll(int)} during a touch interaction and cleared
* by {@link #stopNestedScroll()} at the same point where we clear
* requestDisallowInterceptTouchEvent.
*/
private ViewParent mNestedScrollingParent;
/**
* Consistency verifier for debugging purposes.
* @hide
*/
protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
private int[] mTempNestedScrollConsumed;
/**
* An overlay is going to draw this View instead of being drawn as part of this
* View's parent. mGhostView is the View in the Overlay that must be invalidated
* when this view is invalidated.
*/
GhostView mGhostView;
/**
* Holds pairs of adjacent attribute data: attribute name followed by its value.
* @hide
*/
@ViewDebug.ExportedProperty(category = "attributes", hasAdjacentMapping = true)
public String[] mAttributes;
/**
* Maps a Resource id to its name.
*/
private static SparseArray
* The method onFinishInflate() will be called after all children have been
* added.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @see #View(Context, AttributeSet, int)
*/
public View(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* Perform inflation from XML and apply a class-specific base style from a
* theme attribute. This constructor of View allows subclasses to use their
* own base style when they are inflating. For example, a Button class's
* constructor would call this version of the super class constructor and
* supply
* When determining the final value of a particular attribute, there are
* four inputs that come into play:
*
* Each of these inputs is considered in-order, with the first listed taking
* precedence over the following ones. In other words, if in the
* AttributeSet you have supplied
* Note: this method will only return actual values if the view attribute debugging
* is enabled in Android developer options.
*
* @param attribute Attribute resource ID for which the resolution stack should be returned.
* @return ordered list of resource ID that are considered when resolving attribute values for
* this {@link View}.
*/
@NonNull
public int[] getAttributeResolutionStack(@AttrRes int attribute) {
if (!sDebugViewAttributes
|| mAttributeResolutionStacks == null
|| mAttributeResolutionStacks.get(attribute) == null) {
return new int[0];
}
int[] attributeResolutionStack = mAttributeResolutionStacks.get(attribute);
int stackSize = attributeResolutionStack.length;
if (mSourceLayoutId != ID_NULL) {
stackSize++;
}
int currentIndex = 0;
int[] stack = new int[stackSize];
if (mSourceLayoutId != ID_NULL) {
stack[currentIndex] = mSourceLayoutId;
currentIndex++;
}
for (int i = 0; i < attributeResolutionStack.length; i++) {
stack[currentIndex] = attributeResolutionStack[i];
currentIndex++;
}
return stack;
}
/**
* Returns the mapping of attribute resource ID to source resource ID where the attribute value
* was set. Source resource ID can either be a layout resource ID, if the value was set in XML
* within the View tag, or a style resource ID, if the attribute was set in a style. The source
* resource value will be one of the resource IDs from {@link #getAttributeSourceResourceMap()}.
*
*
* Note: this method will only return actual values if the view attribute debugging
* is enabled in Android developer options.
*
* @return mapping of attribute resource ID to source resource ID where the attribute value
* was set.
*/
@NonNull
@SuppressWarnings("AndroidFrameworkEfficientCollections")
public Map
* Each {@link View} can have an explicit style specified in the layout file.
* This style is used first during the {@link View} attribute resolution, then if an attribute
* is not defined there the resource system looks at default style and theme as fallbacks.
*
*
* Note: this method will only return actual values if the view attribute debugging
* is enabled in Android developer options.
*
* @return The resource ID for the style specified using {@code style="..."} in the
* {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise
* if not specified or otherwise not applicable.
*/
@StyleRes
public int getExplicitStyle() {
if (!sDebugViewAttributes) {
return ID_NULL;
}
return mExplicitStyle;
}
/**
* An implementation of OnClickListener that attempts to lazily load a
* named click handling method from a parent or ancestor context.
*/
private static class DeclaredOnClickListener implements OnClickListener {
private final View mHostView;
private final String mMethodName;
private Method mResolvedMethod;
private Context mResolvedContext;
public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {
mHostView = hostView;
mMethodName = methodName;
}
@Override
public void onClick(@NonNull View v) {
if (mResolvedMethod == null) {
resolveMethod(mHostView.getContext(), mMethodName);
}
try {
mResolvedMethod.invoke(mResolvedContext, v);
} catch (IllegalAccessException e) {
throw new IllegalStateException(
"Could not execute non-public method for android:onClick", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(
"Could not execute method for android:onClick", e);
}
}
@NonNull
private void resolveMethod(@Nullable Context context, @NonNull String name) {
while (context != null) {
try {
if (!context.isRestricted()) {
final Method method = context.getClass().getMethod(mMethodName, View.class);
if (method != null) {
mResolvedMethod = method;
mResolvedContext = context;
return;
}
}
} catch (NoSuchMethodException e) {
// Failed to find method, keep searching up the hierarchy.
}
if (context instanceof ContextWrapper) {
context = ((ContextWrapper) context).getBaseContext();
} else {
// Can't search up the hierarchy, null out and fail.
context = null;
}
}
final int id = mHostView.getId();
final String idText = id == NO_ID ? "" : " with id '"
+ mHostView.getContext().getResources().getResourceEntryName(id) + "'";
throw new IllegalStateException("Could not find method " + mMethodName
+ "(View) in a parent or ancestor Context for android:onClick "
+ "attribute defined on view " + mHostView.getClass() + idText);
}
}
/**
* Non-public constructor for use in testing
*/
@UnsupportedAppUsage
View() {
mResources = null;
mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this));
}
/**
* Returns {@code true} when the View is attached and the system developer setting to show
* the layout bounds is enabled or {@code false} otherwise.
*/
public final boolean isShowingLayoutBounds() {
return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
}
/**
* Used to test isShowingLayoutBounds(). This sets the local value used
* by that function. This method does nothing if the layout isn't attached.
*
* @hide
*/
@TestApi
public final void setShowingLayoutBounds(boolean debugLayout) {
if (mAttachInfo != null) {
mAttachInfo.mDebugLayout = debugLayout;
}
}
private static SparseArray
* Initializes the fading edges from a given set of styled attributes. This
* method should be called by subclasses that need fading edges and when an
* instance of these subclasses is created programmatically rather than
* being inflated from XML. This method is automatically called when the XML
* is inflated.
*
* Initializes the fading edges from a given set of styled attributes. This
* method should be called by subclasses that need fading edges and when an
* instance of these subclasses is created programmatically rather than
* being inflated from XML. This method is automatically called when the XML
* is inflated.
* For details on how to build a Credential Manager request, please see
* {@link GetCredentialRequest}.
*
* This API should be called at any point before the user focuses on the view, e.g. during
* {@code onCreate} of an Activity.
*
* @param request the request to be fired when this view is entered
* @param callback to be invoked when either a response or an exception needs to be
* propagated for the given view
*/
@FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
public void setPendingCredentialRequest(@NonNull GetCredentialRequest request,
@NonNull OutcomeReceiver
* Initializes the scrollbars from a given set of styled attributes. This
* method should be called by subclasses that need scrollbars and when an
* instance of these subclasses is created programmatically rather than
* being inflated from XML. This method is automatically called when the XML
* is inflated.
*
* Initializes the scrollbars from a given set of styled attributes. This
* method should be called by subclasses that need scrollbars and when an
* instance of these subclasses is created programmatically rather than
* being inflated from XML. This method is automatically called when the XML
* is inflated.
*
* Initalizes the scrollability cache if necessary.
*
* See {@link #setScrollIndicators(int, int)} for usage information.
*
* @param indicators a bitmask of indicators that should be enabled, or
* {@code 0} to disable all indicators
* @see #setScrollIndicators(int, int)
* @see #getScrollIndicators()
* @attr ref android.R.styleable#View_scrollIndicators
*/
@RemotableViewMethod
public void setScrollIndicators(@ScrollIndicators int indicators) {
setScrollIndicators(indicators,
SCROLL_INDICATORS_PFLAG3_MASK >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT);
}
/**
* Sets the state of the scroll indicators specified by the mask. To change
* all scroll indicators at once, see {@link #setScrollIndicators(int)}.
*
* When a scroll indicator is enabled, it will be displayed if the view
* can scroll in the direction of the indicator.
*
* Multiple indicator types may be enabled or disabled by passing the
* logical OR of the desired types. If multiple types are specified, they
* will all be set to the same enabled state.
*
* For example, to enable the top scroll indicatorExample: {@code setScrollIndicators
*
* @param indicators the indicator direction, or the logical OR of multiple
* indicator directions. One or more of:
*
* For example, if the top and left scroll indicators are enabled and all
* other indicators are disabled, the return value will be
* {@code View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_LEFT}.
*
* To check whether the bottom scroll indicator is enabled, use the value
* of {@code (getScrollIndicators() & View.SCROLL_INDICATOR_BOTTOM) != 0}.
*
* @return a bitmask representing the enabled scroll indicators
*/
@InspectableProperty(flagMapping = {
@FlagEntry(target = SCROLL_INDICATORS_NONE, mask = 0xffff_ffff, name = "none"),
@FlagEntry(target = SCROLL_INDICATOR_TOP, name = "top"),
@FlagEntry(target = SCROLL_INDICATOR_BOTTOM, name = "bottom"),
@FlagEntry(target = SCROLL_INDICATOR_LEFT, name = "left"),
@FlagEntry(target = SCROLL_INDICATOR_RIGHT, name = "right"),
@FlagEntry(target = SCROLL_INDICATOR_START, name = "start"),
@FlagEntry(target = SCROLL_INDICATOR_END, name = "end")
})
@ScrollIndicators
public int getScrollIndicators() {
return (mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK)
>>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
}
@UnsupportedAppUsage
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
/**
* Register a callback to be invoked when the scroll X or Y positions of
* this view change.
*
* Note: Some views handle scrolling independently from View and may
* have their own separate listeners for scroll-type events. For example,
* {@link android.widget.ListView ListView} allows clients to register an
* {@link android.widget.ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener) AbsListView.OnScrollListener}
* to listen for changes in list scroll position.
*
* @param l The listener to notify when the scroll X or Y position changes.
* @see android.view.View#getScrollX()
* @see android.view.View#getScrollY()
*/
public void setOnScrollChangeListener(OnScrollChangeListener l) {
getListenerInfo().mOnScrollChangeListener = l;
}
/**
* Register a callback to be invoked when focus of this view changed.
*
* @param l The callback that will run.
*/
public void setOnFocusChangeListener(OnFocusChangeListener l) {
getListenerInfo().mOnFocusChangeListener = l;
}
/**
* Add a listener that will be called when the bounds of the view change due to
* layout processing.
*
* @param listener The listener that will be called when layout bounds change.
*/
public void addOnLayoutChangeListener(OnLayoutChangeListener listener) {
ListenerInfo li = getListenerInfo();
if (li.mOnLayoutChangeListeners == null) {
li.mOnLayoutChangeListeners = new ArrayList When set to true, this is a signal to ancestor views in the hierarchy that
* this view would prefer to be brought fully into view when it gains focus.
* For example, a text field that a user is meant to type into. Other views such
* as scrolling containers may prefer to opt-out of this behavior. The default value for views is true, though subclasses may change this
* based on their preferred behavior. When this method returns true for a child view requesting focus, ancestor
* views responding to a focus change in {@link ViewParent#requestChildFocus(View, View)}
* should make a best effort to make the newly focused child fully visible to the user.
* When it returns false, ancestor views should preferably not disrupt scroll positioning or
* other properties affecting visibility to the user as part of the focus change. A View should call this if it maintains some notion of which part
* of its content is interesting. For example, a text editing view
* should call this when its cursor moves.
* The Rectangle passed into this method should be in the View's content coordinate space.
* It should not be affected by which part of the View is currently visible or its scroll
* position.
*
* @param rectangle The rectangle in the View's content coordinate space
* @return Whether any parent scrolled.
* @see AccessibilityAction#ACTION_SHOW_ON_SCREEN
*/
public boolean requestRectangleOnScreen(Rect rectangle) {
return requestRectangleOnScreen(rectangle, false);
}
/**
* Request that a rectangle of this view be visible on the screen,
* scrolling if necessary just enough.
*
* A View should call this if it maintains some notion of which part
* of its content is interesting. For example, a text editing view
* should call this when its cursor moves.
* The Rectangle passed into this method should be in the View's content coordinate space.
* It should not be affected by which part of the View is currently visible or its scroll
* position.
* When
* Note: When not in touch-mode, the framework will try to give focus
* to the first focusable View from the top after focus is cleared. Hence, if this
* View is the first from the top that can take focus, then all callbacks
* related to clearing focus will be invoked after which the framework will
* give focus to this view.
*
* NOTE: The parent view's focused child must be updated manually
* after calling this method. Otherwise, the view hierarchy may be left in
* an inconstent state.
*/
void unFocus(View focused) {
if (DBG) {
System.out.println(this + " unFocus()");
}
clearFocusInternal(focused, false, false);
}
/**
* Returns true if this view has focus itself, or is the ancestor of the
* view that has focus.
*
* @return True if this view has or contains focus, false otherwise.
*/
@ViewDebug.ExportedProperty(category = "focus")
public boolean hasFocus() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0;
}
/**
* Returns true if this view is focusable or if it contains a reachable View
* for which {@link #hasFocusable()} returns {@code true}. A "reachable hasFocusable()"
* is a view whose parents do not block descendants focus.
* Only {@link #VISIBLE} views are considered focusable.
*
* As of {@link Build.VERSION_CODES#O} views that are determined to be focusable
* through {@link #FOCUSABLE_AUTO} will also cause this method to return {@code true}.
* Apps that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} of
* earlier than {@link Build.VERSION_CODES#O} will continue to see this method return
* {@code false} for views not explicitly marked as focusable.
* Use {@link #hasExplicitFocusable()} if you require the pre-{@link Build.VERSION_CODES#O}
* behavior. This method preserves the pre-{@link Build.VERSION_CODES#O} behavior of
* {@link #hasFocusable()} in that only views explicitly set focusable will cause
* this method to return true. A view set to {@link #FOCUSABLE_AUTO} that resolves
* to focusable will not.
* When transitioning from one Activity to another, instead of using
* {@code setAccessibilityPaneTitle()}, set a descriptive title for its window by using
* {@code android:label}
* for the matching Activity entry in your application's manifest or updating the title at
* runtime with {@link android.app.Activity#setTitle(CharSequence)}.
*
*
*
* @param accessibilityPaneTitle The pane's title. Setting to {@code null} indicates that this
* View is not a pane.
*
* {@see AccessibilityNodeInfo#setPaneTitle(CharSequence)}
*
* @attr ref android.R.styleable#View_accessibilityPaneTitle
*/
public void setAccessibilityPaneTitle(@Nullable CharSequence accessibilityPaneTitle) {
if (!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)) {
boolean currentPaneTitleEmpty = mAccessibilityPaneTitle == null;
boolean newPaneTitleEmpty = accessibilityPaneTitle == null;
mAccessibilityPaneTitle = accessibilityPaneTitle;
// Make explicitly important as nulled titles need to be important for DISAPPEARED
// events.
if (mAccessibilityPaneTitle != null
&& getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
if (currentPaneTitleEmpty) {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED);
} else if (newPaneTitleEmpty) {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
} else {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE);
}
}
}
/**
* Get the title of the pane for purposes of accessibility.
*
* @return The current pane title.
*
* {@see #setAccessibilityPaneTitle}.
*
* @attr ref android.R.styleable#View_accessibilityPaneTitle
*/
@InspectableProperty
@Nullable
public CharSequence getAccessibilityPaneTitle() {
return mAccessibilityPaneTitle;
}
private boolean isAccessibilityPane() {
return mAccessibilityPaneTitle != null;
}
/**
* Sends an accessibility event of the given type. If accessibility is
* not enabled this method has no effect. The default implementation calls
* {@link #onInitializeAccessibilityEvent(AccessibilityEvent)} first
* to populate information about the event source (this View), then calls
* {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)} to
* populate the text content of the event source including its descendants,
* then for events type {@link AccessibilityEvent#TYPE_VIEW_SCROLLED}
* and {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} with
* subtype {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_STATE_DESCRIPTION},
* throttle the events, and last calls
* {@link ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
* on its parent to request sending of the event to interested parties.
*
* If an {@link AccessibilityDelegate} has been specified via calling
* {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
* {@link AccessibilityDelegate#sendAccessibilityEvent(View, int)} is
* responsible for handling this call.
*
* If this view uses {@link AccessibilityNodeProvider} to provide virtual view hierarchy rooted
* at this view, this method should not be called to send events from virtual children because
* it will populate the events with wrong information and the events should be throttled per
* child instead at the virtual root level. To send events from virtual children, call
* {@link ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)} on the view's
* parent to request sending of the event to interested parties.
*
* Note: The event generated with this API carries no semantic meaning, and is appropriate only
* in exceptional situations. Apps can generally achieve correct behavior for accessibility by
* accurately supplying the semantics of their UI.
* They should not need to specify what exactly is announced to users.
*
*
* In general, only announce transitions and don't generate a confirmation message for simple
* actions like a button press. Label your controls concisely and precisely instead, and for
* significant UI changes like window changes, use
* {@link android.app.Activity#setTitle(CharSequence)} and
* {@link #setAccessibilityPaneTitle(CharSequence)}.
*
*
* Use {@link #setAccessibilityLiveRegion(int)} to inform the user of changes to critical
* views within the user interface. These should still be used sparingly as they may generate
* announcements every time a View is updated.
*
*
* For notifying users about errors, such as in a login screen with text that displays an
* "incorrect password" notification, that view should send an AccessibilityEvent of type
* {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_ERROR} and set
* {@link AccessibilityNodeInfo#setError(CharSequence)} instead. Custom widgets should expose
* error-setting methods that support accessibility automatically. For example, instead of
* explicitly sending this event when using a TextView, use
* {@link android.widget.TextView#setError(CharSequence)}.
*
*
* Use {@link #setStateDescription(CharSequence)} to convey state changes to views within the
* user interface. While a live region may send different types of events generated by the view,
* state description will send {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events of
* type {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_STATE_DESCRIPTION}.
*
* @param text The announcement text.
*/
public void announceForAccessibility(CharSequence text) {
if (AccessibilityManager.getInstance(mContext).isEnabled() && mParent != null) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_ANNOUNCEMENT);
onInitializeAccessibilityEvent(event);
event.getText().add(text);
event.setContentDescription(null);
mParent.requestSendAccessibilityEvent(this, event);
}
}
/**
* @see #sendAccessibilityEvent(int)
*
* Note: Called from the default {@link AccessibilityDelegate}.
*
* @hide
*/
public void sendAccessibilityEventInternal(int eventType) {
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
}
}
/**
* This method behaves exactly as {@link #sendAccessibilityEvent(int)} but
* takes as an argument an empty {@link AccessibilityEvent} and does not
* perform a check whether accessibility is enabled.
*
* If an {@link AccessibilityDelegate} has been specified via calling
* {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
* {@link AccessibilityDelegate#sendAccessibilityEventUnchecked(View, AccessibilityEvent)}
* is responsible for handling this call.
*
* Note: This method should only be used with event.setText().
* Avoid mutating other event state in this method. In general, put UI metadata in the node for
* services to easily query.
*
* Note that the event text is populated in a separate dispatch path
* ({@link #onPopulateAccessibilityEvent(AccessibilityEvent)}) since we add to the
* event not only the text of the source but also the text of all its descendants.
*
* A typical implementation will call
* {@link #onPopulateAccessibilityEvent(AccessibilityEvent)} on this view
* and then call the {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
* on each child or the first child that is visible. Override this method if custom population
* of the event text content is required.
*
*
* If an {@link AccessibilityDelegate} has been specified via calling
* {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
* {@link AccessibilityDelegate#dispatchPopulateAccessibilityEvent(View, AccessibilityEvent)}
* is responsible for handling this call.
*
* If this view sets {@link #isAccessibilityDataSensitive()} then this view should only append
* sensitive information to an event that also sets
* {@link AccessibilityEvent#isAccessibilityDataSensitive()}.
*
* Note: Accessibility events of certain types are not dispatched for
* populating the event text via this method. For details refer to {@link AccessibilityEvent}.
*
* Note: This method should only be used with event.setText().
* Avoid mutating other event state in this method. Instead, follow the practices described in
* {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}. In general, put UI
* metadata in the node for services to easily query, than in transient events.
*
* Example: Adding formatted date string to an accessibility event in addition
* to the text added by the super implementation:
*
* If an {@link AccessibilityDelegate} has been specified via calling
* {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
* {@link AccessibilityDelegate#onPopulateAccessibilityEvent(View, AccessibilityEvent)}
* is responsible for handling this call.
* Note: Always call the super implementation before adding
* information to the event, in case the default implementation has basic information to add.
*
* Example: Setting the password property of an event in addition
* to properties set by the super implementation:
*
* If an {@link AccessibilityDelegate} has been specified via calling
* {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
* {@link AccessibilityDelegate#onInitializeAccessibilityEvent(View, AccessibilityEvent)}
* is responsible for handling this call.
* Note: Always call the super implementation before adding
* information to the event, in case the default implementation has basic information to add.
*
* Note: The client is responsible for recycling the obtained instance by calling
* {@link AccessibilityNodeInfo#recycle()} to minimize object creation.
*
* Subclasses should override this method, call the super implementation,
* and set additional attributes.
*
* If an {@link AccessibilityDelegate} has been specified via calling
* {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
* {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)}
* is responsible for handling this call.
* The structure should contain at least the following properties:
* It's also recommended to set the following properties - the more properties the structure
* has, the higher the chances of an {@link android.service.autofill.AutofillService} properly
* using the structure:
*
* The default implementation of this method already sets most of these properties based on
* related {@link View} methods (for example, the autofill id is set using
* {@link #getAutofillId()}, the autofill type set using {@link #getAutofillType()}, etc.),
* and views in the standard Android widgets library also override it to set their
* relevant properties (for example, {@link android.widget.TextView} already sets the text
* properties), so it's recommended to only override this method
* (and call {@code super.onProvideAutofillStructure()}) when:
*
* Note: The {@code left} and {@code top} values set in
* {@link ViewStructure#setDimens(int, int, int, int, int, int)} must be relative to the next
* {@link ViewGroup#isImportantForAutofill()} predecessor view included in the structure.
*
* Views support the Autofill Framework mainly by:
* This method is responsible for the former; {@link #autofill(AutofillValue)} is responsible
* for the latter.
*
* @param structure fill in with structured view data for autofill purposes.
* @param flags optional flags.
*
* @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
*/
public void onProvideAutofillStructure(ViewStructure structure, @AutofillFlags int flags) {
onProvideStructure(structure, VIEW_STRUCTURE_FOR_AUTOFILL, flags);
}
/**
* Populates a {@link ViewStructure} for content capture.
*
* This method is called after a view that is eligible for content capture
* (for example, if it {@link #isImportantForContentCapture()}, an intelligence service is
* enabled for the user, and the activity rendering the view is enabled for content capture)
* is laid out and is visible. The populated structure is then passed to the service through
* {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}.
*
* The default implementation of this method sets the most relevant properties based on
* related {@link View} methods, and views in the standard Android widgets library also
* override it to set their relevant properties. Therefore, if overriding this method, it
* is recommended to call {@code super.onProvideContentCaptureStructure()}.
*
* Note: views that manage a virtual structure under this view must populate just
* the node representing this view and return right away, then asynchronously report (not
* necessarily in the UI thread) when the children nodes appear, disappear or have their text
* changed by calling
* {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)},
* {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and
* {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)}
* respectively. The structure for a child must be created using
* {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the
* {@code autofillId} for a child can be obtained either through
* {@code childStructure.getAutofillId()} or
* {@link ContentCaptureSession#newAutofillId(AutofillId, long)}.
*
* When the virtual view hierarchy represents a web page, you should also:
*
* Note: the following methods of the {@code structure} will be ignored:
* This method should be used when the view manages a virtual structure under this view. For
* example, a view that draws input fields using {@link #draw(Canvas)}.
*
* When implementing this method, subclasses must follow the rules below:
*
* Views with virtual children support the Autofill Framework mainly by:
* This method is responsible for the former; {@link #autofill(SparseArray)} is responsible
* for the latter.
*
* @param structure fill in with virtual children data for autofill purposes.
* @param flags optional flags.
*
* @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
*/
public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
if (mContext.isAutofillCompatibilityEnabled()) {
onProvideVirtualStructureCompat(structure, true);
}
}
/**
* Sets the listener to be {@link #performReceiveContent used} to handle insertion of
* content into this view.
*
* Depending on the type of view, this listener may be invoked for different scenarios. For
* example, for an editable {@link android.widget.TextView}, this listener will be invoked for
* the following scenarios:
* When setting a listener, clients must also declare the accepted MIME types.
* The listener will still be invoked even if the MIME type of the content is not one of the
* declared MIME types (e.g. if the user pastes content whose type is not one of the declared
* MIME types).
* In that case, the listener may reject the content (defer to the default platform behavior)
* or execute some other fallback logic (e.g. show an appropriate message to the user).
* The declared MIME types serve as a hint to allow different features to optionally alter
* their behavior. For example, a soft keyboard may optionally choose to hide its UI for
* inserting GIFs for a particular input field if the MIME types set here for that field
* don't include "image/gif" or "image/*".
*
* Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
* MIME types. As a result, you should always write your MIME types with lowercase letters,
* or use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to
* lowercase.
*
* @param mimeTypes The MIME types accepted by the given listener. These may use patterns
* such as "image/*", but may not start with a wildcard. This argument must
* not be null or empty if a non-null listener is passed in.
* @param listener The listener to use. This can be null to reset to the default behavior.
*/
public void setOnReceiveContentListener(
@SuppressLint("NullableCollection") @Nullable String[] mimeTypes,
@Nullable OnReceiveContentListener listener) {
if (listener != null) {
Preconditions.checkArgument(mimeTypes != null && mimeTypes.length > 0,
"When the listener is set, MIME types must also be set");
}
if (mimeTypes != null) {
Preconditions.checkArgument(Arrays.stream(mimeTypes).noneMatch(t -> t.startsWith("*")),
"A MIME type set here must not start with *: " + Arrays.toString(mimeTypes));
}
mReceiveContentMimeTypes = ArrayUtils.isEmpty(mimeTypes) ? null : mimeTypes;
getListenerInfo().mOnReceiveContentListener = listener;
}
/**
* Receives the given content. If no listener is set, invokes {@link #onReceiveContent}. If a
* listener is {@link #setOnReceiveContentListener set}, invokes the listener instead; if the
* listener returns a non-null result, invokes {@link #onReceiveContent} to handle it.
*
* @param payload The content to insert and related metadata.
*
* @return The portion of the passed-in content that was not accepted (may be all, some, or none
* of the passed-in content).
*/
@Nullable
public ContentInfo performReceiveContent(@NonNull ContentInfo payload) {
final OnReceiveContentListener listener = (mListenerInfo == null) ? null
: getListenerInfo().mOnReceiveContentListener;
if (listener != null) {
final ContentInfo remaining = listener.onReceiveContent(this, payload);
return (remaining == null) ? null : onReceiveContent(remaining);
}
return onReceiveContent(payload);
}
/**
* Implements the default behavior for receiving content for this type of view. The default
* view implementation is a no-op (returns the passed-in content without acting on it).
*
* Widgets should override this method to define their default behavior for receiving
* content. Apps should {@link #setOnReceiveContentListener set a listener} to provide
* app-specific handling for receiving content.
*
* See {@link #setOnReceiveContentListener} and {@link #performReceiveContent} for more info.
*
* @param payload The content to insert and related metadata.
*
* @return The portion of the passed-in content that was not handled (may be all, some, or none
* of the passed-in content).
*/
@Nullable
public ContentInfo onReceiveContent(@NonNull ContentInfo payload) {
return payload;
}
/**
* Returns the MIME types accepted by {@link #performReceiveContent} for this view, as
* configured via {@link #setOnReceiveContentListener}. By default returns null.
*
* Different features (e.g. pasting from the clipboard, inserting stickers from the soft
* keyboard, etc) may optionally use this metadata to conditionally alter their behavior. For
* example, a soft keyboard may choose to hide its UI for inserting GIFs for a particular
* input field if the MIME types returned here for that field don't include "image/gif" or
* "image/*".
*
* Note: Comparisons of MIME types should be performed using utilities such as
* {@link ClipDescription#compareMimeTypes} rather than simple string equality, in order to
* correctly handle patterns such as "text/*", "image/*", etc. Note that MIME type matching
* in the Android framework is case-sensitive, unlike formal RFC MIME types. As a result,
* you should always write your MIME types with lowercase letters, or use
* {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to
* lowercase.
*
* @return The MIME types accepted by {@link #performReceiveContent} for this view (may
* include patterns such as "image/*").
*/
@SuppressLint("NullableCollection")
@Nullable
public String[] getReceiveContentMimeTypes() {
return mReceiveContentMimeTypes;
}
/**
* Automatically fills the content of this view with the {@code value}.
*
* Views support the Autofill Framework mainly by:
* {@link #onProvideAutofillStructure(ViewStructure, int)} is responsible for the former,
* this method is responsible for latter.
*
* This method does nothing by default, but when overridden it typically:
* For example, a text-field view could implement the method this way:
*
* If the value is updated asynchronously, the next call to
* {@link AutofillManager#notifyValueChanged(View)} must happen after the value was
* changed to the autofilled value. If not, the view will not be considered autofilled.
*
* Note: After this method is called, the value returned by
* {@link #getAutofillValue()} must be equal to the {@code value} passed to it, otherwise the
* view will not be highlighted as autofilled.
*
* @param value value to be autofilled.
*/
public void autofill(@SuppressWarnings("unused") AutofillValue value) {
}
/**
* Automatically fills the content of the virtual children within this view.
*
* Views with virtual children support the Autofill Framework mainly by:
* {@link #onProvideAutofillVirtualStructure(ViewStructure, int)} is responsible for the
* former, this method is responsible for the latter - see {@link #autofill(AutofillValue)} and
* {@link #onProvideAutofillVirtualStructure(ViewStructure, int)} for more info about autofill.
*
* If a child value is updated asynchronously, the next call to
* {@link AutofillManager#notifyValueChanged(View, int, AutofillValue)} must happen
* after the value was changed to the autofilled value. If not, the child will not be
* considered autofilled.
*
* Note: To indicate that a virtual view was autofilled,
* The autofill id is created on demand, unless it is explicitly set by
* {@link #setAutofillId(AutofillId)}.
*
* See {@link #setAutofillId(AutofillId)} for more info.
*
* @return The View's autofill id.
*/
public final AutofillId getAutofillId() {
if (mAutofillId == null) {
// The autofill id needs to be unique, but its value doesn't matter,
// so it's better to reuse the accessibility id to save space.
mAutofillId = new AutofillId(getAutofillViewId());
}
return mAutofillId;
}
/**
* Returns the {@link GetCredentialRequest} associated with the view.
* If the return value is null, that means no request has been set
* on the view and no {@link CredentialManager} flow will be invoked
* when this view is focused. Traditioanl autofill flows will still
* work, autofilling content if applicable, from
* the active {@link android.service.autofill.AutofillService} on
* the device.
*
* See {@link #setPendingCredentialRequest} for more info.
*
* @return The credential request associated with this View.
*/
@FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
@Nullable
public final GetCredentialRequest getPendingCredentialRequest() {
if (mViewCredentialHandler == null) {
return null;
}
return mViewCredentialHandler.getRequest();
}
/**
* Returns the callback that has previously been set up on this view through
* the {@link #setPendingCredentialRequest} API.
* If the return value is null, that means no callback, or request, has been set
* on the view and no {@link CredentialManager} flow will be invoked
* when this view is focused. Traditioanl autofill flows will still
* work, and autofillable content will still be returned through the
* {@link #autofill(AutofillValue)} )} API.
*
* See {@link #setPendingCredentialRequest} for more info.
*
* @return The callback associated with this view that will be invoked on a response from
* {@link CredentialManager} .
*/
@FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
@Nullable
public final OutcomeReceiver The autofill id is created on demand, and this method should only be called when a view is
* reused after {@link #dispatchProvideAutofillStructure(ViewStructure, int)} is called, as
* that method creates a snapshot of the view that is passed along to the autofill service.
*
* This method is typically used when view subtrees are recycled to represent different
* content* —in this case, the autofill id can be saved before the view content is swapped
* out, and restored later when it's swapped back in. For example:
*
* NOTE: If this view is a descendant of an {@link android.widget.AdapterView}, the system
* may reset its autofill id when this view is recycled. If the autofill ids need to be stable,
* they should be set again in
* {@link android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)}.
*
* @param id an autofill ID that is unique in the {@link android.app.Activity} hosting the view,
* or {@code null} to reset it. Usually it's an id previously allocated to another view (and
* obtained through {@link #getAutofillId()}), or a new value obtained through
* {@link AutofillManager#getNextAutofillId()}.
*
* @throws IllegalStateException if the view is already {@link #isAttachedToWindow() attached to
* a window}.
*
* @throws IllegalArgumentException if the id is an autofill id associated with a virtual view.
*/
public void setAutofillId(@Nullable AutofillId id) {
// TODO(b/37566627): add unit / CTS test for all possible combinations below
if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
Log.v(AUTOFILL_LOG_TAG, "setAutofill(): from " + mAutofillId + " to " + id);
}
if (isAttachedToWindow()) {
throw new IllegalStateException("Cannot set autofill id when view is attached");
}
if (id != null && !id.isNonVirtual()) {
throw new IllegalStateException("Cannot set autofill id assigned to virtual views");
}
if (id == null && (mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) == 0) {
// Ignore reset because it was never explicitly set before.
return;
}
mAutofillId = id;
if (id != null) {
mAutofillViewId = id.getViewId();
mPrivateFlags3 |= PFLAG3_AUTOFILLID_EXPLICITLY_SET;
} else {
mAutofillViewId = NO_ID;
mPrivateFlags3 &= ~PFLAG3_AUTOFILLID_EXPLICITLY_SET;
}
}
/**
* Forces a reset of the autofill ids of the subtree rooted at this view. Like calling
* {@link #setAutofillId(AutofillId) setAutofillId(null)} for each view, but works even if the
* views are attached to a window.
*
* This is useful if the views are being recycled, since an autofill id should uniquely
* identify a particular piece of content.
*
* @hide
*/
public void resetSubtreeAutofillIds() {
if (mAutofillViewId == NO_ID) {
return;
}
if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
Log.v(CONTENT_CAPTURE_LOG_TAG, "resetAutofillId() for " + mAutofillViewId);
} else if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
Log.v(AUTOFILL_LOG_TAG, "resetAutofillId() for " + mAutofillViewId);
}
mAutofillId = null;
mAutofillViewId = NO_ID;
mPrivateFlags3 &= ~PFLAG3_AUTOFILLID_EXPLICITLY_SET;
}
/**
* Describes the autofill type of this view, so an
* {@link android.service.autofill.AutofillService} can create the proper {@link AutofillValue}
* when autofilling the view.
*
* By default returns {@link #AUTOFILL_TYPE_NONE}, but views should override it to properly
* support the Autofill Framework.
*
* @return either {@link #AUTOFILL_TYPE_NONE}, {@link #AUTOFILL_TYPE_TEXT},
* {@link #AUTOFILL_TYPE_LIST}, {@link #AUTOFILL_TYPE_DATE}, or {@link #AUTOFILL_TYPE_TOGGLE}.
*
* @see #onProvideAutofillStructure(ViewStructure, int)
* @see #autofill(AutofillValue)
*/
public @AutofillType int getAutofillType() {
return AUTOFILL_TYPE_NONE;
}
/**
* Gets the hints that help an {@link android.service.autofill.AutofillService} determine how
* to autofill the view with the user's data.
*
* See {@link #setAutofillHints(String...)} for more info about these hints.
*
* @return The hints set via the attribute or {@link #setAutofillHints(String...)}, or
* {@code null} if no hints were set.
*
* @attr ref android.R.styleable#View_autofillHints
*/
@ViewDebug.ExportedProperty()
@InspectableProperty
@Nullable public String[] getAutofillHints() {
return mAutofillHints;
}
/**
* @hide
*/
@TestApi
public boolean isAutofilled() {
return (mPrivateFlags3 & PFLAG3_IS_AUTOFILLED) != 0;
}
/**
* @hide
*/
public boolean hideAutofillHighlight() {
return (mPrivateFlags4 & PFLAG4_AUTOFILL_HIDE_HIGHLIGHT) != 0;
}
/**
* Gets the {@link View}'s current autofill value.
*
* By default returns {@code null}, but subclasses should override it and return an
* appropriate value to properly support the Autofill Framework.
*
* @see #onProvideAutofillStructure(ViewStructure, int)
* @see #autofill(AutofillValue)
*/
@Nullable
public AutofillValue getAutofillValue() {
return null;
}
/**
* Gets the mode for determining whether this view is important for autofill.
*
* See {@link #setImportantForAutofill(int)} and {@link #isImportantForAutofill()} for more
* info about this mode.
*
* @return {@link #IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to
* {@link #setImportantForAutofill(int)}.
*
* @attr ref android.R.styleable#View_importantForAutofill
*/
@ViewDebug.ExportedProperty(mapping = {
@ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_AUTO, to = "auto"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_YES, to = "yes"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO, to = "no"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
to = "yesExcludeDescendants"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,
to = "noExcludeDescendants")})
@InspectableProperty(enumMapping = {
@EnumEntry(value = IMPORTANT_FOR_AUTOFILL_AUTO, name = "auto"),
@EnumEntry(value = IMPORTANT_FOR_AUTOFILL_YES, name = "yes"),
@EnumEntry(value = IMPORTANT_FOR_AUTOFILL_NO, name = "no"),
@EnumEntry(value = IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
name = "yesExcludeDescendants"),
@EnumEntry(value = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,
name = "noExcludeDescendants"),
})
public @AutofillImportance int getImportantForAutofill() {
return (mPrivateFlags3
& PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK) >> PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
}
/**
* Sets the mode for determining whether this view is considered important for autofill.
*
* The platform determines the importance for autofill automatically but you
* can use this method to customize the behavior. For example:
*
* Note: Setting the mode as {@link #IMPORTANT_FOR_AUTOFILL_NO} or
* {@link #IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS} does not guarantee the view (and its
* children) will not be used for autofill purpose; for example, when the user explicitly
* makes an autofill request, all views are included in the ViewStructure, and starting in
* {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} the system uses other factors along
* with importance to determine the autofill behavior. See {@link #isImportantForAutofill()}
* for more details about how the View's importance for autofill is used.
*
* @param mode {@link #IMPORTANT_FOR_AUTOFILL_AUTO}, {@link #IMPORTANT_FOR_AUTOFILL_YES},
* {@link #IMPORTANT_FOR_AUTOFILL_NO}, {@link #IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS},
* or {@link #IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}.
*
* @attr ref android.R.styleable#View_importantForAutofill
*/
public void setImportantForAutofill(@AutofillImportance int mode) {
mPrivateFlags3 &= ~PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK;
mPrivateFlags3 |= (mode << PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT)
& PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK;
}
/**
* Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode}
* associated with this view is considered important for autofill purposes.
*
* Generally speaking, a view is important for autofill if:
* For example, view containers should typically return {@code false} for performance reasons
* (since the important info is provided by their children), but if its properties have relevant
* information (for example, a resource id called {@code credentials}, it should return
* {@code true}. On the other hand, views representing labels or editable fields should
* typically return {@code true}, but in some cases they could return {@code false}
* (for example, if they're part of a "Captcha" mechanism).
*
* The value returned by this method depends on the value returned by
* {@link #getImportantForAutofill()}:
*
* The behavior of importances depends on Android version:
* The window hosting a sensitive view will be marked as secure during an active media
* projection session. This would be equivalent to applying
* {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
*
* @param mode {@link #CONTENT_SENSITIVITY_AUTO}, {@link #CONTENT_SENSITIVITY_NOT_SENSITIVE}
* or {@link #CONTENT_SENSITIVITY_SENSITIVE}
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
public final void setContentSensitivity(@ContentSensitivity int mode) {
mPrivateFlags4 &= ~PFLAG4_CONTENT_SENSITIVITY_MASK;
mPrivateFlags4 |= ((mode << PFLAG4_CONTENT_SENSITIVITY_SHIFT)
& PFLAG4_CONTENT_SENSITIVITY_MASK);
if (sensitiveContentAppProtection()) {
updateSensitiveViewsCountIfNeeded(isAggregatedVisible());
}
}
/**
* Gets content sensitivity mode to determine whether this view displays sensitive content.
*
* See {@link #setContentSensitivity(int)} and
* {@link #isContentSensitive()} for more info about this mode.
*
* @return {@link #CONTENT_SENSITIVITY_AUTO} by default, or value passed to
* {@link #setContentSensitivity(int)}.
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
public @ContentSensitivity final int getContentSensitivity() {
return (mPrivateFlags4 & PFLAG4_CONTENT_SENSITIVITY_MASK)
>> PFLAG4_CONTENT_SENSITIVITY_SHIFT;
}
/**
* Returns whether this view displays sensitive content, based
* on the value explicitly set by {@link #setContentSensitivity(int)}.
*
* @return whether the view displays sensitive content.
*
* @see #setContentSensitivity(int)
* @see #CONTENT_SENSITIVITY_AUTO
* @see #CONTENT_SENSITIVITY_SENSITIVE
* @see #CONTENT_SENSITIVITY_NOT_SENSITIVE
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
public final boolean isContentSensitive() {
final int contentSensitivity = getContentSensitivity();
if (contentSensitivity == CONTENT_SENSITIVITY_SENSITIVE) {
return true;
} else if (contentSensitivity == CONTENT_SENSITIVITY_NOT_SENSITIVE) {
return false;
} else if (sensitiveContentAppProtection()) {
return SensitiveAutofillHintsHelper
.containsSensitiveAutofillHint(getAutofillHints());
}
return false;
}
/**
* Helper used to track sensitive views when they are added or removed from the window
* based on whether it's laid out and visible.
*
* This method is called from many places (visibility changed, view laid out, view attached
* or detached to/from window, etc...)
*/
private void updateSensitiveViewsCountIfNeeded(boolean appeared) {
if (!sensitiveContentAppProtection() || mAttachInfo == null) {
return;
}
if (appeared && isContentSensitive()) {
if ((mPrivateFlags4 & PFLAG4_IS_COUNTED_AS_SENSITIVE) == 0) {
mPrivateFlags4 |= PFLAG4_IS_COUNTED_AS_SENSITIVE;
mAttachInfo.increaseSensitiveViewsCount();
}
} else {
if ((mPrivateFlags4 & PFLAG4_IS_COUNTED_AS_SENSITIVE) != 0) {
mPrivateFlags4 &= ~PFLAG4_IS_COUNTED_AS_SENSITIVE;
mAttachInfo.decreaseSensitiveViewsCount();
}
}
}
/**
* Gets the mode for determining whether this view is important for content capture.
*
* See {@link #setImportantForContentCapture(int)} and
* {@link #isImportantForContentCapture()} for more info about this mode.
*
* @return {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO} by default, or value passed to
* {@link #setImportantForContentCapture(int)}.
*
* @attr ref android.R.styleable#View_importantForContentCapture
*/
@ViewDebug.ExportedProperty(mapping = {
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to = "auto"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES, to = "yes"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO, to = "no"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
to = "yesExcludeDescendants"),
@ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
to = "noExcludeDescendants")})
@InspectableProperty(enumMapping = {
@EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, name = "auto"),
@EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES, name = "yes"),
@EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO, name = "no"),
@EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
name = "yesExcludeDescendants"),
@EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
name = "noExcludeDescendants"),
})
public @ContentCaptureImportance int getImportantForContentCapture() {
// NOTE: the important for content capture values were the first flags added and are set in
// the rightmost position, so we don't need to shift them
return mPrivateFlags4 & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK;
}
/**
* Sets the mode for determining whether this view is considered important for content capture.
*
* The platform determines the importance for autofill automatically but you
* can use this method to customize the behavior. Typically, a view that provides text should
* be marked as {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}.
*
* @param mode {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO},
* {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}, {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO},
* {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS},
* or {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS}.
*
* @attr ref android.R.styleable#View_importantForContentCapture
*/
public void setImportantForContentCapture(@ContentCaptureImportance int mode) {
// Reset first
mPrivateFlags4 &= ~PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK;
// Then set again
// NOTE: the important for content capture values were the first flags added and are set in
// the rightmost position, so we don't need to shift them
mPrivateFlags4 |= (mode & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK);
}
/**
* Hints the Android System whether this view is considered important for content capture, based
* on the value explicitly set by {@link #setImportantForContentCapture(int)} and heuristics
* when it's {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}.
*
* See {@link ContentCaptureManager} for more info about content capture.
*
* @return whether the view is considered important for content capture.
*
* @see #setImportantForContentCapture(int)
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
*/
public final boolean isImportantForContentCapture() {
boolean isImportant;
if ((mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED) != 0) {
isImportant = (mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE) != 0;
return isImportant;
}
isImportant = calculateIsImportantForContentCapture();
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
if (isImportant) {
mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
}
mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED;
return isImportant;
}
/**
* Calculates whether the flag is important for content capture so it can be used by
* {@link #isImportantForContentCapture()} while the tree is traversed.
*/
private boolean calculateIsImportantForContentCapture() {
// Check parent mode to ensure we're important
ViewParent parent = mParent;
while (parent instanceof View) {
final int parentImportance = ((View) parent).getImportantForContentCapture();
if (parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
|| parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS) {
if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for "
+ "content capture because parent " + parent + "'s importance is "
+ parentImportance);
}
return false;
}
parent = parent.getParent();
}
final int importance = getImportantForContentCapture();
// First, check the explicit states.
if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
|| importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES) {
return true;
}
if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
|| importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO) {
if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for content "
+ "capture because its importance is " + importance);
}
return false;
}
// Then use some heuristics to handle AUTO.
if (importance != IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
Log.w(CONTENT_CAPTURE_LOG_TAG, "invalid content capture importance (" + importance
+ " on view " + this);
return false;
}
// View group is important if at least one children also is
if (this instanceof ViewGroup) {
final ViewGroup group = (ViewGroup) this;
for (int i = 0; i < group.getChildCount(); i++) {
final View child = group.getChildAt(i);
if (child.isImportantForContentCapture()) {
return true;
}
}
}
// If the app developer explicitly set hints or autofill hintsfor it, it's important.
if (getAutofillHints() != null) {
return true;
}
// Otherwise, assume it's not important...
return false;
}
/**
* Helper used to notify the {@link ContentCaptureManager} when the view is removed or
* added, based on whether it's laid out and visible, and without knowing if the parent removed
* it from the view hierarchy.
*
* This method is called from many places (visibility changed, view laid out, view attached
* or detached to/from window, etc...) and hence must contain the logic to call the manager, as
* described below:
*
* This method should be called when you need to associate a {@link ContentCaptureContext} to
* the content capture events associated with this view or its view hierarchy (if it's a
* {@link ViewGroup}).
*
* For example, if your activity is associated with a web domain, first you would need to
* set the context for the main DOM:
*
* Then if the page had an {@code IFRAME}, you would create a new session for it:
*
* Typically, this method should only be overridden by subclasses that provide a view
* hierarchy (such as {@link ViewGroup}) - other classes should override
* {@link #onProvideAutofillStructure(ViewStructure, int)} or
* {@link #onProvideAutofillVirtualStructure(ViewStructure, int)} instead.
*
* When overridden, it must:
*
*
* This method only needs overloading if the node is marked as having extra data available.
* Note: By default it returns {@code true}, but views providing a virtual hierarchy
* view must override it.
*
* @return Whether the view is visible on the screen.
*/
public boolean isVisibleToUserForAutofill(int virtualId) {
if (mContext.isAutofillCompatibilityEnabled()) {
final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
if (provider != null) {
final AccessibilityNodeInfo node = provider.createAccessibilityNodeInfo(virtualId);
if (node != null) {
return node.isVisibleToUser();
}
// if node is null, assume it's not visible anymore
} else {
Log.w(VIEW_LOG_TAG, "isVisibleToUserForAutofill(" + virtualId + "): no provider");
}
return false;
}
return true;
}
/**
* Computes whether this view is visible to the user. Such a view is
* attached, visible, all its predecessors are visible, it is not clipped
* entirely by its predecessors, and has an alpha greater than zero.
*
* @return Whether the view is visible on the screen.
*
* @hide
*/
@UnsupportedAppUsage
public boolean isVisibleToUser() {
return isVisibleToUser(null);
}
/**
* Computes whether the given portion of this view is visible to the user.
* Such a view is attached, visible, all its predecessors are visible,
* has an alpha greater than zero, and the specified portion is not
* clipped entirely by its predecessors.
*
* @param boundInView the portion of the view to test; coordinates should be relative; may be
*
* Note: On platform versions prior to
* {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
* views in the {@code android.widget.*} package are called before
* host methods. This prevents certain properties such as class name from
* being modified by overriding
* {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)},
* as any changes will be overwritten by the host class.
*
* Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
* methods are called after host methods, which all properties to be
* modified without being overwritten by the host class.
*
* @param delegate the object to which accessibility method calls should be
* delegated
* @see AccessibilityDelegate
*/
public void setAccessibilityDelegate(@Nullable AccessibilityDelegate delegate) {
mAccessibilityDelegate = delegate;
}
/**
* Gets the provider for managing a virtual view hierarchy rooted at this View
* and reported to {@link android.accessibilityservice.AccessibilityService}s
* that explore the window content.
*
* If this method returns an instance, this instance is responsible for managing
* {@link AccessibilityNodeInfo}s describing the virtual sub-tree rooted at this
* View including the one representing the View itself. Similarly the returned
* instance is responsible for performing accessibility actions on any virtual
* view or the root view itself.
*
* If an {@link AccessibilityDelegate} has been specified via calling
* {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
* {@link AccessibilityDelegate#getAccessibilityNodeProvider(View)}
* is responsible for handling this call.
*
* Note: Do not override this method, as it will have no
* effect on the state description presented to accessibility services.
* You must call {@link #setStateDescription(CharSequence)} to modify the
* state description.
*
* @return the state description
* @see #setStateDescription(CharSequence)
*/
@ViewDebug.ExportedProperty(category = "accessibility")
public final @Nullable CharSequence getStateDescription() {
return mStateDescription;
}
/**
* Returns the {@link View}'s content description.
*
* Note: Do not override this method, as it will have no
* effect on the content description presented to accessibility services.
* You must call {@link #setContentDescription(CharSequence)} to modify the
* content description.
*
* @return the content description
* @see #setContentDescription(CharSequence)
* @attr ref android.R.styleable#View_contentDescription
*/
@ViewDebug.ExportedProperty(category = "accessibility")
@InspectableProperty
public CharSequence getContentDescription() {
return mContentDescription;
}
/**
* Sets the {@link View}'s state description.
*
* A state description briefly describes the states of the view and is primarily used
* for accessibility support to determine how the states of a view should be presented to
* the user. It is a supplement to the boolean states (for example, checked/unchecked) and
* it is used for customized state description (for example, "wifi, connected, three bars").
* State description changes frequently while content description should change less often.
* State description should be localized. For android widgets which have default state
* descriptions, app developers can call this method to override the state descriptions.
* Setting state description to null restores the default behavior.
*
* @param stateDescription The state description.
* @see #getStateDescription()
* @see #setContentDescription(CharSequence) for the difference between content and
* state descriptions.
*/
@RemotableViewMethod
public void setStateDescription(@Nullable CharSequence stateDescription) {
if (mStateDescription == null) {
if (stateDescription == null) {
return;
}
} else if (mStateDescription.equals(stateDescription)) {
return;
}
mStateDescription = stateDescription;
if (!TextUtils.isEmpty(stateDescription)
&& getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
AccessibilityEvent event = AccessibilityEvent.obtain();
event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION);
sendAccessibilityEventUnchecked(event);
}
}
/**
* Sets the {@link View}'s content description.
*
* A content description briefly describes the view and is primarily used
* for accessibility support to determine how a view should be presented to
* the user. In the case of a view with no textual representation, such as
* {@link android.widget.ImageButton}, a useful content description
* explains what the view does. For example, an image button with a phone
* icon that is used to place a call may use "Call" as its content
* description. An image of a floppy disk that is used to save a file may
* use "Save".
*
*
* This should omit role or state. Role refers to the kind of user-interface element the View
* is, such as a Button or Checkbox. State refers to a frequently changing property of the View,
* such as an On/Off state of a button or the audio level of a volume slider.
*
*
* Content description updates are not frequent, and are used when the semantic content - not
* the state - of the element changes. For example, a Play button might change to a Pause
* button during music playback.
*
* @param contentDescription The content description.
* @see #getContentDescription()
* @see #setStateDescription(CharSequence)} for state changes.
* @attr ref android.R.styleable#View_contentDescription
*/
@RemotableViewMethod
public void setContentDescription(CharSequence contentDescription) {
if (mContentDescription == null) {
if (contentDescription == null) {
return;
}
} else if (mContentDescription.equals(contentDescription)) {
return;
}
mContentDescription = contentDescription;
final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
}
}
/**
* Sets the id of a view that screen readers are requested to visit after this view.
*
*
*
* For example, if view B should be visited before view A, with
* B.setAccessibilityTraversalBefore(A), this requests that screen readers visit and traverse
* view B before visiting view A.
*
*
* Note: Views are visited in the order determined by the screen reader. Avoid
* explicitly manipulating focus order, as this may result in inconsistent user
* experiences between apps. Instead, use other semantics, such as restructuring the view
* hierarchy layout, to communicate order.
*
*
* Setting this view to be after a view that is not important for accessibility,
* or if this view is not important for accessibility, means this method will have no effect if
* the service is not aware of unimportant views.
*
*
* To avoid a risk of loops, set clear relationships between views. For example, if focus order
* should be B -> A, and B.setAccessibilityTraversalBefore(A), then also call
* A.setAccessibilityTraversalAfter(B).
*
* @param beforeId The id of a view this one precedes in accessibility traversal.
*
* @attr ref android.R.styleable#View_accessibilityTraversalBefore
*
* @see #setImportantForAccessibility(int)
* @see #setAccessibilityTraversalAfter(int)
*/
@RemotableViewMethod
public void setAccessibilityTraversalBefore(@IdRes int beforeId) {
if (mAccessibilityTraversalBeforeId == beforeId) {
return;
}
mAccessibilityTraversalBeforeId = beforeId;
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
/**
* Gets the id of a view before which this one is visited in accessibility traversal.
*
* @return The id of a view this one precedes in accessibility traversal if
* specified, otherwise {@link #NO_ID}.
*
* @see #setAccessibilityTraversalBefore(int)
*/
@IdRes
@InspectableProperty
public int getAccessibilityTraversalBefore() {
return mAccessibilityTraversalBeforeId;
}
/**
* Sets the id of a view that screen readers are requested to visit before this view.
*
*
* For example, if view B should be visited after A, with B.setAccessibilityTraversalAfter(A),
* then this requests that screen readers visit and traverse view A before visiting view B.
*
*
* Note: Views are visited in the order determined by the screen reader. Avoid
* explicitly manipulating focus order, as this may result in inconsistent user
* experiences between apps. Instead, use other semantics, such as restructuring the view
* hierarchy layout, to communicate order.
*
*
* Setting this view to be after a view that is not important for accessibility,
* or if this view is not important for accessibility, means this method will have no effect if
* the service is not aware of unimportant views.
*
*
* To avoid a risk of loops, set clear relationships between views. For example, if focus order
* should be B -> A, and B.setAccessibilityTraversalBefore(A), then also call
* A.setAccessibilityTraversalAfter(B).
*
* @param afterId The id of a view this one succeeds in accessibility traversal.
*
* @attr ref android.R.styleable#View_accessibilityTraversalAfter
*
* @see #setImportantForAccessibility(int)
* @see #setAccessibilityTraversalBefore(int)
*/
@RemotableViewMethod
public void setAccessibilityTraversalAfter(@IdRes int afterId) {
if (mAccessibilityTraversalAfterId == afterId) {
return;
}
mAccessibilityTraversalAfterId = afterId;
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
/**
* Gets the id of a view after which this one is visited in accessibility traversal.
*
* @return The id of a view this one succeedes in accessibility traversal if
* specified, otherwise {@link #NO_ID}.
*
* @see #setAccessibilityTraversalAfter(int)
*/
@IdRes
@InspectableProperty
public int getAccessibilityTraversalAfter() {
return mAccessibilityTraversalAfterId;
}
/**
* Gets the id of a view for which this view serves as a label for
* accessibility purposes.
*
* @return The labeled view id.
*/
@IdRes
@ViewDebug.ExportedProperty(category = "accessibility")
@InspectableProperty
public int getLabelFor() {
return mLabelForId;
}
/**
* Sets the id of a view for which this view serves as a label for
* accessibility purposes.
*
* @param id The labeled view id.
*/
@RemotableViewMethod
public void setLabelFor(@IdRes int id) {
if (mLabelForId == id) {
return;
}
mLabelForId = id;
if (mLabelForId != View.NO_ID
&& mID == View.NO_ID) {
mID = generateViewId();
}
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
/**
* Invoked whenever this view loses focus, either by losing window focus or by losing
* focus within its window. This method can be used to clear any state tied to the
* focus. For instance, if a button is held pressed with the trackball and the window
* loses focus, this method can be used to cancel the press.
*
* Subclasses of View overriding this method should always call super.onFocusLost().
*
* @see #onFocusChanged(boolean, int, android.graphics.Rect)
* @see #onWindowFocusChanged(boolean)
*
* @hide pending API council approval
*/
@CallSuper
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void onFocusLost() {
resetPressedState();
}
private void resetPressedState() {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return;
}
if (isPressed()) {
setPressed(false);
if (!mHasPerformedLongPress) {
removeLongPressCallback();
}
}
}
/**
* Returns true if this view has focus
*
* @return True if this view has focus, false otherwise.
*/
@ViewDebug.ExportedProperty(category = "focus")
@InspectableProperty(hasAttributeId = false)
public boolean isFocused() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0;
}
/**
* Find the view in the hierarchy rooted at this view that currently has
* focus.
*
* @return The view that currently has focus, or null if no focused view can
* be found.
*/
public View findFocus() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0 ? this : null;
}
/**
* Indicates whether this view is one of the set of scrollable containers in
* its window.
*
* @return whether this view is one of the set of scrollable containers in
* its window
*
* @attr ref android.R.styleable#View_isScrollContainer
*/
@InspectableProperty(name = "isScrollContainer")
public boolean isScrollContainer() {
return (mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0;
}
/**
* Change whether this view is one of the set of scrollable containers in
* its window. This will be used to determine whether the window can
* resize or must pan when a soft input area is open -- scrollable
* containers allow the window to use resize mode since the container
* will appropriately shrink.
*
* @attr ref android.R.styleable#View_isScrollContainer
*/
public void setScrollContainer(boolean isScrollContainer) {
if (isScrollContainer) {
if (mAttachInfo != null && (mPrivateFlags&PFLAG_SCROLL_CONTAINER_ADDED) == 0) {
mAttachInfo.mScrollContainers.add(this);
mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;
}
mPrivateFlags |= PFLAG_SCROLL_CONTAINER;
} else {
if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER_ADDED) != 0) {
mAttachInfo.mScrollContainers.remove(this);
}
mPrivateFlags &= ~(PFLAG_SCROLL_CONTAINER|PFLAG_SCROLL_CONTAINER_ADDED);
}
}
/**
* Returns the quality of the drawing cache.
*
* @return One of {@link #DRAWING_CACHE_QUALITY_AUTO},
* {@link #DRAWING_CACHE_QUALITY_LOW}, or {@link #DRAWING_CACHE_QUALITY_HIGH}
*
* @see #setDrawingCacheQuality(int)
* @see #setDrawingCacheEnabled(boolean)
* @see #isDrawingCacheEnabled()
*
* @attr ref android.R.styleable#View_drawingCacheQuality
*
* @deprecated The view drawing cache was largely made obsolete with the introduction of
* hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
* layers are largely unnecessary and can easily result in a net loss in performance due to the
* cost of creating and updating the layer. In the rare cases where caching layers are useful,
* such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
* rendering. For software-rendered snapshots of a small part of the View hierarchy or
* individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
* {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
* software-rendered usages are discouraged and have compatibility issues with hardware-only
* rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
* bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
* reports or unit testing the {@link PixelCopy} API is recommended.
*/
@Deprecated
@DrawingCacheQuality
@InspectableProperty(enumMapping = {
@EnumEntry(value = DRAWING_CACHE_QUALITY_LOW, name = "low"),
@EnumEntry(value = DRAWING_CACHE_QUALITY_HIGH, name = "high"),
@EnumEntry(value = DRAWING_CACHE_QUALITY_AUTO, name = "auto")
})
public int getDrawingCacheQuality() {
return mViewFlags & DRAWING_CACHE_QUALITY_MASK;
}
/**
* Set the drawing cache quality of this view. This value is used only when the
* drawing cache is enabled
*
* @param quality One of {@link #DRAWING_CACHE_QUALITY_AUTO},
* {@link #DRAWING_CACHE_QUALITY_LOW}, or {@link #DRAWING_CACHE_QUALITY_HIGH}
*
* @see #getDrawingCacheQuality()
* @see #setDrawingCacheEnabled(boolean)
* @see #isDrawingCacheEnabled()
*
* @attr ref android.R.styleable#View_drawingCacheQuality
*
* @deprecated The view drawing cache was largely made obsolete with the introduction of
* hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
* layers are largely unnecessary and can easily result in a net loss in performance due to the
* cost of creating and updating the layer. In the rare cases where caching layers are useful,
* such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
* rendering. For software-rendered snapshots of a small part of the View hierarchy or
* individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
* {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
* software-rendered usages are discouraged and have compatibility issues with hardware-only
* rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
* bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
* reports or unit testing the {@link PixelCopy} API is recommended.
*/
@Deprecated
public void setDrawingCacheQuality(@DrawingCacheQuality int quality) {
setFlags(quality, DRAWING_CACHE_QUALITY_MASK);
}
/**
* Returns whether the screen should remain on, corresponding to the current
* value of {@link #KEEP_SCREEN_ON}.
*
* @return Returns true if {@link #KEEP_SCREEN_ON} is set.
*
* @see #setKeepScreenOn(boolean)
*
* @attr ref android.R.styleable#View_keepScreenOn
*/
@InspectableProperty
public boolean getKeepScreenOn() {
return (mViewFlags & KEEP_SCREEN_ON) != 0;
}
/**
* Controls whether the screen should remain on, modifying the
* value of {@link #KEEP_SCREEN_ON}.
*
* @param keepScreenOn Supply true to set {@link #KEEP_SCREEN_ON}.
*
* @see #getKeepScreenOn()
*
* @attr ref android.R.styleable#View_keepScreenOn
*/
public void setKeepScreenOn(boolean keepScreenOn) {
setFlags(keepScreenOn ? KEEP_SCREEN_ON : 0, KEEP_SCREEN_ON);
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_LEFT}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusLeft
*/
@IdRes
@InspectableProperty(name = "nextFocusLeft")
public int getNextFocusLeftId() {
return mNextFocusLeftId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_LEFT}.
* @param nextFocusLeftId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusLeft
*/
public void setNextFocusLeftId(@IdRes int nextFocusLeftId) {
mNextFocusLeftId = nextFocusLeftId;
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusRight
*/
@IdRes
@InspectableProperty(name = "nextFocusRight")
public int getNextFocusRightId() {
return mNextFocusRightId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_RIGHT}.
* @param nextFocusRightId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusRight
*/
public void setNextFocusRightId(@IdRes int nextFocusRightId) {
mNextFocusRightId = nextFocusRightId;
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_UP}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusUp
*/
@IdRes
@InspectableProperty(name = "nextFocusUp")
public int getNextFocusUpId() {
return mNextFocusUpId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_UP}.
* @param nextFocusUpId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusUp
*/
public void setNextFocusUpId(@IdRes int nextFocusUpId) {
mNextFocusUpId = nextFocusUpId;
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_DOWN}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusDown
*/
@IdRes
@InspectableProperty(name = "nextFocusDown")
public int getNextFocusDownId() {
return mNextFocusDownId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_DOWN}.
* @param nextFocusDownId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusDown
*/
public void setNextFocusDownId(@IdRes int nextFocusDownId) {
mNextFocusDownId = nextFocusDownId;
}
/**
* Gets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}.
* @return The next focus ID, or {@link #NO_ID} if the framework should decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusForward
*/
@IdRes
@InspectableProperty(name = "nextFocusForward")
public int getNextFocusForwardId() {
return mNextFocusForwardId;
}
/**
* Sets the id of the view to use when the next focus is {@link #FOCUS_FORWARD}.
* @param nextFocusForwardId The next focus ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextFocusForward
*/
public void setNextFocusForwardId(@IdRes int nextFocusForwardId) {
mNextFocusForwardId = nextFocusForwardId;
}
/**
* Gets the id of the root of the next keyboard navigation cluster.
* @return The next keyboard navigation cluster ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextClusterForward
*/
@IdRes
@InspectableProperty(name = "nextClusterForward")
public int getNextClusterForwardId() {
return mNextClusterForwardId;
}
/**
* Sets the id of the view to use as the root of the next keyboard navigation cluster.
* @param nextClusterForwardId The next cluster ID, or {@link #NO_ID} if the framework should
* decide automatically.
*
* @attr ref android.R.styleable#View_nextClusterForward
*/
public void setNextClusterForwardId(@IdRes int nextClusterForwardId) {
mNextClusterForwardId = nextClusterForwardId;
}
/**
* Returns the visibility of this view and all of its ancestors
*
* @return True if this view and all of its ancestors are {@link #VISIBLE}
*/
public boolean isShown() {
View current = this;
//noinspection ConstantConditions
do {
if ((current.mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
ViewParent parent = current.mParent;
if (parent == null) {
return false; // We are not attached to the view root
}
if (!(parent instanceof View)) {
return true;
}
current = (View) parent;
} while (current != null);
return false;
}
private boolean detached() {
View current = this;
//noinspection ConstantConditions
do {
if ((current.mPrivateFlags4 & PFLAG4_DETACHED) != 0) {
return true;
}
ViewParent parent = current.mParent;
if (parent == null) {
return false;
}
if (!(parent instanceof View)) {
return false;
}
current = (View) parent;
} while (current != null);
return false;
}
/**
* Called by the view hierarchy when the content insets for a window have
* changed, to allow it to adjust its content to fit within those windows.
* The content insets tell you the space that the status bar, input method,
* and other system windows infringe on the application's window.
*
* You do not normally need to deal with this function, since the default
* window decoration given to applications takes care of applying it to the
* content of the window. If you use {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}
* or {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION} this will not be the case,
* and your content can be placed under those system elements. You can then
* use this method within your view hierarchy if you have parts of your UI
* which you would like to ensure are not being covered.
*
* The default implementation of this method simply applies the content
* insets to the view's padding, consuming that content (modifying the
* insets to be 0), and returning true. This behavior is off by default, but can
* be enabled through {@link #setFitsSystemWindows(boolean)}.
*
* This function's traversal down the hierarchy is depth-first. The same content
* insets object is propagated down the hierarchy, so any changes made to it will
* be seen by all following views (including potentially ones above in
* the hierarchy since this is a depth-first traversal). The first view
* that returns true will abort the entire traversal.
*
* The default implementation works well for a situation where it is
* used with a container that covers the entire window, allowing it to
* apply the appropriate insets to its content on all edges. If you need
* a more complicated layout (such as two different views fitting system
* windows, one on the top of the window, and one on the bottom),
* you can override the method and handle the insets however you would like.
* Note that the insets provided by the framework are always relative to the
* far edges of the window, not accounting for the location of the called view
* within that window. (In fact when this method is called you do not yet know
* where the layout will place the view, as it is done before layout happens.)
*
* Note: unlike many View methods, there is no dispatch phase to this
* call. If you are overriding it in a ViewGroup and want to allow the
* call to continue to your children, you must be sure to call the super
* implementation.
*
* Here is a sample layout that makes use of fitting system windows
* to have controls for a video view placed inside of the window decorations
* that it hides and shows. This can be used with code like the second
* sample (video player) shown in {@link #setSystemUiVisibility(int)}.
*
* {@sample development/samples/ApiDemos/res/layout/video_player.xml complete}
*
* @param insets Current content insets of the window. Prior to
* {@link android.os.Build.VERSION_CODES#JELLY_BEAN} you must not modify
* the insets or else you and Android will be unhappy.
*
* @return {@code true} if this view applied the insets and it should not
* continue propagating further down the hierarchy, {@code false} otherwise.
* @see #getFitsSystemWindows()
* @see #setFitsSystemWindows(boolean)
* @see #setSystemUiVisibility(int)
*
* @deprecated As of API 20 use {@link #dispatchApplyWindowInsets(WindowInsets)} to apply
* insets to views. Views should override {@link #onApplyWindowInsets(WindowInsets)} or use
* {@link #setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener)}
* to implement handling their own insets.
*/
@Deprecated
protected boolean fitSystemWindows(Rect insets) {
if ((mPrivateFlags3 & PFLAG3_APPLYING_INSETS) == 0) {
if (insets == null) {
// Null insets by definition have already been consumed.
// This call cannot apply insets since there are none to apply,
// so return false.
return false;
}
// If we're not in the process of dispatching the newer apply insets call,
// that means we're not in the compatibility path. Dispatch into the newer
// apply insets path and take things from there.
try {
mPrivateFlags3 |= PFLAG3_FITTING_SYSTEM_WINDOWS;
return dispatchApplyWindowInsets(new WindowInsets(insets)).isConsumed();
} finally {
mPrivateFlags3 &= ~PFLAG3_FITTING_SYSTEM_WINDOWS;
}
} else {
// We're being called from the newer apply insets path.
// Perform the standard fallback behavior.
return fitSystemWindowsInt(insets);
}
}
private boolean fitSystemWindowsInt(Rect insets) {
if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
Rect localInsets = sThreadLocal.get();
boolean res = computeFitSystemWindows(insets, localInsets);
applyInsets(localInsets);
return res;
}
return false;
}
private void applyInsets(Rect insets) {
mUserPaddingStart = UNDEFINED_PADDING;
mUserPaddingEnd = UNDEFINED_PADDING;
mUserPaddingLeftInitial = insets.left;
mUserPaddingRightInitial = insets.right;
internalSetPadding(insets.left, insets.top, insets.right, insets.bottom);
}
/**
* Called when the view should apply {@link WindowInsets} according to its internal policy.
*
* This method should be overridden by views that wish to apply a policy different from or
* in addition to the default behavior. Clients that wish to force a view subtree
* to apply insets should call {@link #dispatchApplyWindowInsets(WindowInsets)}. Clients may supply an {@link OnApplyWindowInsetsListener} to a view. If one is set
* it will be called during dispatch instead of this method. The listener may optionally
* call this method from its own implementation if it wishes to apply the view's default
* insets policy in addition to its own. Implementations of this method should either return the insets parameter unchanged
* or a new {@link WindowInsets} cloned from the supplied insets with any insets consumed
* that this view applied itself. This allows new inset types added in future platform
* versions to pass through existing implementations unchanged without being erroneously
* consumed. By default if a view's {@link #setFitsSystemWindows(boolean) fitsSystemWindows}
* property is set then the view will consume the system window insets and apply them
* as padding for the view. This method should be called by clients wishing to apply insets corresponding to areas
* obscured by window decorations or overlays. This can include the status and navigation bars,
* action bars, input methods and more. New inset categories may be added in the future.
* The method returns the insets provided minus any that were applied by this view or its
* children. Clients wishing to provide custom behavior should override the
* {@link #onApplyWindowInsets(WindowInsets)} method or alternatively provide a
* {@link OnApplyWindowInsetsListener} via the
* {@link #setOnApplyWindowInsetsListener(View.OnApplyWindowInsetsListener) setOnApplyWindowInsetsListener}
* method. This method replaces the older {@link #fitSystemWindows(Rect) fitSystemWindows} method.
*
* The callback's {@link WindowInsetsAnimation.Callback#getDispatchMode()
* dispatch mode} will affect whether animation callbacks are dispatched to the children of
* this view.
* Use this to tell the system which specific sub-areas of a view need to receive gesture
* input in order to function correctly in the presence of global system gestures that may
* conflict. For example, if the system wishes to capture swipe-in-from-screen-edge gestures
* to provide system-level navigation functionality, a view such as a navigation drawer
* container can mark the left (or starting) edge of itself as requiring gesture capture
* priority using this API. The system may then choose to relax its own gesture recognition
* to allow the app to consume the user's gesture. It is not necessary for an app to register
* exclusion rects for broadly spanning regions such as the entirety of a
* Note: the system will put a limit of Do not modify the returned list.
* The system will try to respect this preference, but when not possible will ignore it.
*
* Note: This is independent from {@link #setPreferKeepClearRects}. If both are set, both will
* be taken into account.
*
* @see #setPreferKeepClearRects
* @see #isPreferKeepClear
* @attr ref android.R.styleable#View_preferKeepClear
*/
public final void setPreferKeepClear(boolean preferKeepClear) {
getListenerInfo().mPreferKeepClear = preferKeepClear;
updatePositionUpdateListener();
postUpdate(this::updateKeepClearRects);
}
/**
* Retrieve the preference for this view to be kept clear. This is set either by
* {@link #setPreferKeepClear} or via the attribute android.R.styleable#View_preferKeepClear.
*
* If this is {@code true}, the system will ignore the Rects set by
* {@link #setPreferKeepClearRects} and try to keep the whole view clear.
*
* @see #setPreferKeepClear
* @attr ref android.R.styleable#View_preferKeepClear
*/
public final boolean isPreferKeepClear() {
return mListenerInfo != null && mListenerInfo.mPreferKeepClear;
}
/**
* Set a preference to keep the provided rects clear from floating windows above this
* view's window. This informs the system that these rects are considered vital areas for the
* user and that ideally they should not be covered. Setting this is only appropriate for UI
* where the user would likely take action to uncover it.
*
* The system will try to respect this preference, but when not possible will ignore it.
*
* Note: This is independent from {@link #setPreferKeepClear}. If both are set, both will be
* taken into account.
*
* @see #setPreferKeepClear
* @see #getPreferKeepClearRects
*
* @param rects A list of rects in this view's local coordinate system
*/
public final void setPreferKeepClearRects(@NonNull List
* Note: The difference with {@link #setPreferKeepClearRects} is that the system won't apply
* restrictions to the rects set here.
*
* @see #setPreferKeepClear
* @see #getPreferKeepClearRects
*
* @param rects A list of rects in this view's local coordinate system
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS)
public final void setUnrestrictedPreferKeepClearRects(@NonNull List This method is mainly used to enlarge the view's handwriting bounds for a better user
* experience.
* Note that when the view is clipped (e.g. the view is in a
* {@link android.widget.ScrollView}), the offsets are applied after the view's handwriting
* bounds is clipped.
*
* @param offsetLeft the amount of pixel offset applied to the left edge outwards of the view's
* handwriting bounds.
* @param offsetTop the amount of pixel offset applied to the top edge outwards of the view's
* handwriting bounds.
* @param offsetRight the amount of pixel offset applied to the right edge outwards of the
* view's handwriting bounds.
* @param offsetBottom the amount of pixel offset applied to the bottom edge outwards of the
* view's handwriting bounds.
*
* @see #setAutoHandwritingEnabled(boolean)
* @see #getHandwritingBoundsOffsetLeft()
* @see #getHandwritingBoundsOffsetTop()
* @see #getHandwritingBoundsOffsetRight()
* @see #getHandwritingBoundsOffsetBottom()
*/
public void setHandwritingBoundsOffsets(float offsetLeft, float offsetTop,
float offsetRight, float offsetBottom) {
mHandwritingBoundsOffsetLeft = offsetLeft;
mHandwritingBoundsOffsetTop = offsetTop;
mHandwritingBoundsOffsetRight = offsetRight;
mHandwritingBoundsOffsetBottom = offsetBottom;
}
/**
* Return the amount of offset applied to the left edge of this view's handwriting bounds,
* in the unit of pixel.
*
* @see #setAutoHandwritingEnabled(boolean)
* @see #setHandwritingBoundsOffsets(float, float, float, float)
*/
public float getHandwritingBoundsOffsetLeft() {
return mHandwritingBoundsOffsetLeft;
}
/**
* Return the amount of offset applied to the top edge of this view's handwriting bounds,
* in the unit of pixel.
*
* @see #setAutoHandwritingEnabled(boolean)
* @see #setHandwritingBoundsOffsets(float, float, float, float)
*/
public float getHandwritingBoundsOffsetTop() {
return mHandwritingBoundsOffsetTop;
}
/**
* Return the amount of offset applied to the right edge of this view's handwriting bounds, in
* the unit of pixel.
*
* @see #setAutoHandwritingEnabled(boolean)
* @see #setHandwritingBoundsOffsets(float, float, float, float)
*/
public float getHandwritingBoundsOffsetRight() {
return mHandwritingBoundsOffsetRight;
}
/**
* Return the amount of offset applied to the bottom edge of this view's handwriting bounds, in
* the unit of pixel.
*
* @see #setAutoHandwritingEnabled(boolean)
* @see #setHandwritingBoundsOffsets(float, float, float, float)
*/
public float getHandwritingBoundsOffsetBottom() {
return mHandwritingBoundsOffsetBottom;
}
/**
* Set a handwriting area in this view. If there is any stylus {@link MotionEvent}
* occurs within this area, it will trigger stylus handwriting mode. This can be disabled by
* disabling the auto handwriting initiation by calling
* {@link #setAutoHandwritingEnabled(boolean)} with false.
*
* @attr rect the handwriting area in the view's local coordiniates.
*
* @see android.view.inputmethod.InputMethodManager#startStylusHandwriting(View)
* @see #setAutoHandwritingEnabled(boolean)
*
* @hide
*/
public void setHandwritingArea(@Nullable Rect rect) {
final ListenerInfo info = getListenerInfo();
info.mHandwritingArea = rect;
updatePositionUpdateListener();
postUpdate(this::updateHandwritingArea);
}
/**
* Return the handwriting areas set on this view, in its local coordinates.
* @see #setHandwritingArea(Rect)
*
* @hide
*/
@Nullable
public Rect getHandwritingArea() {
final ListenerInfo info = mListenerInfo;
if (info != null && info.mHandwritingArea != null) {
return new Rect(info.mHandwritingArea);
}
return null;
}
void updateHandwritingArea() {
// If autoHandwritingArea is not enabled, do nothing.
if (!shouldTrackHandwritingArea()) return;
final AttachInfo ai = mAttachInfo;
if (ai != null) {
ai.mViewRootImpl.getHandwritingInitiator().updateHandwritingAreasForView(this);
}
}
/**
* Returns true if a stylus {@link MotionEvent} within this view's bounds should initiate
* handwriting mode, either for this view ({@link #isAutoHandwritingEnabled()} is {@code true})
* or for a handwriting delegate view ({@link #getHandwritingDelegatorCallback()} is not {@code
* null}).
*/
boolean shouldInitiateHandwriting() {
return isAutoHandwritingEnabled() || getHandwritingDelegatorCallback() != null;
}
/**
* Returns whether the handwriting initiator should track the handwriting area for this view,
* either to initiate handwriting mode, or to prepare handwriting delegation, or to show the
* handwriting unsupported message.
* @hide
*/
public boolean shouldTrackHandwritingArea() {
return shouldInitiateHandwriting();
}
/**
* Sets a callback which should be called when a stylus {@link MotionEvent} occurs within this
* view's bounds. The callback will be called from the UI thread.
*
* Setting a callback allows this view to act as a handwriting delegator, so that handwriting
* mode for a delegate editor view can be initiated by stylus movement on this delegator view.
* The callback implementation is expected to show and focus the delegate editor view. If a view
* which returns {@code true} for {@link #isHandwritingDelegate()} creates an input connection
* while the same stylus {@link MotionEvent} sequence is ongoing, handwriting mode will be
* initiated for that view.
*
* A common use case is a custom view which looks like a text editor but does not actually
* support text editing itself, and clicking on the custom view causes an EditText to be shown.
* To support handwriting initiation in this case, this method can be called on the custom view
* to configure it as a delegator. The EditText should call {@link #setIsHandwritingDelegate} to
* set it as a delegate. The {@code callback} implementation is typically the same as the click
* listener implementation which shows the EditText.
*
* If {@code null} is passed, this view will no longer act as a handwriting initiation
* delegator.
*
* @param callback a callback which should be called when a stylus {@link MotionEvent} occurs
* within this view's bounds
*/
public void setHandwritingDelegatorCallback(@Nullable Runnable callback) {
mHandwritingDelegatorCallback = callback;
if (callback != null) {
setHandwritingArea(new Rect(0, 0, getWidth(), getHeight()));
}
}
/**
* Returns the callback set by {@link #setHandwritingDelegatorCallback} which should be called
* when a stylus {@link MotionEvent} occurs within this view's bounds. The callback should only
* be called from the UI thread.
*/
@Nullable
public Runnable getHandwritingDelegatorCallback() {
return mHandwritingDelegatorCallback;
}
/**
* Specifies that this view may act as a handwriting initiation delegator for a delegate editor
* view from the specified package. If this method is not called, delegators may only be used to
* initiate handwriting mode for a delegate editor view from the same package as the delegator
* view. This method allows specifying a different trusted package which may contain a delegate
* editor view linked to this delegator view.
*
* This method has no effect unless {@link #setHandwritingDelegatorCallback} is also called
* to configure this view to act as a handwriting delegator.
*
* If this method is called on the delegator view, then {@link
* #setAllowedHandwritingDelegatorPackage} should also be called on the delegate editor view.
*
* For example, to configure a delegator view in package 1:
*
* This method has no effect unless {@link #setIsHandwritingDelegate} is also called to
* configure this view to act as a handwriting delegate.
*
* If this method is called on the delegate editor view, then {@link
* #setAllowedHandwritingDelegatePackage} should also be called on the delegator view.
*
* @param allowedPackageName the package name of a delegator view linked to this delegate editor
* view, or {@code null} to restore the default behavior of only allowing delegator views
* from the same package as this delegate editor view
*/
public void setAllowedHandwritingDelegatorPackage(@Nullable String allowedPackageName) {
mAllowedHandwritingDelegatorPackageName = allowedPackageName;
}
/**
* Returns the allowed package for views which may act as a handwriting delegator for this
* delegate editor view, as set by {@link #setAllowedHandwritingDelegatorPackage}. If {@link
* #setAllowedHandwritingDelegatorPackage} has not been called, or called with {@code null}
* argument, this will return {@code null}, meaning that only views from the same package as
* this delegator editor view may act as a handwriting delegator.
*/
@Nullable
public String getAllowedHandwritingDelegatorPackageName() {
return mAllowedHandwritingDelegatorPackageName;
}
/**
* Sets flags configuring the handwriting delegation behavior for this delegate editor view.
*
* This method has no effect unless {@link #setIsHandwritingDelegate} is also called to
* configure this view to act as a handwriting delegate.
*
* @param flags {@link InputMethodManager#HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED} or
* {@code 0}
*/
@FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
public void setHandwritingDelegateFlags(
@InputMethodManager.HandwritingDelegateFlags int flags) {
mHandwritingDelegateFlags = flags;
}
/**
* Returns flags configuring the handwriting delegation behavior for this delegate editor view,
* as set by {@link #setHandwritingDelegateFlags}.
*/
@FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
public @InputMethodManager.HandwritingDelegateFlags int getHandwritingDelegateFlags() {
return mHandwritingDelegateFlags;
}
/**
* Gets the coordinates of this view in the coordinate space of the
* {@link Surface} that contains the view.
*
* In multiple-screen scenarios, if the surface spans multiple screens,
* the coordinate space of the surface also spans multiple screens.
*
* After the method returns, the argument array contains the x and y
* coordinates of the view relative to the view's left and top edges,
* respectively.
*
* @param location A two-element integer array in which the view coordinates
* are stored. The x-coordinate is at index 0; the y-coordinate, at
* index 1.
*/
public void getLocationInSurface(@NonNull @Size(2) int[] location) {
getLocationInWindow(location);
if (mAttachInfo != null && mAttachInfo.mViewRootImpl != null) {
location[0] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.left;
location[1] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.top;
}
}
/**
* Provide original WindowInsets that are dispatched to the view hierarchy. The insets are
* only available if the view is attached.
*
* @return WindowInsets from the top of the view hierarchy or null if View is detached
*/
public WindowInsets getRootWindowInsets() {
if (mAttachInfo != null) {
return mAttachInfo.mViewRootImpl.getWindowInsets(false /* forceConstruct */);
}
return null;
}
/**
* Retrieves the single {@link WindowInsetsController} of the window this view is attached to.
*
* @return The {@link WindowInsetsController} or {@code null} if the view is neither attached to
* a window nor a view tree with a decor.
* @see Window#getInsetsController()
*/
public @Nullable WindowInsetsController getWindowInsetsController() {
if (mAttachInfo != null) {
return mAttachInfo.mViewRootImpl.getInsetsController();
}
ViewParent parent = getParent();
if (parent instanceof View) {
return ((View) parent).getWindowInsetsController();
} else if (parent instanceof ViewRootImpl) {
// Between WindowManager.addView() and the first traversal AttachInfo isn't set yet.
return ((ViewRootImpl) parent).getInsetsController();
}
return null;
}
/**
* Walk up the View hierarchy to find the nearest {@link OnBackInvokedDispatcher}.
*
* @return The {@link OnBackInvokedDispatcher} from this or the nearest
* ancestor, or null if this view is both not attached and have no ancestor providing an
* {@link OnBackInvokedDispatcher}.
*/
@Nullable
public final OnBackInvokedDispatcher findOnBackInvokedDispatcher() {
ViewParent parent = getParent();
if (parent != null) {
return parent.findOnBackInvokedDispatcherForChild(this, this);
}
return null;
}
/**
* @hide Compute the insets that should be consumed by this view and the ones
* that should propagate to those under it.
*
* Note: This is used by appcompat's ActionBarOverlayLayout through reflection.
*
* @param inoutInsets the insets given to this view
* @param outLocalInsets the insets that should be applied to this view
* @deprecated use {@link #computeSystemWindowInsets}
* @return
*/
@Deprecated
@UnsupportedAppUsage
protected boolean computeFitSystemWindows(Rect inoutInsets, Rect outLocalInsets) {
WindowInsets innerInsets = computeSystemWindowInsets(new WindowInsets(inoutInsets),
outLocalInsets);
inoutInsets.set(innerInsets.getSystemWindowInsetsAsRect());
return innerInsets.isSystemWindowInsetsConsumed();
}
/**
* Compute insets that should be consumed by this view and the ones that should propagate
* to those under it.
*
* @param in Insets currently being processed by this View, likely received as a parameter
* to {@link #onApplyWindowInsets(WindowInsets)}.
* @param outLocalInsets A Rect that will receive the insets that should be consumed
* by this view
* @return Insets that should be passed along to views under this one
*/
public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) {
boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
|| (mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0;
if (isOptionalFitSystemWindows && mAttachInfo != null) {
OnContentApplyWindowInsetsListener listener =
mAttachInfo.mContentOnApplyWindowInsetsListener;
if (listener == null) {
// The application wants to take care of fitting system window for
// the content.
outLocalInsets.setEmpty();
return in;
}
Pair Note that if you are providing your own implementation of
* {@link #fitSystemWindows(Rect)}, then there is no need to set this
* flag to true -- your implementation will be overriding the default
* implementation that checks this flag.
*
* @param fitSystemWindows If true, then the default implementation of
* {@link #fitSystemWindows(Rect)} will be executed.
*
* @attr ref android.R.styleable#View_fitsSystemWindows
* @see #getFitsSystemWindows()
* @see #fitSystemWindows(Rect)
* @see #setSystemUiVisibility(int)
*/
public void setFitsSystemWindows(boolean fitSystemWindows) {
setFlags(fitSystemWindows ? FITS_SYSTEM_WINDOWS : 0, FITS_SYSTEM_WINDOWS);
}
/**
* Check for state of {@link #setFitsSystemWindows(boolean)}. If this method
* returns {@code true}, the default implementation of {@link #fitSystemWindows(Rect)}
* will be executed.
*
* @return {@code true} if the default implementation of
* {@link #fitSystemWindows(Rect)} will be executed.
*
* @attr ref android.R.styleable#View_fitsSystemWindows
* @see #setFitsSystemWindows(boolean)
* @see #fitSystemWindows(Rect)
* @see #setSystemUiVisibility(int)
*/
@ViewDebug.ExportedProperty
@InspectableProperty
public boolean getFitsSystemWindows() {
return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
}
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean fitsSystemWindows() {
return getFitsSystemWindows();
}
/**
* Ask that a new dispatch of {@link #fitSystemWindows(Rect)} be performed.
* @deprecated Use {@link #requestApplyInsets()} for newer platform versions.
*/
@Deprecated
public void requestFitSystemWindows() {
if (mParent != null) {
mParent.requestFitSystemWindows();
}
}
/**
* Ask that a new dispatch of {@link #onApplyWindowInsets(WindowInsets)} be performed.
*/
public void requestApplyInsets() {
requestFitSystemWindows();
}
/**
* @see #OPTIONAL_FITS_SYSTEM_WINDOWS
* @hide
*/
@UnsupportedAppUsage
public void makeOptionalFitsSystemWindows() {
setFlags(OPTIONAL_FITS_SYSTEM_WINDOWS, OPTIONAL_FITS_SYSTEM_WINDOWS);
}
/**
* @see #PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS
* @hide
*/
public void makeFrameworkOptionalFitsSystemWindows() {
mPrivateFlags4 |= PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS;
}
/**
* @hide
*/
public boolean isFrameworkOptionalFitsSystemWindows() {
return (mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0;
}
/**
* Returns the visibility status for this view.
*
* @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
* @attr ref android.R.styleable#View_visibility
*/
@ViewDebug.ExportedProperty(mapping = {
@ViewDebug.IntToString(from = VISIBLE, to = "VISIBLE"),
@ViewDebug.IntToString(from = INVISIBLE, to = "INVISIBLE"),
@ViewDebug.IntToString(from = GONE, to = "GONE")
})
@InspectableProperty(enumMapping = {
@EnumEntry(value = VISIBLE, name = "visible"),
@EnumEntry(value = INVISIBLE, name = "invisible"),
@EnumEntry(value = GONE, name = "gone")
})
@Visibility
public int getVisibility() {
return mViewFlags & VISIBILITY_MASK;
}
/**
* Set the visibility state of this view.
*
* @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
* @attr ref android.R.styleable#View_visibility
*/
@RemotableViewMethod
public void setVisibility(@Visibility int visibility) {
setFlags(visibility, VISIBILITY_MASK);
}
/**
* Returns the enabled status for this view. The interpretation of the
* enabled state varies by subclass.
*
* @return True if this view is enabled, false otherwise.
*/
@ViewDebug.ExportedProperty
@InspectableProperty
public boolean isEnabled() {
return (mViewFlags & ENABLED_MASK) == ENABLED;
}
/**
* Set the enabled state of this view. The interpretation of the enabled
* state varies by subclass.
*
* @param enabled True if this view is enabled, false otherwise.
*/
@RemotableViewMethod
public void setEnabled(boolean enabled) {
if (enabled == isEnabled()) return;
setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);
/*
* The View most likely has to change its appearance, so refresh
* the drawable state.
*/
refreshDrawableState();
// Invalidate too, since the default behavior for views is to be
// be drawn at 50% alpha rather than to change the drawable.
invalidate(true);
if (!enabled) {
cancelPendingInputEvents();
}
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED);
}
/**
* Set whether this view can receive the focus.
*
* Setting this to false will also ensure that this view is not focusable
* in touch mode.
*
* @param focusable If true, this view can receive the focus.
*
* @see #setFocusableInTouchMode(boolean)
* @see #setFocusable(int)
* @attr ref android.R.styleable#View_focusable
*/
@RemotableViewMethod
public void setFocusable(boolean focusable) {
setFocusable(focusable ? FOCUSABLE : NOT_FOCUSABLE);
}
/**
* Sets whether this view can receive focus.
*
* Setting this to {@link #FOCUSABLE_AUTO} tells the framework to determine focusability
* automatically based on the view's interactivity. This is the default.
*
* Setting this to NOT_FOCUSABLE will ensure that this view is also not focusable
* in touch mode.
*
* @param focusable One of {@link #NOT_FOCUSABLE}, {@link #FOCUSABLE},
* or {@link #FOCUSABLE_AUTO}.
* @see #setFocusableInTouchMode(boolean)
* @attr ref android.R.styleable#View_focusable
*/
@RemotableViewMethod
public void setFocusable(@Focusable int focusable) {
if ((focusable & (FOCUSABLE_AUTO | FOCUSABLE)) == 0) {
setFlags(0, FOCUSABLE_IN_TOUCH_MODE);
}
setFlags(focusable, FOCUSABLE_MASK);
}
/**
* Set whether this view can receive focus while in touch mode.
*
* Setting this to true will also ensure that this view is focusable.
*
* @param focusableInTouchMode If true, this view can receive the focus while
* in touch mode.
*
* @see #setFocusable(boolean)
* @attr ref android.R.styleable#View_focusableInTouchMode
*/
@RemotableViewMethod
public void setFocusableInTouchMode(boolean focusableInTouchMode) {
// Focusable in touch mode should always be set before the focusable flag
// otherwise, setting the focusable flag will trigger a focusableViewAvailable()
// which, in touch mode, will not successfully request focus on this view
// because the focusable in touch mode flag is not set
setFlags(focusableInTouchMode ? FOCUSABLE_IN_TOUCH_MODE : 0, FOCUSABLE_IN_TOUCH_MODE);
// Clear FOCUSABLE_AUTO if set.
if (focusableInTouchMode) {
// Clears FOCUSABLE_AUTO if set.
setFlags(FOCUSABLE, FOCUSABLE_MASK);
}
}
/**
* Sets the hints that help an {@link android.service.autofill.AutofillService} determine how
* to autofill the view with the user's data.
*
* Typically, there is only one way to autofill a view, but there could be more than one.
* For example, if the application accepts either an username or email address to identify
* an user.
*
* These hints are not validated by the Android System, but passed "as is" to the service.
* Hence, they can have any value, but it's recommended to use the {@code AUTOFILL_HINT_}
* constants such as:
* {@link #AUTOFILL_HINT_USERNAME}, {@link #AUTOFILL_HINT_PASSWORD},
* {@link #AUTOFILL_HINT_EMAIL_ADDRESS},
* {@link #AUTOFILL_HINT_NAME},
* {@link #AUTOFILL_HINT_PHONE},
* {@link #AUTOFILL_HINT_POSTAL_ADDRESS}, {@link #AUTOFILL_HINT_POSTAL_CODE},
* {@link #AUTOFILL_HINT_CREDIT_CARD_NUMBER}, {@link #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE},
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE},
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH} or
* {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}.
*
* @param autofillHints The autofill hints to set. If the array is emtpy, {@code null} is set.
* @attr ref android.R.styleable#View_autofillHints
*/
public void setAutofillHints(@Nullable String... autofillHints) {
if (autofillHints == null || autofillHints.length == 0) {
mAutofillHints = null;
} else {
mAutofillHints = autofillHints;
}
if (sensitiveContentAppProtection()) {
if (getContentSensitivity() == CONTENT_SENSITIVITY_AUTO) {
updateSensitiveViewsCountIfNeeded(isAggregatedVisible());
}
}
}
/**
* @hide
*/
@TestApi
public void setAutofilled(boolean isAutofilled, boolean hideHighlight) {
boolean wasChanged = isAutofilled != isAutofilled();
if (wasChanged) {
if (isAutofilled) {
mPrivateFlags3 |= PFLAG3_IS_AUTOFILLED;
} else {
mPrivateFlags3 &= ~PFLAG3_IS_AUTOFILLED;
}
if (hideHighlight) {
mPrivateFlags4 |= PFLAG4_AUTOFILL_HIDE_HIGHLIGHT;
} else {
mPrivateFlags4 &= ~PFLAG4_AUTOFILL_HIDE_HIGHLIGHT;
}
invalidate();
}
}
/**
* Set whether this view should have sound effects enabled for events such as
* clicking and touching.
*
* You may wish to disable sound effects for a view if you already play sounds,
* for instance, a dial key that plays dtmf tones.
*
* @param soundEffectsEnabled whether sound effects are enabled for this view.
* @see #isSoundEffectsEnabled()
* @see #playSoundEffect(int)
* @attr ref android.R.styleable#View_soundEffectsEnabled
*/
public void setSoundEffectsEnabled(boolean soundEffectsEnabled) {
setFlags(soundEffectsEnabled ? SOUND_EFFECTS_ENABLED: 0, SOUND_EFFECTS_ENABLED);
}
/**
* @return whether this view should have sound effects enabled for events such as
* clicking and touching.
*
* @see #setSoundEffectsEnabled(boolean)
* @see #playSoundEffect(int)
* @attr ref android.R.styleable#View_soundEffectsEnabled
*/
@ViewDebug.ExportedProperty
@InspectableProperty
public boolean isSoundEffectsEnabled() {
return SOUND_EFFECTS_ENABLED == (mViewFlags & SOUND_EFFECTS_ENABLED);
}
/**
* Set whether this view should have haptic feedback for events such as
* long presses.
*
* You may wish to disable haptic feedback if your view already controls
* its own haptic feedback.
*
* @param hapticFeedbackEnabled whether haptic feedback enabled for this view.
* @see #isHapticFeedbackEnabled()
* @see #performHapticFeedback(int)
* @attr ref android.R.styleable#View_hapticFeedbackEnabled
*/
public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) {
setFlags(hapticFeedbackEnabled ? HAPTIC_FEEDBACK_ENABLED: 0, HAPTIC_FEEDBACK_ENABLED);
}
/**
* @return whether this view should have haptic feedback enabled for events
* such as long presses.
*
* @see #setHapticFeedbackEnabled(boolean)
* @see #performHapticFeedback(int)
* @attr ref android.R.styleable#View_hapticFeedbackEnabled
*/
@ViewDebug.ExportedProperty
@InspectableProperty
public boolean isHapticFeedbackEnabled() {
return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED);
}
/**
* Returns the layout direction for this view.
*
* @return One of {@link #LAYOUT_DIRECTION_LTR},
* {@link #LAYOUT_DIRECTION_RTL},
* {@link #LAYOUT_DIRECTION_INHERIT} or
* {@link #LAYOUT_DIRECTION_LOCALE}.
*
* @attr ref android.R.styleable#View_layoutDirection
*
* @hide
*/
@ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "LTR"),
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RTL"),
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to = "INHERIT"),
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE, to = "LOCALE")
})
@InspectableProperty(hasAttributeId = false, enumMapping = {
@EnumEntry(value = LAYOUT_DIRECTION_LTR, name = "ltr"),
@EnumEntry(value = LAYOUT_DIRECTION_RTL, name = "rtl"),
@EnumEntry(value = LAYOUT_DIRECTION_INHERIT, name = "inherit"),
@EnumEntry(value = LAYOUT_DIRECTION_LOCALE, name = "locale")
})
@LayoutDir
public int getRawLayoutDirection() {
return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK) >> PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT;
}
/**
* Set the layout direction for this view. This will propagate a reset of layout direction
* resolution to the view's children and resolve layout direction for this view.
*
* @param layoutDirection the layout direction to set. Should be one of:
*
* {@link #LAYOUT_DIRECTION_LTR},
* {@link #LAYOUT_DIRECTION_RTL},
* {@link #LAYOUT_DIRECTION_INHERIT},
* {@link #LAYOUT_DIRECTION_LOCALE}.
*
* Resolution will be done if the value is set to LAYOUT_DIRECTION_INHERIT. The resolution
* proceeds up the parent chain of the view to get the value. If there is no parent, then it
* will return the default {@link #LAYOUT_DIRECTION_LTR}.
*
* @attr ref android.R.styleable#View_layoutDirection
*/
@RemotableViewMethod
public void setLayoutDirection(@LayoutDir int layoutDirection) {
if (getRawLayoutDirection() != layoutDirection) {
// Reset the current layout direction and the resolved one
mPrivateFlags2 &= ~PFLAG2_LAYOUT_DIRECTION_MASK;
resetRtlProperties();
// Set the new layout direction (filtered)
mPrivateFlags2 |=
((layoutDirection << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) & PFLAG2_LAYOUT_DIRECTION_MASK);
// We need to resolve all RTL properties as they all depend on layout direction
resolveRtlPropertiesIfNeeded();
requestLayout();
invalidate(true);
}
}
/**
* Returns the resolved layout direction for this view.
*
* @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
* {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL.
*
* For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version
* is lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}.
*
* @attr ref android.R.styleable#View_layoutDirection
*/
@ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"),
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
})
@InspectableProperty(enumMapping = {
@EnumEntry(value = LAYOUT_DIRECTION_LTR, name = "ltr"),
@EnumEntry(value = LAYOUT_DIRECTION_RTL, name = "rtl")
})
@ResolvedLayoutDir
public int getLayoutDirection() {
return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ==
PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}
/**
* Indicates whether or not this view's layout is right-to-left. This is resolved from
* layout attribute and/or the inherited value from the parent
*
* @return true if the layout is right-to-left.
*
* @hide
*/
@ViewDebug.ExportedProperty(category = "layout")
@UnsupportedAppUsage
public boolean isLayoutRtl() {
return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
/**
* Indicates whether the view is currently tracking transient state that the
* app should not need to concern itself with saving and restoring, but that
* the framework should take special note to preserve when possible.
*
* A view with transient state cannot be trivially rebound from an external
* data source, such as an adapter binding item views in a list. This may be
* because the view is performing an animation, tracking user selection
* of content, or similar. A view with transient state cannot be trivially rebound from an external
* data source, such as an adapter binding item views in a list. This may be
* because the view is performing an animation, tracking user selection
* of content, or similar.
* Note: Use
* {@link androidx.core.view.ViewCompat#setScreenReaderFocusable(View, boolean)}
* for backwards-compatibility.
* @see #setScreenReaderFocusable(boolean)
*
* @return Whether the view should be treated as a focusable unit by screen reader.
*
* @attr ref android.R.styleable#View_screenReaderFocusable
*/
@InspectableProperty
public boolean isScreenReaderFocusable() {
return (mPrivateFlags3 & PFLAG3_SCREEN_READER_FOCUSABLE) != 0;
}
/**
* Sets whether this View should be a focusable element for screen readers
* and include non-focusable Views from its subtree when providing feedback.
*
* Generic motion events with source class {@link InputDevice#SOURCE_CLASS_POINTER}
* are delivered to the view under the pointer. All other generic motion events are
* delivered to the focused view. Hover events are handled specially and are delivered
* to {@link #onHoverEvent(MotionEvent)} first.
*
* Do not call this method directly.
* Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead.
*
* Do not call this method directly.
* Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead.
*
* Do not call this method directly.
* Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead.
*
* Dispatches touch related pointer events to {@link #onTouchEvent(MotionEvent)} and all
* other events to {@link #onGenericMotionEvent(MotionEvent)}. This separation of concerns
* reinforces the invariant that {@link #onTouchEvent(MotionEvent)} is really about touches
* and should not be expected to handle other pointing device features.
*
* Key presses in software keyboards will generally NOT trigger this
* listener, although some may elect to do so in some situations. Do not
* rely on this to catch software key presses.
*
* @param keyCode a key code that represents the button pressed, from
* {@link android.view.KeyEvent}
* @param event the KeyEvent object that defines the button action
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode) && event.hasNoModifiers()) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
if (event.getRepeatCount() == 0) {
// Long clickable items don't necessarily have to be clickable.
final boolean clickable = (mViewFlags & CLICKABLE) == CLICKABLE
|| (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
if (clickable || (mViewFlags & TOOLTIP) == TOOLTIP) {
// For the purposes of menu anchoring and drawable hotspots,
// key events are considered to be at the center of the view.
final float x = getWidth() / 2f;
final float y = getHeight() / 2f;
if (clickable) {
setPressed(true, x, y);
}
checkForLongClick(
ViewConfiguration.getLongPressTimeout(),
x,
y,
// This is not a touch gesture -- do not classify it as one.
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION);
return true;
}
}
}
return false;
}
/**
* Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
* the event).
* Key presses in software keyboards will generally NOT trigger this listener,
* although some may elect to do so in some situations. Do not rely on this to
* catch software key presses.
*/
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
return false;
}
/**
* Default implementation of {@link KeyEvent.Callback#onKeyUp(int, KeyEvent)
* KeyEvent.Callback.onKeyUp()}: perform clicking of the view
* when {@link KeyEvent#KEYCODE_DPAD_CENTER}, {@link KeyEvent#KEYCODE_ENTER}
* or {@link KeyEvent#KEYCODE_SPACE} is released.
* Key presses in software keyboards will generally NOT trigger this listener,
* although some may elect to do so in some situations. Do not rely on this to
* catch software key presses.
*
* @param keyCode A key code that represents the button pressed, from
* {@link android.view.KeyEvent}.
* @param event The KeyEvent object that defines the button action.
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode) && event.hasNoModifiers()) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) {
setPressed(false);
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
if (!event.isCanceled()) {
return performClickInternal();
}
}
}
}
return false;
}
/**
* Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
* KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle
* the event).
* Key presses in software keyboards will generally NOT trigger this listener,
* although some may elect to do so in some situations. Do not rely on this to
* catch software key presses.
*
* @param keyCode A key code that represents the button pressed, from
* {@link android.view.KeyEvent}.
* @param repeatCount The number of times the action was made.
* @param event The KeyEvent object that defines the button action.
*/
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
return false;
}
/**
* Called on the focused view when a key shortcut event is not handled.
* Override this method to implement local key shortcuts for the View.
* Key shortcuts can also be implemented by setting the
* {@link MenuItem#setShortcut(char, char) shortcut} property of menu items.
*
* @param keyCode The value in event.getKeyCode().
* @param event Description of the key event.
* @return If you handled the event, return true. If you want to allow the
* event to be handled by the next receiver, return false.
*/
public boolean onKeyShortcut(int keyCode, KeyEvent event) {
return false;
}
/**
* Check whether the called view is a text editor, in which case it
* would make sense to automatically display a soft input window for
* it. Subclasses should override this if they implement
* {@link #onCreateInputConnection(EditorInfo)} to return true if
* a call on that method would return a non-null InputConnection, and
* they are really a first-class editor that the user would normally
* start typing on when the go into a window containing your view.
*
* The default implementation always returns false. This does
* not mean that its {@link #onCreateInputConnection(EditorInfo)}
* will not be called or the user can not otherwise perform edits on your
* view; it is just a hint to the system that this is not the primary
* purpose of this view.
*
* @return Returns true if this view is a text editor, else false.
*/
public boolean onCheckIsTextEditor() {
return false;
}
/**
* Create a new InputConnection for an InputMethod to interact
* with the view. The default implementation returns null, since it doesn't
* support input methods. You can override this to implement such support.
* This is only needed for views that take focus and text input.
*
* When implementing this, you probably also want to implement
* {@link #onCheckIsTextEditor()} to indicate you will return a
* non-null InputConnection. Also, take good care to fill in the {@link android.view.inputmethod.EditorInfo}
* object correctly and in its entirety, so that the connected IME can rely
* on its values. For example, {@link android.view.inputmethod.EditorInfo#initialSelStart}
* and {@link android.view.inputmethod.EditorInfo#initialSelEnd} members
* must be filled in with the correct cursor position for IMEs to work correctly
* with your application. The default implementation does nothing, since a view doesn't support input methods by
* default (see {@link #onCreateInputConnection}).
*
* @param inputConnection The {@link InputConnection} from {@link #onCreateInputConnection},
* after it's been fully initialized by the system.
* @param editorInfo The {@link EditorInfo} that was used to create the {@link InputConnection}.
* @param handler The dedicated {@link Handler} on which IPC method calls from input methods
* will be dispatched. This is the handler returned by {@link InputConnection#getHandler()}. If
* that method returns null, this parameter will be null also.
*
* @hide
*/
public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
@NonNull EditorInfo editorInfo, @Nullable Handler handler) {}
/**
* Called by the {@link android.view.inputmethod.InputMethodManager} to notify the application
* that the {@link InputConnection} has been closed.
*
* The default implementation does nothing, since a view doesn't support input methods by
* default (see {@link #onCreateInputConnection}).
*
* Note: This callback is not invoked if the view is already detached when
* the {@link InputConnection} is closed or the connection is not valid and managed by
* {@link com.android.server.inputmethod.InputMethodManagerService}.
* TODO(b/170645312): Before un-hiding this API, handle the detached view scenario.
*
* @hide
*/
public void onInputConnectionClosedInternal() {}
/**
* Called by the {@link android.view.inputmethod.InputMethodManager}
* when a view who is not the current
* input connection target is trying to make a call on the manager. The
* default implementation returns false; you can override this to return
* true for certain views if you are performing InputConnection proxying
* to them.
* @param view The View that is making the InputMethodManager call.
* @return Return true to allow the call, false to reject.
*/
public boolean checkInputConnectionProxy(View view) {
return false;
}
/**
* Show the context menu for this view. It is not safe to hold on to the
* menu after returning from this method.
*
* You should normally not overload this method. Overload
* {@link #onCreateContextMenu(ContextMenu)} or define an
* {@link OnCreateContextMenuListener} to add items to the context menu.
*
* @param menu The context menu to populate
*/
public void createContextMenu(ContextMenu menu) {
ContextMenuInfo menuInfo = getContextMenuInfo();
// Sets the current menu info so all items added to menu will have
// my extra info set.
((MenuBuilder)menu).setCurrentMenuInfo(menuInfo);
onCreateContextMenu(menu);
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnCreateContextMenuListener != null) {
li.mOnCreateContextMenuListener.onCreateContextMenu(menu, this, menuInfo);
}
// Clear the extra information so subsequent items that aren't mine don't
// have my extra info.
((MenuBuilder)menu).setCurrentMenuInfo(null);
if (mParent != null) {
mParent.createContextMenu(menu);
}
}
/**
* Views should implement this if they have extra information to associate
* with the context menu. The return result is supplied as a parameter to
* the {@link OnCreateContextMenuListener#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)}
* callback.
*
* @return Extra information about the item for which the context menu
* should be shown. This information will vary across different
* subclasses of View.
*/
protected ContextMenuInfo getContextMenuInfo() {
return null;
}
/**
* Views should implement this if the view itself is going to add items to
* the context menu.
*
* @param menu the context menu to populate
*/
protected void onCreateContextMenu(ContextMenu menu) {
}
/**
* Implement this method to handle trackball motion events.
*
* The relative movement of the trackball since the last event
* can be retrieve with {@link MotionEvent#getX MotionEvent.getX()} and
* {@link MotionEvent#getY MotionEvent.getY()}. These are normalized so
* that a movement of 1 corresponds to the user pressing one DPAD key (so
* they will often be fractional values, representing the more fine-grained
* movement information available from a trackball).
*
* Generic motion events describe joystick movements, hover events from mouse or stylus
* devices, trackpad touches, scroll wheel movements and other motion events not handled
* by {@link #onTouchEvent(MotionEvent)} or {@link #onTrackballEvent(MotionEvent)}.
* The {@link MotionEvent#getSource() source} of the motion event specifies
* the class of input that was received. Implementations of this method
* must examine the bits in the source before processing the event.
* The following code example shows how this is done.
*
* Generic motion events with source class {@link InputDevice#SOURCE_CLASS_POINTER}
* are delivered to the view under the pointer. All other generic motion events are
* delivered to the focused view.
*
* This method is dispatching hover events to the delegate target to support explore by touch.
* Similar to {@link ViewGroup#dispatchTouchEvent}, this method send proper hover events to
* the delegate target according to the pointer and the touch area of the delegate while touch
* exploration enabled.
*
* This method is called whenever a pointer is hovering into, over, or out of the
* bounds of a view and the view is not currently being touched.
* Hover events are represented as pointer events with action
* {@link MotionEvent#ACTION_HOVER_ENTER}, {@link MotionEvent#ACTION_HOVER_MOVE},
* or {@link MotionEvent#ACTION_HOVER_EXIT}.
*
* The view should implement this method to return true to indicate that it is
* handling the hover event, such as by changing its drawable state.
*
* The default implementation calls {@link #setHovered} to update the hovered state
* of the view when a hover enter or hover exit event is received, if the view
* is enabled and is clickable. The default implementation also sends hover
* accessibility events.
*
* Calling this method also changes the drawable state of the view. This
* enables the view to react to hover by using different drawable resources
* to change its appearance.
*
* The {@link #onHoverChanged} method is called when the hovered state changes.
*
* This method is called whenever the hover state changes as a result of a
* call to {@link #setHovered}.
*
* If this method is used to detect click actions, it is recommended that
* the actions be performed by implementing and calling
* {@link #performClick()}. This will ensure consistent system behavior,
* including:
* This API is not intended for most applications. Buffered dispatch
* provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent
* streams will not improve your input latency. Side effects include: increased latency,
* jittery scrolls and inability to take advantage of system resampling. Talk to your input
* professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for
* you. Prior to {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, calling this method
* will not result in any behavioral changes when this View is not attached to a window.
*
* @param source The combined input source class to request unbuffered dispatch for. All
* events coming from these source classes will not be buffered. Set to
* {@link InputDevice#SOURCE_CLASS_NONE} in order to return to default behaviour.
*
* @see View#requestUnbufferedDispatch(MotionEvent)
*/
public final void requestUnbufferedDispatch(@InputSourceClass int source) {
if (mUnbufferedInputSource == source) {
return;
}
mUnbufferedInputSource = source;
if (mParent != null) {
mParent.onDescendantUnbufferedRequested();
}
}
private boolean hasSize() {
return (mBottom > mTop) && (mRight > mLeft);
}
private boolean canTakeFocus() {
return ((mViewFlags & VISIBILITY_MASK) == VISIBLE)
&& ((mViewFlags & FOCUSABLE) == FOCUSABLE)
&& ((mViewFlags & ENABLED_MASK) == ENABLED)
&& (sCanFocusZeroSized || !isLayoutValid() || hasSize());
}
/**
* Set flags controlling behavior of this view.
*
* @param flags Constant indicating the value which should be set
* @param mask Constant indicating the bit range that should be changed
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
void setFlags(int flags, int mask) {
final boolean accessibilityEnabled =
AccessibilityManager.getInstance(mContext).isEnabled();
final boolean oldIncludeForAccessibility =
accessibilityEnabled && includeForAccessibility(false);
int old = mViewFlags;
mViewFlags = (mViewFlags & ~mask) | (flags & mask);
int changed = mViewFlags ^ old;
if (changed == 0) {
return;
}
int privateFlags = mPrivateFlags;
boolean shouldNotifyFocusableAvailable = false;
// If focusable is auto, update the FOCUSABLE bit.
int focusableChangedByAuto = 0;
if (((mViewFlags & FOCUSABLE_AUTO) != 0)
&& (changed & (FOCUSABLE_MASK | CLICKABLE)) != 0) {
// Heuristic only takes into account whether view is clickable.
final int newFocus;
if ((mViewFlags & CLICKABLE) != 0) {
newFocus = FOCUSABLE;
} else {
newFocus = NOT_FOCUSABLE;
}
mViewFlags = (mViewFlags & ~FOCUSABLE) | newFocus;
focusableChangedByAuto = (old & FOCUSABLE) ^ (newFocus & FOCUSABLE);
changed = (changed & ~FOCUSABLE) | focusableChangedByAuto;
}
/* Check if the FOCUSABLE bit has changed */
if (((changed & FOCUSABLE) != 0) && ((privateFlags & PFLAG_HAS_BOUNDS) != 0)) {
if (((old & FOCUSABLE) == FOCUSABLE)
&& ((privateFlags & PFLAG_FOCUSED) != 0)) {
/* Give up focus if we are no longer focusable */
clearFocus();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).clearFocusedInCluster();
}
} else if (((old & FOCUSABLE) == NOT_FOCUSABLE)
&& ((privateFlags & PFLAG_FOCUSED) == 0)) {
/*
* Tell the view system that we are now available to take focus
* if no one else already has it.
*/
if (mParent != null) {
ViewRootImpl viewRootImpl = getViewRootImpl();
if (!sAutoFocusableOffUIThreadWontNotifyParents
|| focusableChangedByAuto == 0
|| viewRootImpl == null
|| viewRootImpl.mThread == Thread.currentThread()) {
shouldNotifyFocusableAvailable = canTakeFocus();
}
}
}
}
final int newVisibility = flags & VISIBILITY_MASK;
if (newVisibility == VISIBLE) {
if ((changed & VISIBILITY_MASK) != 0) {
/*
* If this view is becoming visible, invalidate it in case it changed while
* it was not visible. Marking it drawn ensures that the invalidation will
* go through.
*/
mPrivateFlags |= PFLAG_DRAWN;
invalidate(true);
needGlobalAttributesUpdate(true);
// a view becoming visible is worth notifying the parent about in case nothing has
// focus. Even if this specific view isn't focusable, it may contain something that
// is, so let the root view try to give this focus if nothing else does.
shouldNotifyFocusableAvailable = hasSize();
}
}
if ((changed & ENABLED_MASK) != 0) {
if ((mViewFlags & ENABLED_MASK) == ENABLED) {
// a view becoming enabled should notify the parent as long as the view is also
// visible and the parent wasn't already notified by becoming visible during this
// setFlags invocation.
shouldNotifyFocusableAvailable = canTakeFocus();
} else {
if (isFocused()) clearFocus();
}
}
if (shouldNotifyFocusableAvailable && mParent != null) {
mParent.focusableViewAvailable(this);
}
/* Check if the GONE bit has changed */
if ((changed & GONE) != 0) {
needGlobalAttributesUpdate(false);
requestLayout();
if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
if (hasFocus()) {
clearFocus();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).clearFocusedInCluster();
}
}
clearAccessibilityFocus();
destroyDrawingCache();
if (mParent instanceof View) {
// GONE views noop invalidation, so invalidate the parent
((View) mParent).invalidate(true);
}
// Mark the view drawn to ensure that it gets invalidated properly the next
// time it is visible and gets invalidated
mPrivateFlags |= PFLAG_DRAWN;
}
if (mAttachInfo != null) {
mAttachInfo.mViewVisibilityChanged = true;
}
}
/* Check if the VISIBLE bit has changed */
if ((changed & INVISIBLE) != 0) {
needGlobalAttributesUpdate(false);
/*
* If this view is becoming invisible, set the DRAWN flag so that
* the next invalidate() will not be skipped.
*/
mPrivateFlags |= PFLAG_DRAWN;
if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE)) {
// root view becoming invisible shouldn't clear focus and accessibility focus
if (getRootView() != this) {
if (hasFocus()) {
clearFocus();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).clearFocusedInCluster();
}
}
clearAccessibilityFocus();
}
}
if (mAttachInfo != null) {
mAttachInfo.mViewVisibilityChanged = true;
}
}
if ((changed & VISIBILITY_MASK) != 0) {
// If the view is invisible, cleanup its display list to free up resources
if (newVisibility != VISIBLE && mAttachInfo != null) {
cleanupDraw();
}
if (mParent instanceof ViewGroup) {
ViewGroup parent = (ViewGroup) mParent;
parent.onChildVisibilityChanged(this, (changed & VISIBILITY_MASK),
newVisibility);
parent.invalidate(true);
} else if (mParent != null) {
mParent.invalidateChild(this, null);
}
if (mAttachInfo != null) {
dispatchVisibilityChanged(this, newVisibility);
// Aggregated visibility changes are dispatched to attached views
// in visible windows where the parent is currently shown/drawn
// or the parent is not a ViewGroup (and therefore assumed to be a ViewRoot),
// discounting clipping or overlapping. This makes it a good place
// to change animation states.
if (mParent != null && getWindowVisibility() == VISIBLE &&
((!(mParent instanceof ViewGroup)) || ((ViewGroup) mParent).isShown())) {
dispatchVisibilityAggregated(newVisibility == VISIBLE);
}
// If this view is invisible from visible, then sending the A11y event by its
// parent which is shown and has the accessibility important.
if ((old & VISIBILITY_MASK) == VISIBLE) {
notifySubtreeAccessibilityStateChangedByParentIfNeeded();
} else {
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
}
if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {
destroyDrawingCache();
}
if ((changed & DRAWING_CACHE_ENABLED) != 0) {
destroyDrawingCache();
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
invalidateParentCaches();
}
if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) {
destroyDrawingCache();
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
if ((changed & DRAW_MASK) != 0) {
if ((mViewFlags & WILL_NOT_DRAW) != 0) {
if (mBackground != null
|| mDefaultFocusHighlight != null
|| (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
} else {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
} else {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
requestLayout();
invalidate(true);
}
if ((changed & KEEP_SCREEN_ON) != 0) {
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);
}
}
if (accessibilityEnabled) {
// If we're an accessibility pane and the visibility changed, we already have sent
// a state change, so we really don't need to report other changes.
if (isAccessibilityPane()) {
changed &= ~VISIBILITY_MASK;
}
if ((changed & FOCUSABLE) != 0 || (changed & VISIBILITY_MASK) != 0
|| (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0
|| (changed & CONTEXT_CLICKABLE) != 0) {
if (oldIncludeForAccessibility != includeForAccessibility(false)) {
notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
} else if ((changed & ENABLED_MASK) != 0) {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
/**
* Change the view's z order in the tree, so it's on top of other sibling
* views. This ordering change may affect layout, if the parent container
* uses an order-dependent layout scheme (e.g., LinearLayout). Prior
* to {@link android.os.Build.VERSION_CODES#KITKAT} this
* method should be followed by calls to {@link #requestLayout()} and
* {@link View#invalidate()} on the view's parent to force the parent to redraw
* with the new child ordering.
*
* @see ViewGroup#bringChildToFront(View)
*/
public void bringToFront() {
if (mParent != null) {
mParent.bringChildToFront(this);
}
}
private HapticScrollFeedbackProvider getScrollFeedbackProvider() {
if (mScrollFeedbackProvider == null) {
mScrollFeedbackProvider = new HapticScrollFeedbackProvider(this,
ViewConfiguration.get(mContext), /* disabledIfViewPlaysScrollHaptics= */ false);
}
return mScrollFeedbackProvider;
}
private void doRotaryProgressForScrollHaptics(MotionEvent rotaryEvent) {
final float axisScrollValue = rotaryEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
final float verticalScrollFactor =
ViewConfiguration.get(mContext).getScaledVerticalScrollFactor();
final int scrollAmount = -Math.round(axisScrollValue * verticalScrollFactor);
getScrollFeedbackProvider().onScrollProgress(
rotaryEvent.getDeviceId(), InputDevice.SOURCE_ROTARY_ENCODER,
MotionEvent.AXIS_SCROLL, scrollAmount);
}
private void doRotaryLimitForScrollHaptics(MotionEvent rotaryEvent) {
final boolean isStart = rotaryEvent.getAxisValue(MotionEvent.AXIS_SCROLL) > 0;
getScrollFeedbackProvider().onScrollLimit(
rotaryEvent.getDeviceId(), InputDevice.SOURCE_ROTARY_ENCODER,
MotionEvent.AXIS_SCROLL, isStart);
}
private void processScrollEventForRotaryEncoderHaptics() {
if ((mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT) != 0) {
mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT;
mPrivateFlags4 &= ~PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT;
}
}
/**
* This is called in response to an internal scroll in this view (i.e., the
* view scrolled its own contents). This is typically as a result of
* {@link #scrollBy(int, int)} or {@link #scrollTo(int, int)} having been
* called.
*
* @param l Current horizontal scroll origin.
* @param t Current vertical scroll origin.
* @param oldl Previous horizontal scroll origin.
* @param oldt Previous vertical scroll origin.
*/
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
notifySubtreeAccessibilityStateChangedIfNeeded();
postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt);
processScrollEventForRotaryEncoderHaptics();
mBackgroundSizeChanged = true;
mDefaultFocusHighlightSizeChanged = true;
if (mForegroundInfo != null) {
mForegroundInfo.mBoundsChanged = true;
}
final AttachInfo ai = mAttachInfo;
if (ai != null) {
ai.mViewScrollChanged = true;
}
if (mListenerInfo != null && mListenerInfo.mOnScrollChangeListener != null) {
mListenerInfo.mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
}
}
/**
* Interface definition for a callback to be invoked when the scroll
* X or Y positions of a view change.
*
* Note: Some views handle scrolling independently from View and may
* have their own separate listeners for scroll-type events. For example,
* {@link android.widget.ListView ListView} allows clients to register an
* {@link android.widget.ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener) AbsListView.OnScrollListener}
* to listen for changes in list scroll position.
*
* @see #setOnScrollChangeListener(View.OnScrollChangeListener)
*/
public interface OnScrollChangeListener {
/**
* Called when the scroll position of a view changes.
*
* @param v The view whose scroll position has changed.
* @param scrollX Current horizontal scroll origin.
* @param scrollY Current vertical scroll origin.
* @param oldScrollX Previous horizontal scroll origin.
* @param oldScrollY Previous vertical scroll origin.
*/
void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY);
}
/**
* Interface definition for a callback to be invoked when the layout bounds of a view
* changes due to layout processing.
*/
public interface OnLayoutChangeListener {
/**
* Called when the layout bounds of a view changes due to layout processing.
*
* @param v The view whose bounds have changed.
* @param left The new value of the view's left property.
* @param top The new value of the view's top property.
* @param right The new value of the view's right property.
* @param bottom The new value of the view's bottom property.
* @param oldLeft The previous value of the view's left property.
* @param oldTop The previous value of the view's top property.
* @param oldRight The previous value of the view's right property.
* @param oldBottom The previous value of the view's bottom property.
*/
void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom);
}
/**
* This is called during layout when the size of this view has changed. If
* you were just added to the view hierarchy, you're called with the old
* values of 0.
*
* @param w Current width of this view.
* @param h Current height of this view.
* @param oldw Old width of this view.
* @param oldh Old height of this view.
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
}
/**
* Called by draw to draw the child views. This may be overridden
* by derived classes to gain control just before its children are drawn
* (but after its own view has been drawn).
* @param canvas the canvas on which to draw the view
*/
protected void dispatchDraw(@NonNull Canvas canvas) {
}
/**
* Gets the parent of this view. Note that the parent is a
* ViewParent and not necessarily a View.
*
* @return Parent of this view.
*/
public final ViewParent getParent() {
return mParent;
}
/**
* Set the horizontal scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param value the x position to scroll to
*/
public void setScrollX(int value) {
scrollTo(value, mScrollY);
}
/**
* Set the vertical scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param value the y position to scroll to
*/
public void setScrollY(int value) {
scrollTo(mScrollX, value);
}
/**
* Return the scrolled left position of this view. This is the left edge of
* the displayed part of your view. You do not need to draw any pixels
* farther left, since those are outside of the frame of your view on
* screen.
*
* @return The left edge of the displayed part of your view, in pixels.
*/
@InspectableProperty
public final int getScrollX() {
return mScrollX;
}
/**
* Return the scrolled top position of this view. This is the top edge of
* the displayed part of your view. You do not need to draw any pixels above
* it, since those are outside of the frame of your view on screen.
*
* @return The top edge of the displayed part of your view, in pixels.
*/
@InspectableProperty
public final int getScrollY() {
return mScrollY;
}
/**
* Return the width of your view.
*
* @return The width of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
/**
* Return the height of your view.
*
* @return The height of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
return mBottom - mTop;
}
/**
* Return the visible drawing bounds of your view. Fills in the output
* rectangle with the values from getScrollX(), getScrollY(),
* getWidth(), and getHeight(). These bounds do not account for any
* transformation properties currently set on the view, such as
* {@link #setScaleX(float)} or {@link #setRotation(float)}.
*
* @param outRect The (scrolled) drawing bounds of the view.
*/
public void getDrawingRect(Rect outRect) {
outRect.left = mScrollX;
outRect.top = mScrollY;
outRect.right = mScrollX + (mRight - mLeft);
outRect.bottom = mScrollY + (mBottom - mTop);
}
/**
* Like {@link #getMeasuredWidthAndState()}, but only returns the
* raw width component (that is the result is masked by
* {@link #MEASURED_SIZE_MASK}).
*
* @return The raw measured width of this view.
*/
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
/**
* Return the full width measurement information for this view as computed
* by the most recent call to {@link #measure(int, int)}. This result is a bit mask
* as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
* This should be used during measurement and layout calculations only. Use
* {@link #getWidth()} to see how wide a view is after layout.
*
* @return The measured width of this view as a bit mask.
*/
@ViewDebug.ExportedProperty(category = "measurement", flagMapping = {
@ViewDebug.FlagToString(mask = MEASURED_STATE_MASK, equals = MEASURED_STATE_TOO_SMALL,
name = "MEASURED_STATE_TOO_SMALL"),
})
public final int getMeasuredWidthAndState() {
return mMeasuredWidth;
}
/**
* Like {@link #getMeasuredHeightAndState()}, but only returns the
* raw height component (that is the result is masked by
* {@link #MEASURED_SIZE_MASK}).
*
* @return The raw measured height of this view.
*/
public final int getMeasuredHeight() {
return mMeasuredHeight & MEASURED_SIZE_MASK;
}
/**
* Return the full height measurement information for this view as computed
* by the most recent call to {@link #measure(int, int)}. This result is a bit mask
* as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
* This should be used during measurement and layout calculations only. Use
* {@link #getHeight()} to see how high a view is after layout.
*
* @return The measured height of this view as a bit mask.
*/
@ViewDebug.ExportedProperty(category = "measurement", flagMapping = {
@ViewDebug.FlagToString(mask = MEASURED_STATE_MASK, equals = MEASURED_STATE_TOO_SMALL,
name = "MEASURED_STATE_TOO_SMALL"),
})
public final int getMeasuredHeightAndState() {
return mMeasuredHeight;
}
/**
* Return only the state bits of {@link #getMeasuredWidthAndState()}
* and {@link #getMeasuredHeightAndState()}, combined into one integer.
* The width component is in the regular bits {@link #MEASURED_STATE_MASK}
* and the height component is at the shifted bits
* {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}.
*/
public final int getMeasuredState() {
return (mMeasuredWidth&MEASURED_STATE_MASK)
| ((mMeasuredHeight>>MEASURED_HEIGHT_STATE_SHIFT)
& (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
}
/**
* The transform matrix of this view, which is calculated based on the current
* rotation, scale, and pivot properties.
*
* @see #getRotation()
* @see #getScaleX()
* @see #getScaleY()
* @see #getPivotX()
* @see #getPivotY()
* @return The current transform matrix for the view
*/
public Matrix getMatrix() {
ensureTransformationInfo();
final Matrix matrix = mTransformationInfo.mMatrix;
mRenderNode.getMatrix(matrix);
return matrix;
}
/**
* Returns true if the transform matrix is the identity matrix.
* Recomputes the matrix if necessary.
*
* @return True if the transform matrix is the identity matrix, false otherwise.
* @hide
*/
@UnsupportedAppUsage
public final boolean hasIdentityMatrix() {
return mRenderNode.hasIdentityMatrix();
}
@UnsupportedAppUsage
void ensureTransformationInfo() {
if (mTransformationInfo == null) {
mTransformationInfo = new TransformationInfo();
}
}
/**
* Utility method to retrieve the inverse of the current mMatrix property.
* We cache the matrix to avoid recalculating it when transform properties
* have not changed.
*
* @return The inverse of the current matrix of this view.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public final Matrix getInverseMatrix() {
ensureTransformationInfo();
if (mTransformationInfo.mInverseMatrix == null) {
mTransformationInfo.mInverseMatrix = new Matrix();
}
final Matrix matrix = mTransformationInfo.mInverseMatrix;
mRenderNode.getInverseMatrix(matrix);
return matrix;
}
/**
* Gets the distance along the Z axis from the camera to this view.
*
* @see #setCameraDistance(float)
*
* @return The distance along the Z axis.
*/
public float getCameraDistance() {
final float dpi = mResources.getDisplayMetrics().densityDpi;
return mRenderNode.getCameraDistance() * dpi;
}
/**
* Sets the distance along the Z axis (orthogonal to the X/Y plane on which
* views are drawn) from the camera to this view. The camera's distance
* affects 3D transformations, for instance rotations around the X and Y
* axis. If the rotationX or rotationY properties are changed and this view is
* large (more than half the size of the screen), it is recommended to always
* use a camera distance that's greater than the height (X axis rotation) or
* the width (Y axis rotation) of this view. The distance of the camera from the view plane can have an affect on the
* perspective distortion of the view when it is rotated around the x or y axis.
* For example, a large distance will result in a large viewing angle, and there
* will not be much perspective distortion of the view as it rotates. A short
* distance may cause much more perspective distortion upon rotation, and can
* also result in some drawing artifacts if the rotated view ends up partially
* behind the camera (which is why the recommendation is to use a distance at
* least as far as the size of the view, if the view is to be rotated.) The distance is expressed in "depth pixels." The default distance depends
* on the screen density. For instance, on a medium density display, the
* default distance is 1280. On a high density display, the default distance
* is 1920. If you want to specify a distance that leads to visually consistent
* results across various densities, use the following formula: The density scale factor of a high density display is 1.5,
* and 1920 = 1280 * 1.5. By default, this is 1.0f.
*
* @see #getPivotX()
* @see #getPivotY()
* @return The scaling factor.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@InspectableProperty
public float getScaleX() {
return mRenderNode.getScaleX();
}
/**
* Sets the amount that the view is scaled in x around the pivot point, as a proportion of
* the view's unscaled width. A value of 1 means that no scaling is applied.
*
* @param scaleX The scaling factor.
* @see #getPivotX()
* @see #getPivotY()
*
* @attr ref android.R.styleable#View_scaleX
*/
@RemotableViewMethod
public void setScaleX(float scaleX) {
if (scaleX != getScaleX()) {
scaleX = sanitizeFloatPropertyValue(scaleX, "scaleX");
invalidateViewProperty(true, false);
mRenderNode.setScaleX(scaleX);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
/**
* The amount that the view is scaled in y around the pivot point, as a proportion of
* the view's unscaled height. A value of 1, the default, means that no scaling is applied.
*
* By default, this is 1.0f.
*
* @see #getPivotX()
* @see #getPivotY()
* @return The scaling factor.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@InspectableProperty
public float getScaleY() {
return mRenderNode.getScaleY();
}
/**
* Sets the amount that the view is scaled in Y around the pivot point, as a proportion of
* the view's unscaled width. A value of 1 means that no scaling is applied.
*
* @param scaleY The scaling factor.
* @see #getPivotX()
* @see #getPivotY()
*
* @attr ref android.R.styleable#View_scaleY
*/
@RemotableViewMethod
public void setScaleY(float scaleY) {
if (scaleY != getScaleY()) {
scaleY = sanitizeFloatPropertyValue(scaleY, "scaleY");
invalidateViewProperty(true, false);
mRenderNode.setScaleY(scaleY);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
/**
* The x location of the point around which the view is {@link #setRotation(float) rotated}
* and {@link #setScaleX(float) scaled}.
*
* @see #getRotation()
* @see #getScaleX()
* @see #getScaleY()
* @see #getPivotY()
* @return The x location of the pivot point.
*
* @attr ref android.R.styleable#View_transformPivotX
*/
@ViewDebug.ExportedProperty(category = "drawing")
@InspectableProperty(name = "transformPivotX")
public float getPivotX() {
return mRenderNode.getPivotX();
}
/**
* Sets the x location of the point around which the view is
* {@link #setRotation(float) rotated} and {@link #setScaleX(float) scaled}.
* By default, the pivot point is centered on the object.
* Setting this property disables this behavior and causes the view to use only the
* explicitly set pivotX and pivotY values.
*
* @param pivotX The x location of the pivot point.
* @see #getRotation()
* @see #getScaleX()
* @see #getScaleY()
* @see #getPivotY()
*
* @attr ref android.R.styleable#View_transformPivotX
*/
@RemotableViewMethod
public void setPivotX(float pivotX) {
if (!mRenderNode.isPivotExplicitlySet() || pivotX != getPivotX()) {
invalidateViewProperty(true, false);
mRenderNode.setPivotX(pivotX);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
}
}
/**
* The y location of the point around which the view is {@link #setRotation(float) rotated}
* and {@link #setScaleY(float) scaled}.
*
* @see #getRotation()
* @see #getScaleX()
* @see #getScaleY()
* @see #getPivotY()
* @return The y location of the pivot point.
*
* @attr ref android.R.styleable#View_transformPivotY
*/
@ViewDebug.ExportedProperty(category = "drawing")
@InspectableProperty(name = "transformPivotY")
public float getPivotY() {
return mRenderNode.getPivotY();
}
/**
* Sets the y location of the point around which the view is {@link #setRotation(float) rotated}
* and {@link #setScaleY(float) scaled}. By default, the pivot point is centered on the object.
* Setting this property disables this behavior and causes the view to use only the
* explicitly set pivotX and pivotY values.
*
* @param pivotY The y location of the pivot point.
* @see #getRotation()
* @see #getScaleX()
* @see #getScaleY()
* @see #getPivotY()
*
* @attr ref android.R.styleable#View_transformPivotY
*/
@RemotableViewMethod
public void setPivotY(float pivotY) {
if (!mRenderNode.isPivotExplicitlySet() || pivotY != getPivotY()) {
invalidateViewProperty(true, false);
mRenderNode.setPivotY(pivotY);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
}
}
/**
* Returns whether or not a pivot has been set by a call to {@link #setPivotX(float)} or
* {@link #setPivotY(float)}. If no pivot has been set then the pivot will be the center
* of the view.
*
* @return True if a pivot has been set, false if the default pivot is being used
*/
public boolean isPivotSet() {
return mRenderNode.isPivotExplicitlySet();
}
/**
* Clears any pivot previously set by a call to {@link #setPivotX(float)} or
* {@link #setPivotY(float)}. After calling this {@link #isPivotSet()} will be false
* and the pivot used for rotation will return to default of being centered on the view.
*/
public void resetPivot() {
if (mRenderNode.resetPivot()) {
invalidateViewProperty(false, false);
}
}
/**
* The opacity of the view. This is a value from 0 to 1, where 0 means the view is
* completely transparent and 1 means the view is completely opaque.
*
* By default this is 1.0f.
* @return The opacity of the view.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@InspectableProperty
public float getAlpha() {
return mTransformationInfo != null ? mTransformationInfo.mAlpha : 1;
}
/**
* Sets the behavior for overlapping rendering for this view (see {@link
* #hasOverlappingRendering()} for more details on this behavior). Calling this method
* is an alternative to overriding {@link #hasOverlappingRendering()} in a subclass,
* providing the value which is then used internally. That is, when {@link
* #forceHasOverlappingRendering(boolean)} is called, the value of {@link
* #hasOverlappingRendering()} is ignored and the value passed into this method is used
* instead.
*
* @param hasOverlappingRendering The value for overlapping rendering to be used internally
* instead of that returned by {@link #hasOverlappingRendering()}.
*
* @attr ref android.R.styleable#View_forceHasOverlappingRendering
*/
public void forceHasOverlappingRendering(boolean hasOverlappingRendering) {
mPrivateFlags3 |= PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED;
if (hasOverlappingRendering) {
mPrivateFlags3 |= PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE;
} else {
mPrivateFlags3 &= ~PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE;
}
}
/**
* Returns the value for overlapping rendering that is used internally. This is either
* the value passed into {@link #forceHasOverlappingRendering(boolean)}, if called, or
* the return value of {@link #hasOverlappingRendering()}, otherwise.
*
* @return The value for overlapping rendering being used internally.
*/
public final boolean getHasOverlappingRendering() {
return (mPrivateFlags3 & PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED) != 0 ?
(mPrivateFlags3 & PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE) != 0 :
hasOverlappingRendering();
}
/**
* Returns whether this View has content which overlaps.
*
* This function, intended to be overridden by specific View types, is an optimization when
* alpha is set on a view. If rendering overlaps in a view with alpha < 1, that view is drawn to
* an offscreen buffer and then composited into place, which can be expensive. If the view has
* no overlapping rendering, the view can draw each primitive with the appropriate alpha value
* directly. An example of overlapping rendering is a TextView with a background image, such as
* a Button. An example of non-overlapping rendering is a TextView with no background, or an
* ImageView with only the foreground image. The default implementation returns true; subclasses
* should override if they have cases which can be optimized. Note: The return value of this method is ignored if {@link
* #forceHasOverlappingRendering(boolean)} has been called on this view. Note: setting alpha to a translucent value (0 < alpha < 1)
* can have significant performance implications, especially for large views. It is best to use
* the alpha property sparingly and transiently, as in the case of fading animations. For a view with a frequently changing alpha, such as during a fading animation, it is
* strongly recommended for performance reasons to either override
* {@link #hasOverlappingRendering()} to return If this view overrides {@link #onSetAlpha(int)} to return true, then this view is
* responsible for applying the opacity itself. On versions {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and below, note that if
* the view is backed by a {@link #setLayerType(int, android.graphics.Paint) layer} and is
* associated with a {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an
* alpha value less than 1.0 will supersede the alpha of the layer paint. Starting with {@link android.os.Build.VERSION_CODES#M}, setting a translucent alpha
* value will clip a View to its bounds, unless the View returns
* Any previously attached StateListAnimator will be detached.
*
* @param stateListAnimator The StateListAnimator to update the view
* @see android.animation.StateListAnimator
*/
public void setStateListAnimator(StateListAnimator stateListAnimator) {
if (mStateListAnimator == stateListAnimator) {
return;
}
if (mStateListAnimator != null) {
mStateListAnimator.setTarget(null);
}
mStateListAnimator = stateListAnimator;
if (stateListAnimator != null) {
stateListAnimator.setTarget(this);
if (isAttachedToWindow()) {
stateListAnimator.setState(getDrawableState());
}
}
}
/**
* Returns whether the Outline should be used to clip the contents of the View.
*
* Note that this flag will only be respected if the View's Outline returns true from
* {@link Outline#canClip()}.
*
* @see #setOutlineProvider(ViewOutlineProvider)
* @see #setClipToOutline(boolean)
*/
public final boolean getClipToOutline() {
return mRenderNode.getClipToOutline();
}
/**
* Sets whether the View's Outline should be used to clip the contents of the View.
*
* Only a single non-rectangular clip can be applied on a View at any time.
* Circular clips from a {@link ViewAnimationUtils#createCircularReveal(View, int, int, float, float)
* circular reveal} animation take priority over Outline clipping, and
* child Outline clipping takes priority over Outline clipping done by a
* parent.
*
* Note that this flag will only be respected if the View's Outline returns true from
* {@link Outline#canClip()}.
*
* @see #setOutlineProvider(ViewOutlineProvider)
* @see #getClipToOutline()
*
* @attr ref android.R.styleable#View_clipToOutline
*/
@RemotableViewMethod
public void setClipToOutline(boolean clipToOutline) {
damageInParent();
if (getClipToOutline() != clipToOutline) {
mRenderNode.setClipToOutline(clipToOutline);
}
}
// correspond to the enum values of View_outlineProvider
private static final int PROVIDER_BACKGROUND = 0;
private static final int PROVIDER_NONE = 1;
private static final int PROVIDER_BOUNDS = 2;
private static final int PROVIDER_PADDED_BOUNDS = 3;
private void setOutlineProviderFromAttribute(int providerInt) {
switch (providerInt) {
case PROVIDER_BACKGROUND:
setOutlineProvider(ViewOutlineProvider.BACKGROUND);
break;
case PROVIDER_NONE:
setOutlineProvider(null);
break;
case PROVIDER_BOUNDS:
setOutlineProvider(ViewOutlineProvider.BOUNDS);
break;
case PROVIDER_PADDED_BOUNDS:
setOutlineProvider(ViewOutlineProvider.PADDED_BOUNDS);
break;
}
}
/**
* Sets the {@link ViewOutlineProvider} of the view, which generates the Outline that defines
* the shape of the shadow it casts, and enables outline clipping.
*
* The default ViewOutlineProvider, {@link ViewOutlineProvider#BACKGROUND}, queries the Outline
* from the View's background drawable, via {@link Drawable#getOutline(Outline)}. Changing the
* outline provider with this method allows this behavior to be overridden.
*
* If the ViewOutlineProvider is null, if querying it for an outline returns false,
* or if the produced Outline is {@link Outline#isEmpty()}, shadows will not be cast.
*
* Only outlines that return true from {@link Outline#canClip()} may be used for clipping.
*
* @see #setClipToOutline(boolean)
* @see #getClipToOutline()
* @see #getOutlineProvider()
*/
public void setOutlineProvider(ViewOutlineProvider provider) {
if (mOutlineProvider != provider) {
mOutlineProvider = provider;
invalidateOutline();
}
}
/**
* Returns the current {@link ViewOutlineProvider} of the view, which generates the Outline
* that defines the shape of the shadow it casts, and enables outline clipping.
*
* @see #setOutlineProvider(ViewOutlineProvider)
*/
@InspectableProperty
public ViewOutlineProvider getOutlineProvider() {
return mOutlineProvider;
}
/**
* Called to rebuild this View's Outline from its {@link ViewOutlineProvider outline provider}
*
* @see #setOutlineProvider(ViewOutlineProvider)
*/
public void invalidateOutline() {
rebuildOutline();
notifySubtreeAccessibilityStateChangedIfNeeded();
invalidateViewProperty(false, false);
}
/**
* Internal version of {@link #invalidateOutline()} which invalidates the
* outline without invalidating the view itself. This is intended to be called from
* within methods in the View class itself which are the result of the view being
* invalidated already. For example, when we are drawing the background of a View,
* we invalidate the outline in case it changed in the meantime, but we do not
* need to invalidate the view because we're already drawing the background as part
* of drawing the view in response to an earlier invalidation of the view.
*/
private void rebuildOutline() {
// Unattached views ignore this signal, and outline is recomputed in onAttachedToWindow()
if (mAttachInfo == null) return;
if (mOutlineProvider == null) {
// no provider, remove outline
mRenderNode.setOutline(null);
} else {
final Outline outline = mAttachInfo.mTmpOutline;
outline.setEmpty();
outline.setAlpha(1.0f);
mOutlineProvider.getOutline(this, outline);
mRenderNode.setOutline(outline);
}
}
/**
* HierarchyViewer only
*
* @hide
*/
@ViewDebug.ExportedProperty(category = "drawing")
public boolean hasShadow() {
return mRenderNode.hasShadow();
}
/**
* Sets the color of the spot shadow that is drawn when the view has a positive Z or
* elevation value.
*
* By default the shadow color is black. Generally, this color will be opaque so the intensity
* of the shadow is consistent between different views with different colors.
*
* The opacity of the final spot shadow is a function of the shadow caster height, the
* alpha channel of the outlineSpotShadowColor (typically opaque), and the
* {@link android.R.attr#spotShadowAlpha} theme attribute.
*
* @attr ref android.R.styleable#View_outlineSpotShadowColor
* @param color The color this View will cast for its elevation spot shadow.
*/
public void setOutlineSpotShadowColor(@ColorInt int color) {
if (mRenderNode.setSpotShadowColor(color)) {
invalidateViewProperty(true, true);
}
}
/**
* @return The shadow color set by {@link #setOutlineSpotShadowColor(int)}, or black if nothing
* was set
*/
@InspectableProperty
public @ColorInt int getOutlineSpotShadowColor() {
return mRenderNode.getSpotShadowColor();
}
/**
* Sets the color of the ambient shadow that is drawn when the view has a positive Z or
* elevation value.
*
* By default the shadow color is black. Generally, this color will be opaque so the intensity
* of the shadow is consistent between different views with different colors.
*
* The opacity of the final ambient shadow is a function of the shadow caster height, the
* alpha channel of the outlineAmbientShadowColor (typically opaque), and the
* {@link android.R.attr#ambientShadowAlpha} theme attribute.
*
* @attr ref android.R.styleable#View_outlineAmbientShadowColor
* @param color The color this View will cast for its elevation shadow.
*/
public void setOutlineAmbientShadowColor(@ColorInt int color) {
if (mRenderNode.setAmbientShadowColor(color)) {
invalidateViewProperty(true, true);
}
}
/**
* @return The shadow color set by {@link #setOutlineAmbientShadowColor(int)}, or black if
* nothing was set
*/
@InspectableProperty
public @ColorInt int getOutlineAmbientShadowColor() {
return mRenderNode.getAmbientShadowColor();
}
/** @hide */
public void setRevealClip(boolean shouldClip, float x, float y, float radius) {
mRenderNode.setRevealClip(shouldClip, x, y, radius);
invalidateViewProperty(false, false);
}
/**
* Hit rectangle in parent's coordinates
*
* @param outRect The hit rectangle of the view.
*/
public void getHitRect(Rect outRect) {
if (hasIdentityMatrix() || mAttachInfo == null) {
outRect.set(mLeft, mTop, mRight, mBottom);
} else {
final RectF tmpRect = mAttachInfo.mTmpTransformRect;
tmpRect.set(0, 0, getWidth(), getHeight());
getMatrix().mapRect(tmpRect); // TODO: mRenderNode.mapRect(tmpRect)
outRect.set((int) tmpRect.left + mLeft, (int) tmpRect.top + mTop,
(int) tmpRect.right + mLeft, (int) tmpRect.bottom + mTop);
}
}
/**
* Determines whether the given point, in local coordinates is inside the view.
*/
/*package*/ final boolean pointInView(float localX, float localY) {
return pointInView(localX, localY, 0);
}
/**
* Utility method to determine whether the given point, in local coordinates,
* is inside the view, where the area of the view is expanded by the slop factor.
* This method is called while processing touch-move events to determine if the event
* is still within the view.
*
* @hide
*/
@UnsupportedAppUsage
public boolean pointInView(float localX, float localY, float slop) {
return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
/**
* When a view has focus and the user navigates away from it, the next view is searched for
* starting from the rectangle filled in by this method.
*
* By default, the rectangle is the {@link #getDrawingRect(android.graphics.Rect)})
* of the view. However, if your view maintains some idea of internal selection,
* such as a cursor, or a selected row or column, you should override this method and
* fill in a more specific rectangle.
*
* @param r The rectangle to fill in, in this view's coordinates.
*/
public void getFocusedRect(Rect r) {
getDrawingRect(r);
}
/**
* Sets {@code r} to the coordinates of the non-clipped area of this view in
* the coordinate space of the view's root view. Sets {@code globalOffset}
* to the offset of the view's x and y coordinates from the coordinate space
* origin, which is the top left corner of the root view irrespective of
* screen decorations and system UI elements.
*
* To convert {@code r} to coordinates relative to the top left corner of
* this view (without taking view rotations into account), offset {@code r}
* by the inverse values of
* {@code globalOffset}—{@code r.offset(-globalOffset.x,
* -globalOffset.y)}—which is equivalent to calling
* {@link #getLocalVisibleRect(Rect) getLocalVisibleRect(Rect)}.
*
* Note: Do not use this method to determine the size of a window
* in multi-window mode; use
* {@link WindowManager#getCurrentWindowMetrics()}.
*
* @param r If the method returns true, contains the coordinates of the
* visible portion of this view in the coordinate space of the view's
* root view. If the method returns false, the contents of {@code r}
* are undefined.
* @param globalOffset If the method returns true, contains the offset of
* the x and y coordinates of this view from the top left corner of the
* view's root view. If the method returns false, the contents of
* {@code globalOffset} are undefined. The argument can be null (see
* {@link #getGlobalVisibleRect(Rect) getGlobalVisibleRect(Rect)}.
* @return true if at least part of the view is visible within the root
* view; false if the view is completely clipped or translated out of
* the visible area of the root view.
*
* @see #getLocalVisibleRect(Rect)
*/
public boolean getGlobalVisibleRect(Rect r, Point globalOffset) {
int width = mRight - mLeft;
int height = mBottom - mTop;
if (width > 0 && height > 0) {
r.set(0, 0, width, height);
if (globalOffset != null) {
globalOffset.set(-mScrollX, -mScrollY);
}
return mParent == null || mParent.getChildVisibleRect(this, r, globalOffset);
}
return false;
}
/**
* Sets {@code r} to the coordinates of the non-clipped area of this view in
* the coordinate space of the view's root view.
*
* See {@link #getGlobalVisibleRect(Rect, Point)
* getGlobalVisibleRect(Rect, Point)} for more information.
*
* @param r If the method returns true, contains the coordinates of the
* visible portion of this view in the coordinate space of the view's
* root view. If the method returns false, the contents of {@code r}
* are undefined.
* @return true if at least part of the view is visible within the root
* view; otherwise false.
*/
public final boolean getGlobalVisibleRect(Rect r) {
return getGlobalVisibleRect(r, null);
}
/**
* Sets {@code r} to the coordinates of the non-clipped area of this view
* relative to the top left corner of the view.
*
* If the view is clipped on the left or top, the left and top
* coordinates are offset from 0 by the clipped amount. For example, if the
* view is off screen 50px on the left and 30px at the top, the left and top
* coordinates are 50 and 30 respectively.
*
* If the view is clipped on the right or bottom, the right and bottom
* coordinates are reduced by the clipped amount. For example, if the view
* is off screen 40px on the right and 20px at the bottom, the right
* coordinate is the view width - 40, and the bottom coordinate is the view
* height - 20.
*
* @param r If the method returns true, contains the coordinates of the
* visible portion of this view relative to the top left corner of the
* view. If the method returns false, the contents of {@code r} are
* undefined.
* @return true if at least part of the view is visible; false if the view
* is completely clipped or translated out of the visible area.
*
* @see #getGlobalVisibleRect(Rect, Point)
*/
public final boolean getLocalVisibleRect(Rect r) {
final Point offset = mAttachInfo != null ? mAttachInfo.mPoint : new Point();
if (getGlobalVisibleRect(r, offset)) {
r.offset(-offset.x, -offset.y); // make r local
return true;
}
return false;
}
/**
* Offset this view's vertical location by the specified number of pixels.
*
* @param offset the number of pixels to offset the view by
*/
public void offsetTopAndBottom(int offset) {
if (offset != 0) {
final boolean matrixIsIdentity = hasIdentityMatrix();
if (matrixIsIdentity) {
if (isHardwareAccelerated()) {
invalidateViewProperty(false, false);
} else {
final ViewParent p = mParent;
if (p != null && mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
int minTop;
int maxBottom;
int yLoc;
if (offset < 0) {
minTop = mTop + offset;
maxBottom = mBottom;
yLoc = offset;
} else {
minTop = mTop;
maxBottom = mBottom + offset;
yLoc = 0;
}
r.set(0, yLoc, mRight - mLeft, maxBottom - minTop);
p.invalidateChild(this, r);
}
}
} else {
invalidateViewProperty(false, false);
}
mTop += offset;
mBottom += offset;
mRenderNode.offsetTopAndBottom(offset);
if (isHardwareAccelerated()) {
invalidateViewProperty(false, false);
invalidateParentIfNeededAndWasQuickRejected();
} else {
if (!matrixIsIdentity) {
invalidateViewProperty(false, true);
}
invalidateParentIfNeeded();
}
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
/**
* Offset this view's horizontal location by the specified amount of pixels.
*
* @param offset the number of pixels to offset the view by
*/
public void offsetLeftAndRight(int offset) {
if (offset != 0) {
final boolean matrixIsIdentity = hasIdentityMatrix();
if (matrixIsIdentity) {
if (isHardwareAccelerated()) {
invalidateViewProperty(false, false);
} else {
final ViewParent p = mParent;
if (p != null && mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
int minLeft;
int maxRight;
if (offset < 0) {
minLeft = mLeft + offset;
maxRight = mRight;
} else {
minLeft = mLeft;
maxRight = mRight + offset;
}
r.set(0, 0, maxRight - minLeft, mBottom - mTop);
p.invalidateChild(this, r);
}
}
} else {
invalidateViewProperty(false, false);
}
mLeft += offset;
mRight += offset;
mRenderNode.offsetLeftAndRight(offset);
if (isHardwareAccelerated()) {
invalidateViewProperty(false, false);
invalidateParentIfNeededAndWasQuickRejected();
} else {
if (!matrixIsIdentity) {
invalidateViewProperty(false, true);
}
invalidateParentIfNeeded();
}
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
/**
* Get the LayoutParams associated with this view. All views should have
* layout parameters. These supply parameters to the parent of this
* view specifying how it should be arranged. There are many subclasses of
* ViewGroup.LayoutParams, and these correspond to the different subclasses
* of ViewGroup that are responsible for arranging their children.
*
* This method may return null if this View is not attached to a parent
* ViewGroup or {@link #setLayoutParams(android.view.ViewGroup.LayoutParams)}
* was not invoked successfully. When a View is attached to a parent
* ViewGroup, this method must not return null.
*
* @return The LayoutParams associated with this view, or null if no
* parameters have been set yet
*/
@ViewDebug.ExportedProperty(deepExport = true, prefix = "layout_")
public ViewGroup.LayoutParams getLayoutParams() {
return mLayoutParams;
}
/**
* Set the layout parameters associated with this view. These supply
* parameters to the parent of this view specifying how it should be
* arranged. There are many subclasses of ViewGroup.LayoutParams, and these
* correspond to the different subclasses of ViewGroup that are responsible
* for arranging their children.
*
* @param params The layout parameters for this view, cannot be null
*/
public void setLayoutParams(ViewGroup.LayoutParams params) {
if (params == null) {
throw new NullPointerException("Layout parameters cannot be null");
}
mLayoutParams = params;
resolveLayoutParams();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onSetLayoutParams(this, params);
}
requestLayout();
}
/**
* Resolve the layout parameters depending on the resolved layout direction
*
* @hide
*/
public void resolveLayoutParams() {
if (mLayoutParams != null) {
mLayoutParams.resolveLayoutDirection(getLayoutDirection());
}
}
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* @param y the y position to scroll to
*/
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
/**
* Move the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the amount of pixels to scroll by horizontally
* @param y the amount of pixels to scroll by vertically
*/
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
/**
* Trigger the scrollbars to draw. When invoked this method starts an
* animation to fade the scrollbars out after a default delay. If a subclass
* provides animated scrolling, the start delay should equal the duration
* of the scrolling animation. The animation starts only if at least one of the scrollbars is
* enabled, as specified by {@link #isHorizontalScrollBarEnabled()} and
* {@link #isVerticalScrollBarEnabled()}. When the animation is started,
* this method returns true, and false otherwise. If the animation is
* started, this method calls {@link #invalidate()}; in that case the
* caller should not call {@link #invalidate()}. This method should be invoked every time a subclass directly updates
* the scroll parameters. This method is automatically invoked by {@link #scrollBy(int, int)}
* and {@link #scrollTo(int, int)}.
* Trigger the scrollbars to draw. When invoked this method starts an
* animation to fade the scrollbars out after a fixed delay. If a subclass
* provides animated scrolling, the start delay should equal the duration of
* the scrolling animation.
*
* The animation starts only if at least one of the scrollbars is enabled,
* as specified by {@link #isHorizontalScrollBarEnabled()} and
* {@link #isVerticalScrollBarEnabled()}. When the animation is started,
* this method returns true, and false otherwise. If the animation is
* started, this method calls {@link #invalidate()}; in that case the caller
* should not call {@link #invalidate()}.
*
* This method should be invoked every time a subclass directly updates the
* scroll parameters.
*
* Trigger the scrollbars to draw. When invoked this method starts an
* animation to fade the scrollbars out after a fixed delay. If a subclass
* provides animated scrolling, the start delay should equal the duration of
* the scrolling animation.
*
* The animation starts only if at least one of the scrollbars is enabled,
* as specified by {@link #isHorizontalScrollBarEnabled()} and
* {@link #isVerticalScrollBarEnabled()}. When the animation is started,
* this method returns true, and false otherwise. If the animation is
* started, this method calls {@link #invalidate()} if the invalidate parameter
* is set to true; in that case the caller
* should not call {@link #invalidate()}.
*
* This method should be invoked every time a subclass directly updates the
* scroll parameters.
*
* This must be called from a UI thread. To call from a non-UI thread, call
* {@link #postInvalidate()}.
*
* WARNING: In API 19 and below, this method may be destructive to
* {@code dirty}.
*
* @param dirty the rectangle representing the bounds of the dirty region
*
* @deprecated The switch to hardware accelerated rendering in API 14 reduced
* the importance of the dirty rectangle. In API 21 the given rectangle is
* ignored entirely in favor of an internally-calculated area instead.
* Because of this, clients are encouraged to just call {@link #invalidate()}.
*/
@Deprecated
public void invalidate(Rect dirty) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,
dirty.right - scrollX, dirty.bottom - scrollY, true, false);
}
/**
* Mark the area defined by the rect (l,t,r,b) as needing to be drawn. The
* coordinates of the dirty rect are relative to the view. If the view is
* visible, {@link #onDraw(android.graphics.Canvas)} will be called at some
* point in the future.
*
* This must be called from a UI thread. To call from a non-UI thread, call
* {@link #postInvalidate()}.
*
* @param l the left position of the dirty region
* @param t the top position of the dirty region
* @param r the right position of the dirty region
* @param b the bottom position of the dirty region
*
* @deprecated The switch to hardware accelerated rendering in API 14 reduced
* the importance of the dirty rectangle. In API 21 the given rectangle is
* ignored entirely in favor of an internally-calculated area instead.
* Because of this, clients are encouraged to just call {@link #invalidate()}.
*/
@Deprecated
public void invalidate(int l, int t, int r, int b) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
}
/**
* Invalidate the whole view. If the view is visible,
* {@link #onDraw(android.graphics.Canvas)} will be called at some point in
* the future.
*
* This must be called from a UI thread. To call from a non-UI thread, call
* {@link #postInvalidate()}.
*/
public void invalidate() {
invalidate(true);
}
/**
* This is where the invalidate() work actually happens. A full invalidate()
* causes the drawing cache to be invalidated, but this function can be
* called with invalidateCache set to false to skip that invalidation step
* for cases that do not need it (for example, a component that remains at
* the same dimensions with the same content).
*
* @param invalidateCache Whether the drawing cache for this view should be
* invalidated as well. This is usually true for a full
* invalidate, but may be set to false if the View's contents or
* dimensions have not changed.
* @hide
*/
@UnsupportedAppUsage
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
if (skipInvalidate()) {
return;
}
// Reset content capture caches
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
mContentCaptureSessionCached = false;
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
// Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
}
}
/**
* @return this view's projection receiver, or {@code null} if none exists
*/
private View getProjectionReceiver() {
ViewParent p = getParent();
while (p != null && p instanceof View) {
final View v = (View) p;
if (v.isProjectionReceiver()) {
return v;
}
p = p.getParent();
}
return null;
}
/**
* @return whether the view is a projection receiver
*/
private boolean isProjectionReceiver() {
return mBackground != null;
}
/**
* Quick invalidation for View property changes (alpha, translationXY, etc.). We don't want to
* set any flags or handle all of the cases handled by the default invalidation methods.
* Instead, we just want to schedule a traversal in ViewRootImpl with the appropriate
* dirty rect. This method calls into fast invalidation methods in ViewGroup that
* walk up the hierarchy, transforming the dirty rect as necessary.
*
* The method also handles normal invalidation logic if display list properties are not
* being used in this view. The invalidateParent and forceRedraw flags are used by that
* backup approach, to handle these cases used in the various property-setting methods.
*
* @param invalidateParent Force a call to invalidateParentCaches() if display list properties
* are not being used in this view
* @param forceRedraw Mark the view as DRAWN to force the invalidation to propagate, if display
* list properties are not being used in this view
*/
@UnsupportedAppUsage
void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
if (!isHardwareAccelerated()
|| !mRenderNode.hasDisplayList()
|| (mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
if (invalidateParent) {
invalidateParentCaches();
}
if (forceRedraw) {
mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation
}
invalidate(false);
} else {
damageInParent();
}
mPrivateFlags4 |= PFLAG4_HAS_VIEW_PROPERTY_INVALIDATION;
}
/**
* Tells the parent view to damage this view's bounds.
*
* @hide
*/
protected void damageInParent() {
if (mParent != null && mAttachInfo != null) {
mParent.onDescendantInvalidated(this, this);
}
}
/**
* Used to indicate that the parent of this view should clear its caches. This functionality
* is used to force the parent to rebuild its display list (when hardware-accelerated),
* which is necessary when various parent-managed properties of the view change, such as
* alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y. This method only
* clears the parent caches and does not causes an invalidate event.
*
* @hide
*/
@UnsupportedAppUsage
protected void invalidateParentCaches() {
if (mParent instanceof View) {
((View) mParent).mPrivateFlags |= PFLAG_INVALIDATED;
}
}
/**
* Used to indicate that the parent of this view should be invalidated. This functionality
* is used to force the parent to rebuild its display list (when hardware-accelerated),
* which is necessary when various parent-managed properties of the view change, such as
* alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y. This method will propagate
* an invalidation event to the parent.
*
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void invalidateParentIfNeeded() {
if (isHardwareAccelerated() && mParent instanceof View) {
((View) mParent).invalidate(true);
}
}
/**
* @hide
*/
protected void invalidateParentIfNeededAndWasQuickRejected() {
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) != 0) {
// View was rejected last time it was drawn by its parent; this may have changed
invalidateParentIfNeeded();
}
}
/**
* Indicates whether this View is opaque. An opaque View guarantees that it will
* draw all the pixels overlapping its bounds using a fully opaque color.
*
* Subclasses of View should override this method whenever possible to indicate
* whether an instance is opaque. Opaque Views are treated in a special way by
* the View hierarchy, possibly allowing it to perform optimizations during
* invalidate/draw passes.
*
* @return True if this View is guaranteed to be fully opaque, false otherwise.
*/
@ViewDebug.ExportedProperty(category = "drawing")
public boolean isOpaque() {
return (mPrivateFlags & PFLAG_OPAQUE_MASK) == PFLAG_OPAQUE_MASK &&
getFinalAlpha() >= 1.0f;
}
/**
* @hide
*/
@UnsupportedAppUsage
protected void computeOpaqueFlags() {
// Opaque if:
// - Has a background
// - Background is opaque
// - Doesn't have scrollbars or scrollbars overlay
if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {
mPrivateFlags |= PFLAG_OPAQUE_BACKGROUND;
} else {
mPrivateFlags &= ~PFLAG_OPAQUE_BACKGROUND;
}
final int flags = mViewFlags;
if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) ||
(flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY ||
(flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_OUTSIDE_OVERLAY) {
mPrivateFlags |= PFLAG_OPAQUE_SCROLLBARS;
} else {
mPrivateFlags &= ~PFLAG_OPAQUE_SCROLLBARS;
}
}
/**
* @hide
*/
protected boolean hasOpaqueScrollbars() {
return (mPrivateFlags & PFLAG_OPAQUE_SCROLLBARS) == PFLAG_OPAQUE_SCROLLBARS;
}
/**
* @return A handler associated with the thread running the View. This
* handler can be used to pump events in the UI events queue.
*/
public Handler getHandler() {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler;
}
return null;
}
/**
* Returns the queue of runnable for this view.
*
* @return the queue of runnables for this view
*/
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
/**
* Gets the view root associated with the View.
* @return The view root, or null if none.
* @hide
*/
@UnsupportedAppUsage
public ViewRootImpl getViewRootImpl() {
if (mAttachInfo != null) {
return mAttachInfo.mViewRootImpl;
}
return null;
}
/**
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ThreadedRenderer getThreadedRenderer() {
return mAttachInfo != null ? mAttachInfo.mThreadedRenderer : null;
}
/**
* Causes the Runnable to be added to the message queue.
* The runnable will be run on the user interface thread. Causes the Runnable to be added to the message queue, to be run
* after the specified amount of time elapses.
* The runnable will be run on the user interface thread. Causes the Runnable to execute on the next animation time step.
* The runnable will be run on the user interface thread. Causes the Runnable to execute on the next animation time step,
* after the specified amount of time elapses.
* The runnable will be run on the user interface thread. Removes the specified Runnable from the message queue. Cause an invalidate to happen on a subsequent cycle through the event loop.
* Use this to invalidate the View from a non-UI thread. This method can be invoked from outside of the UI thread
* only when this View is attached to a window. Cause an invalidate of the specified area to happen on a subsequent cycle
* through the event loop. Use this to invalidate the View from a non-UI thread. This method can be invoked from outside of the UI thread
* only when this View is attached to a window. Cause an invalidate to happen on a subsequent cycle through the event
* loop. Waits for the specified amount of time. This method can be invoked from outside of the UI thread
* only when this View is attached to a window. Cause an invalidate of the specified area to happen on a subsequent cycle
* through the event loop. Waits for the specified amount of time. This method can be invoked from outside of the UI thread
* only when this View is attached to a window. Cause an invalidate to happen on the next animation time step, typically the
* next display frame. This method can be invoked from outside of the UI thread
* only when this View is attached to a window. Cause an invalidate of the specified area to happen on the next animation
* time step, typically the next display frame. This method can be invoked from outside of the UI thread
* only when this View is attached to a window. Indicate whether the horizontal edges are faded when the view is
* scrolled horizontally. Define whether the horizontal edges should be faded when this view
* is scrolled horizontally. Indicate whether the vertical edges are faded when the view is
* scrolled horizontally. Define whether the vertical edges should be faded when this view
* is scrolled vertically. Indicate whether the horizontal scrollbar should be drawn or not. The
* scrollbar is not drawn by default. Define whether the horizontal scrollbar should be drawn or not. The
* scrollbar is not drawn by default. Indicate whether the vertical scrollbar should be drawn or not. The
* scrollbar is not drawn by default. Define whether the vertical scrollbar should be drawn or not. The
* scrollbar is not drawn by default. Specify the style of the scrollbars. The scrollbars can be overlaid or
* inset. When inset, they add to the padding of the view. And the scrollbars
* can be drawn inside the padding area or on the edge of the view. For example,
* if a view has a background drawable and you want to draw the scrollbars
* inside the padding specified by the drawable, you can use
* SCROLLBARS_INSIDE_OVERLAY or SCROLLBARS_INSIDE_INSET. If you want them to
* appear at the edge of the view, ignoring the padding, then you can use
* SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET. Returns the current scrollbar style. Compute the horizontal range that the horizontal scrollbar
* represents. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollExtent()} and
* {@link #computeHorizontalScrollOffset()}. The default range is the drawing width of this view. Compute the horizontal offset of the horizontal scrollbar's thumb
* within the horizontal range. This value is used to compute the position
* of the thumb within the scrollbar's track. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollRange()} and
* {@link #computeHorizontalScrollExtent()}. The default offset is the scroll offset of this view. Compute the horizontal extent of the horizontal scrollbar's thumb
* within the horizontal range. This value is used to compute the length
* of the thumb within the scrollbar's track. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeHorizontalScrollRange()} and
* {@link #computeHorizontalScrollOffset()}. The default extent is the drawing width of this view. Compute the vertical range that the vertical scrollbar represents. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeVerticalScrollExtent()} and
* {@link #computeVerticalScrollOffset()}. The default range is the drawing height of this view. Compute the vertical offset of the vertical scrollbar's thumb
* within the horizontal range. This value is used to compute the position
* of the thumb within the scrollbar's track. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeVerticalScrollRange()} and
* {@link #computeVerticalScrollExtent()}. The default offset is the scroll offset of this view. Compute the vertical extent of the vertical scrollbar's thumb
* within the vertical range. This value is used to compute the length
* of the thumb within the scrollbar's track. The range is expressed in arbitrary units that must be the same as the
* units used by {@link #computeVerticalScrollRange()} and
* {@link #computeVerticalScrollOffset()}. The default extent is the drawing height of this view. This is without regard to whether the view is enabled or not, or if it will scroll
* in response to user input or not.
*
* @param direction Negative to check scrolling left, positive to check scrolling right.
* @return true if this view can be scrolled in the specified direction, false otherwise.
*/
public boolean canScrollHorizontally(int direction) {
final int offset = computeHorizontalScrollOffset();
final int range = computeHorizontalScrollRange() - computeHorizontalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
/**
* Check if this view can be scrolled vertically in a certain direction.
*
* This is without regard to whether the view is enabled or not, or if it will scroll
* in response to user input or not.
*
* @param direction Negative to check scrolling up, positive to check scrolling down.
* @return true if this view can be scrolled in the specified direction, false otherwise.
*/
public boolean canScrollVertically(int direction) {
final int offset = computeVerticalScrollOffset();
final int range = computeVerticalScrollRange() - computeVerticalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
void getScrollIndicatorBounds(@NonNull Rect out) {
out.left = mScrollX;
out.right = mScrollX + mRight - mLeft;
out.top = mScrollY;
out.bottom = mScrollY + mBottom - mTop;
}
private void onDrawScrollIndicators(@NonNull Canvas c) {
if ((mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK) == 0) {
// No scroll indicators enabled.
return;
}
final Drawable dr = mScrollIndicatorDrawable;
if (dr == null) {
// Scroll indicators aren't supported here.
return;
}
if (mAttachInfo == null) {
// View is not attached.
return;
}
final int h = dr.getIntrinsicHeight();
final int w = dr.getIntrinsicWidth();
final Rect rect = mAttachInfo.mTmpInvalRect;
getScrollIndicatorBounds(rect);
if ((mPrivateFlags3 & PFLAG3_SCROLL_INDICATOR_TOP) != 0) {
final boolean canScrollUp = canScrollVertically(-1);
if (canScrollUp) {
dr.setBounds(rect.left, rect.top, rect.right, rect.top + h);
dr.draw(c);
}
}
if ((mPrivateFlags3 & PFLAG3_SCROLL_INDICATOR_BOTTOM) != 0) {
final boolean canScrollDown = canScrollVertically(1);
if (canScrollDown) {
dr.setBounds(rect.left, rect.bottom - h, rect.right, rect.bottom);
dr.draw(c);
}
}
final int leftRtl;
final int rightRtl;
if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
leftRtl = PFLAG3_SCROLL_INDICATOR_END;
rightRtl = PFLAG3_SCROLL_INDICATOR_START;
} else {
leftRtl = PFLAG3_SCROLL_INDICATOR_START;
rightRtl = PFLAG3_SCROLL_INDICATOR_END;
}
final int leftMask = PFLAG3_SCROLL_INDICATOR_LEFT | leftRtl;
if ((mPrivateFlags3 & leftMask) != 0) {
final boolean canScrollLeft = canScrollHorizontally(-1);
if (canScrollLeft) {
dr.setBounds(rect.left, rect.top, rect.left + w, rect.bottom);
dr.draw(c);
}
}
final int rightMask = PFLAG3_SCROLL_INDICATOR_RIGHT | rightRtl;
if ((mPrivateFlags3 & rightMask) != 0) {
final boolean canScrollRight = canScrollHorizontally(1);
if (canScrollRight) {
dr.setBounds(rect.right - w, rect.top, rect.right, rect.bottom);
dr.draw(c);
}
}
}
private void getHorizontalScrollBarBounds(@Nullable Rect drawBounds,
@Nullable Rect touchBounds) {
final Rect bounds = drawBounds != null ? drawBounds : touchBounds;
if (bounds == null) {
return;
}
final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled()
&& !isVerticalScrollBarHidden();
final int size = getHorizontalScrollbarHeight();
final int verticalScrollBarGap = drawVerticalScrollBar ?
getVerticalScrollbarWidth() : 0;
final int width = mRight - mLeft;
final int height = mBottom - mTop;
bounds.top = mScrollY + height - size - (mUserPaddingBottom & inside);
bounds.left = mScrollX + (mPaddingLeft & inside);
bounds.right = mScrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap;
bounds.bottom = bounds.top + size;
if (touchBounds == null) {
return;
}
if (touchBounds != bounds) {
touchBounds.set(bounds);
}
final int minTouchTarget = mScrollCache.scrollBarMinTouchTarget;
if (touchBounds.height() < minTouchTarget) {
final int adjust = (minTouchTarget - touchBounds.height()) / 2;
touchBounds.bottom = Math.min(touchBounds.bottom + adjust, mScrollY + height);
touchBounds.top = touchBounds.bottom - minTouchTarget;
}
if (touchBounds.width() < minTouchTarget) {
final int adjust = (minTouchTarget - touchBounds.width()) / 2;
touchBounds.left -= adjust;
touchBounds.right = touchBounds.left + minTouchTarget;
}
}
private void getVerticalScrollBarBounds(@Nullable Rect bounds, @Nullable Rect touchBounds) {
if (mRoundScrollbarRenderer == null) {
getStraightVerticalScrollBarBounds(bounds, touchBounds);
} else {
mRoundScrollbarRenderer.getRoundVerticalScrollBarBounds(
bounds != null ? bounds : touchBounds);
}
}
private void getStraightVerticalScrollBarBounds(@Nullable Rect drawBounds,
@Nullable Rect touchBounds) {
final Rect bounds = drawBounds != null ? drawBounds : touchBounds;
if (bounds == null) {
return;
}
final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
final int size = getVerticalScrollbarWidth();
int verticalScrollbarPosition = mVerticalScrollbarPosition;
if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) {
verticalScrollbarPosition = isLayoutRtl() ?
SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT;
}
final int width = mRight - mLeft;
final int height = mBottom - mTop;
switch (verticalScrollbarPosition) {
default:
case SCROLLBAR_POSITION_RIGHT:
bounds.left = mScrollX + width - size - (mUserPaddingRight & inside);
break;
case SCROLLBAR_POSITION_LEFT:
bounds.left = mScrollX + (mUserPaddingLeft & inside);
break;
}
bounds.top = mScrollY + (mPaddingTop & inside);
bounds.right = bounds.left + size;
bounds.bottom = mScrollY + height - (mUserPaddingBottom & inside);
if (touchBounds == null) {
return;
}
if (touchBounds != bounds) {
touchBounds.set(bounds);
}
final int minTouchTarget = mScrollCache.scrollBarMinTouchTarget;
if (touchBounds.width() < minTouchTarget) {
final int adjust = (minTouchTarget - touchBounds.width()) / 2;
if (verticalScrollbarPosition == SCROLLBAR_POSITION_RIGHT) {
touchBounds.right = Math.min(touchBounds.right + adjust, mScrollX + width);
touchBounds.left = touchBounds.right - minTouchTarget;
} else {
touchBounds.left = Math.max(touchBounds.left + adjust, mScrollX);
touchBounds.right = touchBounds.left + minTouchTarget;
}
}
if (touchBounds.height() < minTouchTarget) {
final int adjust = (minTouchTarget - touchBounds.height()) / 2;
touchBounds.top -= adjust;
touchBounds.bottom = touchBounds.top + minTouchTarget;
}
}
/**
* Request the drawing of the horizontal and the vertical scrollbar. The
* scrollbars are painted only if they have been awakened first. Draw the horizontal scrollbar if
* {@link #isHorizontalScrollBarEnabled()} returns true. Draw the vertical scrollbar if {@link #isVerticalScrollBarEnabled()}
* returns true. This call will be followed by {@link #onConfigurationChanged(Configuration)} if the
* applied configuration actually changed. It is up to app developer to choose whether to handle
* the change in this method or in the following {@link #onConfigurationChanged(Configuration)}
* call.
*
* Use this callback to track changes to the displays if some functionality relies on an
* association with some display properties.
*
* @param displayId The id of the display to which the view was moved.
* @param config Configuration of the resources on new display after move.
*
* @see #onConfigurationChanged(Configuration)
* @hide
*/
public void onMovedToDisplay(int displayId, Configuration config) {
}
/**
* Return true if the application tag in the AndroidManifest has set "supportRtl" to true
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean hasRtlSupport() {
return mContext.getApplicationInfo().hasRtlSupport();
}
/**
* Return true if we are in RTL compatibility mode (either before Jelly Bean MR1 or
* RTL not supported)
*/
private boolean isRtlCompatibilityMode() {
return !hasRtlSupport();
}
/**
* @return true if RTL properties need resolution.
*
*/
private boolean needRtlPropertiesResolution() {
return (mPrivateFlags2 & ALL_RTL_PROPERTIES_RESOLVED) != ALL_RTL_PROPERTIES_RESOLVED;
}
/**
* Called when any RTL property (layout direction or text direction or text alignment) has
* been changed.
*
* Subclasses need to override this method to take care of cached information that depends on the
* resolved layout direction, or to inform child views that inherit their layout direction.
*
* The default implementation does nothing.
*
* @param layoutDirection the direction of the layout
*
* @see #LAYOUT_DIRECTION_LTR
* @see #LAYOUT_DIRECTION_RTL
*/
public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
}
/**
* Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing
* that the parent directionality can and will be resolved before its children.
*
* @return true if resolution has been done, false otherwise.
*
* @hide
*/
public boolean resolveLayoutDirection() {
// Clear any previous layout direction resolution
mPrivateFlags2 &= ~PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK;
if (hasRtlSupport()) {
// Set resolved depending on layout direction
switch ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK) >>
PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) {
case LAYOUT_DIRECTION_INHERIT:
// We cannot resolve yet. LTR is by default and let the resolution happen again
// later to get the correct resolved value
if (!canResolveLayoutDirection()) return false;
// Parent has not yet resolved, LTR is still the default
try {
if (!mParent.isLayoutDirectionResolved()) return false;
if (mParent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL;
}
} catch (AbstractMethodError e) {
Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
" does not fully implement ViewParent", e);
}
break;
case LAYOUT_DIRECTION_RTL:
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL;
break;
case LAYOUT_DIRECTION_LOCALE:
if((LAYOUT_DIRECTION_RTL ==
TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()))) {
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL;
}
break;
default:
// Nothing to do, LTR by default
}
}
// Set to resolved
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED;
return true;
}
/**
* Check if layout direction resolution can be done.
*
* @return true if layout direction resolution can be done otherwise return false.
*/
public boolean canResolveLayoutDirection() {
switch (getRawLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
if (mParent != null) {
try {
return mParent.canResolveLayoutDirection();
} catch (AbstractMethodError e) {
Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
" does not fully implement ViewParent", e);
}
}
return false;
default:
return true;
}
}
/**
* Reset the resolved layout direction. Layout direction will be resolved during a call to
* {@link #onMeasure(int, int)}.
*
* @hide
*/
@TestApi
public void resetResolvedLayoutDirection() {
// Reset the current resolved bits
mPrivateFlags2 &= ~PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK;
}
/**
* @return true if the layout direction is inherited.
*
* @hide
*/
public boolean isLayoutDirectionInherited() {
return (getRawLayoutDirection() == LAYOUT_DIRECTION_INHERIT);
}
/**
* @return true if layout direction has been resolved.
*/
public boolean isLayoutDirectionResolved() {
return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED) == PFLAG2_LAYOUT_DIRECTION_RESOLVED;
}
/**
* Return if padding has been resolved
*
* @hide
*/
@UnsupportedAppUsage
boolean isPaddingResolved() {
return (mPrivateFlags2 & PFLAG2_PADDING_RESOLVED) == PFLAG2_PADDING_RESOLVED;
}
/**
* Resolves padding depending on layout direction, if applicable, and
* recomputes internal padding values to adjust for scroll bars.
*
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void resolvePadding() {
final int resolvedLayoutDirection = getLayoutDirection();
if (!isRtlCompatibilityMode()) {
// Post Jelly Bean MR1 case: we need to take the resolved layout direction into account.
// If start / end padding are defined, they will be resolved (hence overriding) to
// left / right or right / left depending on the resolved layout direction.
// If start / end padding are not defined, use the left / right ones.
if (mBackground != null && (!mLeftPaddingDefined || !mRightPaddingDefined)) {
Rect padding = sThreadLocal.get();
if (padding == null) {
padding = new Rect();
sThreadLocal.set(padding);
}
mBackground.getPadding(padding);
if (!mLeftPaddingDefined) {
mUserPaddingLeftInitial = padding.left;
}
if (!mRightPaddingDefined) {
mUserPaddingRightInitial = padding.right;
}
}
switch (resolvedLayoutDirection) {
case LAYOUT_DIRECTION_RTL:
if (mUserPaddingStart != UNDEFINED_PADDING) {
mUserPaddingRight = mUserPaddingStart;
} else {
mUserPaddingRight = mUserPaddingRightInitial;
}
if (mUserPaddingEnd != UNDEFINED_PADDING) {
mUserPaddingLeft = mUserPaddingEnd;
} else {
mUserPaddingLeft = mUserPaddingLeftInitial;
}
break;
case LAYOUT_DIRECTION_LTR:
default:
if (mUserPaddingStart != UNDEFINED_PADDING) {
mUserPaddingLeft = mUserPaddingStart;
} else {
mUserPaddingLeft = mUserPaddingLeftInitial;
}
if (mUserPaddingEnd != UNDEFINED_PADDING) {
mUserPaddingRight = mUserPaddingEnd;
} else {
mUserPaddingRight = mUserPaddingRightInitial;
}
}
mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
}
internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom);
onRtlPropertiesChanged(resolvedLayoutDirection);
mPrivateFlags2 |= PFLAG2_PADDING_RESOLVED;
}
/**
* Reset the resolved layout direction.
*
* @hide
*/
@TestApi
public void resetResolvedPadding() {
resetResolvedPaddingInternal();
}
/**
* Used when we only want to reset *this* view's padding and not trigger overrides
* in ViewGroup that reset children too.
*/
void resetResolvedPaddingInternal() {
mPrivateFlags2 &= ~PFLAG2_PADDING_RESOLVED;
}
/**
* This is called when the view is detached from a window. At this point it
* no longer has a surface for drawing.
*
* @see #onAttachedToWindow()
*/
@CallSuper
protected void onDetachedFromWindow() {
}
/**
* This is a framework-internal mirror of onDetachedFromWindow() that's called
* after onDetachedFromWindow().
*
* If you override this you *MUST* call super.onDetachedFromWindowInternal()!
* The super method should be called at the end of the overridden method to ensure
* subclasses are destroyed first
*
* @hide
*/
@CallSuper
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void onDetachedFromWindowInternal() {
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
removeUnsetPressCallback();
removeLongPressCallback();
removePerformClickCallback();
clearAccessibilityThrottles();
stopNestedScroll();
// Anything that started animating right before detach should already
// be in its final state when re-attached.
jumpDrawablesToCurrentState();
destroyDrawingCache();
cleanupDraw();
mCurrentAnimation = null;
if ((mViewFlags & TOOLTIP) == TOOLTIP) {
removeCallbacks(mTooltipInfo.mShowTooltipRunnable);
removeCallbacks(mTooltipInfo.mHideTooltipRunnable);
hideTooltip();
}
AccessibilityNodeIdManager.getInstance().unregisterViewWithId(getAccessibilityViewId());
if (mBackgroundRenderNode != null) {
mBackgroundRenderNode.forceEndAnimators();
}
mRenderNode.forceEndAnimators();
}
private void cleanupDraw() {
resetDisplayList();
if (mAttachInfo != null) {
mAttachInfo.mViewRootImpl.cancelInvalidate(this);
}
}
void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
}
/**
* @return The number of times this view has been attached to a window
*/
protected int getWindowAttachCount() {
return mWindowAttachCount;
}
/**
* Retrieve a unique token identifying the window this view is attached to.
* @return Return the window's token for use in
* {@link WindowManager.LayoutParams#token WindowManager.LayoutParams.token}.
* This token maybe null if this view is not attached to a window.
* @see #isAttachedToWindow() for current window attach state
* @see OnAttachStateChangeListener to listen to window attach/detach state changes
*/
public IBinder getWindowToken() {
return mAttachInfo != null ? mAttachInfo.mWindowToken : null;
}
/**
* Retrieve the {@link WindowId} for the window this view is
* currently attached to.
*/
public WindowId getWindowId() {
AttachInfo ai = mAttachInfo;
if (ai == null) {
return null;
}
if (ai.mWindowId == null) {
try {
ai.mIWindowId = ai.mSession.getWindowId(ai.mWindowToken);
if (ai.mIWindowId != null) {
ai.mWindowId = new WindowId(ai.mIWindowId);
}
} catch (RemoteException e) {
}
}
return ai.mWindowId;
}
/**
* Retrieve a unique token identifying the top-level "real" window of
* the window that this view is attached to. That is, this is like
* {@link #getWindowToken}, except if the window this view in is a panel
* window (attached to another containing window), then the token of
* the containing window is returned instead.
*
* @return Returns the associated window token, either
* {@link #getWindowToken()} or the containing window's token.
*/
public IBinder getApplicationWindowToken() {
AttachInfo ai = mAttachInfo;
if (ai != null) {
IBinder appWindowToken = ai.mPanelParentWindowToken;
if (appWindowToken == null) {
appWindowToken = ai.mWindowToken;
}
return appWindowToken;
}
return null;
}
/**
* Gets the logical display to which the view's window has been attached.
*
* @return The logical display, or null if the view is not currently attached to a window.
*/
public Display getDisplay() {
return mAttachInfo != null ? mAttachInfo.mDisplay : null;
}
/**
* Retrieve private session object this view hierarchy is using to
* communicate with the window manager.
* @return the session object to communicate with the window manager
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
/*package*/ IWindowSession getWindowSession() {
return mAttachInfo != null ? mAttachInfo.mSession : null;
}
/**
* Return the window this view is currently attached to.
* @hide
*/
protected IWindow getWindow() {
return mAttachInfo != null ? mAttachInfo.mWindow : null;
}
/**
* Return the visibility value of the least visible component passed.
*/
int combineVisibility(int vis1, int vis2) {
// This works because VISIBLE < INVISIBLE < GONE.
return Math.max(vis1, vis2);
}
private boolean mShouldFakeFocus = false;
/**
* Fake send a focus event after attaching to window.
* See {@link android.view.ViewRootImpl#dispatchCompatFakeFocus()} for details.
* @hide
*/
public void fakeFocusAfterAttachingToWindow() {
mShouldFakeFocus = true;
}
/**
* @param info the {@link android.view.View.AttachInfo} to associated with
* this view
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
}
mWindowAttachCount++;
// We will need to evaluate the drawable state at least once.
mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
if (mFloatingTreeObserver != null) {
info.mTreeObserver.merge(mFloatingTreeObserver);
mFloatingTreeObserver = null;
}
registerPendingFrameMetricsObservers();
if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {
mAttachInfo.mScrollContainers.add(this);
mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;
}
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
ListenerInfo li = mListenerInfo;
final CopyOnWriteArrayList Many views post high-level events such as click handlers to the event queue
* to run deferred in order to preserve a desired user experience - clearing visible
* pressed states before executing, etc. This method will abort any events of this nature
* that are currently in flight. Custom views that generate their own high-level deferred input events should override
* {@link #onCancelPendingInputEvents()} and remove those pending events from the queue. This will also cancel pending input events for any child views. Note that this may not be sufficient as a debouncing strategy for clicks in all cases.
* This will not impact newer events posted after this call that may occur as a result of
* lower-level input events still waiting in the queue. If you are trying to prevent
* double-submitted events for the duration of some sort of asynchronous transaction
* you should also take other steps to protect against unexpected double inputs e.g. calling
* {@link #setEnabled(boolean) setEnabled(false)} and re-enabling the view when
* the transaction completes, tracking already submitted transaction IDs, etc. This method is responsible for removing any pending high-level input events that were
* posted to the event queue to run later. Custom view classes that post their own deferred
* high-level events via {@link #post(Runnable)}, {@link #postDelayed(Runnable, long)} or
* {@link android.os.Handler} should override this method, call
*
* Some examples of things you may store here: the current cursor position
* in a text view (but usually not the text itself since that is stored in a
* content provider or other persistent storage), the currently selected
* item in a list view.
*
* @return Returns a Parcelable object containing the view's current dynamic
* state, or null if there is nothing interesting to save.
* @see #onRestoreInstanceState(Parcelable)
* @see #saveHierarchyState(SparseArray)
* @see #dispatchSaveInstanceState(SparseArray)
* @see #setSaveEnabled(boolean)
*/
@CallSuper
@Nullable protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (mStartActivityRequestWho != null || isAutofilled()
|| mAutofillViewId > LAST_APP_AUTOFILL_ID) {
BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
if (mStartActivityRequestWho != null) {
state.mSavedData |= BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED;
}
if (isAutofilled()) {
state.mSavedData |= BaseSavedState.IS_AUTOFILLED;
}
if (mAutofillViewId > LAST_APP_AUTOFILL_ID) {
state.mSavedData |= BaseSavedState.AUTOFILL_ID;
}
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
state.mIsAutofilled = isAutofilled();
state.mHideHighlight = hideAutofillHighlight();
state.mAutofillViewId = mAutofillViewId;
return state;
}
return BaseSavedState.EMPTY_STATE;
}
/**
* Restore this view hierarchy's frozen state from the given container.
*
* @param container The SparseArray which holds previously frozen states.
*
* @see #saveHierarchyState(android.util.SparseArray)
* @see #dispatchRestoreInstanceState(android.util.SparseArray)
* @see #onRestoreInstanceState(android.os.Parcelable)
*/
public void restoreHierarchyState(SparseArray Return the time at which the drawing of the view hierarchy started. Enables or disables the duplication of the parent's state into this view. When
* duplication is enabled, this view gets its drawable state from its parent rather
* than from its own internal properties. Note: in the current implementation, setting this property to true after the
* view was added to a ViewGroup might have no effect at all. This property should
* always be used from XML or set to true before adding this view to a ViewGroup. Note: if this view's parent addStateFromChildren property is enabled and this
* property is enabled, an exception will be thrown. Note: if the child view uses and updates additional states which are unknown to the
* parent, these states should not be affected by this method. Indicates whether this duplicates its drawable state from its parent. Specifies the type of layer backing this view. The layer can be
* {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
* {@link #LAYER_TYPE_HARDWARE}. A layer is associated with an optional {@link android.graphics.Paint}
* instance that controls how the layer is composed on screen. The following
* properties of the paint are taken into account when composing the layer: If this view has an alpha value set to < 1.0 by calling
* {@link #setAlpha(float)}, the alpha value of the layer's paint is superseded
* by this view's alpha value. Refer to the documentation of {@link #LAYER_TYPE_NONE},
* {@link #LAYER_TYPE_SOFTWARE} and {@link #LAYER_TYPE_HARDWARE}
* for more information on when and how to use layers. A layer is associated with an optional {@link android.graphics.Paint}
* instance that controls how the layer is composed on screen. The following
* properties of the paint are taken into account when composing the layer: If this view has an alpha value set to < 1.0 by calling {@link #setAlpha(float)}, the
* alpha value of the layer's paint is superseded by this view's alpha value. Enables or disables the drawing cache. When the drawing cache is enabled, the next call
* to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a
* bitmap. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when
* the cache is enabled. To benefit from the cache, you must request the drawing cache by
* calling {@link #getDrawingCache()} and draw it on screen if the returned bitmap is not
* null. Enabling the drawing cache is similar to
* {@link #setLayerType(int, android.graphics.Paint) setting a layer} when hardware
* acceleration is turned off. When hardware acceleration is turned on, enabling the
* drawing cache has no effect on rendering because the system uses a different mechanism
* for acceleration which ignores the flag. If you want to use a Bitmap for the view, even
* when hardware acceleration is enabled, see {@link #setLayerType(int, android.graphics.Paint)}
* for information on how to enable software and hardware layers. This API can be used to manually generate
* a bitmap copy of this view, by setting the flag to Indicates whether the drawing cache is enabled for this view. Calling this method is equivalent to calling Returns the bitmap in which this view drawing is cached. The returned bitmap
* is null when caching is disabled. If caching is enabled and the cache is not ready,
* this method will create it. Calling {@link #draw(android.graphics.Canvas)} will not
* draw from the cache when the cache is enabled. To benefit from the cache, you must
* request the drawing cache by calling this method and draw it on screen if the
* returned bitmap is not null. Note about auto scaling in compatibility mode: When auto scaling is not enabled,
* this method will create a bitmap of the same size as this view. Because this bitmap
* will be drawn scaled by the parent ViewGroup, the result on screen might show
* scaling artifacts. To avoid such artifacts, you should call this method by setting
* the auto scaling to true. Doing so, however, will generate a bitmap of a different
* size than the view. This implies that your application must be able to handle this
* size. Frees the resources used by the drawing cache. If you call
* {@link #buildDrawingCache()} manually without calling
* {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you
* should cleanup the cache with this method afterwards. Calling this method is equivalent to calling Forces the drawing cache to be built if the drawing cache is invalid. If you call {@link #buildDrawingCache()} manually without calling
* {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you
* should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards. Note about auto scaling in compatibility mode: When auto scaling is not enabled,
* this method will create a bitmap of the same size as this view. Because this bitmap
* will be drawn scaled by the parent ViewGroup, the result on screen might show
* scaling artifacts. To avoid such artifacts, you should call this method by setting
* the auto scaling to true. Doing so, however, will generate a bitmap of a different
* size than the view. This implies that your application must be able to handle this
* size. You should avoid calling this method when hardware acceleration is enabled. If
* you do not need the drawing cache bitmap, calling this method will increase memory
* usage and cause the view to be rendered in software once, thus negatively impacting
* performance. Indicates whether this view is attached to a hardware accelerated
* window or not. Even if this method returns true, it does not mean that every call
* to {@link #draw(android.graphics.Canvas)} will be made with an hardware
* accelerated {@link android.graphics.Canvas}. For instance, if this view
* is drawn onto an offscreen {@link android.graphics.Bitmap} and its
* window is hardware accelerated,
* {@link android.graphics.Canvas#isHardwareAccelerated()} will likely
* return false, and this method will return true. Note: Overlays do not currently work correctly with {@link
* SurfaceView} or {@link TextureView}; contents in overlays for these
* types of views may not display correctly. Indicates whether or not this view's layout will be requested during
* the next hierarchy layout pass.
* When set, a {@link Trace} instant event and a log with the stacktrace is emitted every
* time a requestLayout of a class matching {@code s} name happens.
* This applies only to views attached from this point onwards.
*
* @see Trace#instant(long, String)
* @hide
*/
public static void setTracedRequestLayoutClassClass(String s) {
sTraceRequestLayoutClass = s;
}
private boolean setOpticalFrame(int left, int top, int right, int bottom) {
Insets parentInsets = mParent instanceof View ?
((View) mParent).getOpticalInsets() : Insets.NONE;
Insets childInsets = getOpticalInsets();
return setFrame(
left + parentInsets.left - childInsets.left,
top + parentInsets.top - childInsets.top,
right + parentInsets.left + childInsets.right,
bottom + parentInsets.top + childInsets.bottom);
}
/**
* Assign a size and position to a view and all of its
* descendants
*
* This is the second phase of the layout mechanism.
* (The first is measuring). In this phase, each parent calls
* layout on all of its children to position them.
* This is typically done using the child measurements
* that were stored in the measure pass(). Derived classes should not override this method.
* Derived classes with children should override
* onLayout. In that method, they should
* call layout on each of their children. Even if the subclass overrides onFinishInflate, they should always be
* sure to call the super method, so that we get called.
*/
@CallSuper
protected void onFinishInflate() {
}
/**
* Returns the resources associated with this view.
*
* @return Resources object.
*/
public Resources getResources() {
return mResources;
}
/**
* Invalidates the specified Drawable.
*
* @param drawable the drawable to invalidate
*/
@Override
public void invalidateDrawable(@NonNull Drawable drawable) {
if (verifyDrawable(drawable)) {
final Rect dirty = drawable.getDirtyBounds();
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidate(dirty.left + scrollX, dirty.top + scrollY,
dirty.right + scrollX, dirty.bottom + scrollY);
rebuildOutline();
}
}
/**
* Schedules an action on a drawable to occur at a specified time.
*
* @param who the recipient of the action
* @param what the action to run on the drawable
* @param when the time at which the action must occur. Uses the
* {@link SystemClock#uptimeMillis} timebase.
*/
@Override
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
if (verifyDrawable(who) && what != null) {
final long delay = when - SystemClock.uptimeMillis();
if (mAttachInfo != null) {
mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
Choreographer.CALLBACK_ANIMATION, what, who,
Choreographer.subtractFrameDelay(delay));
} else {
// Postpone the runnable until we know
// on which thread it needs to run.
getRunQueue().postDelayed(what, delay);
}
}
}
/**
* Cancels a scheduled action on a drawable.
*
* @param who the recipient of the action
* @param what the action to cancel
*/
@Override
public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
if (verifyDrawable(who) && what != null) {
if (mAttachInfo != null) {
mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
Choreographer.CALLBACK_ANIMATION, what, who);
}
getRunQueue().removeCallbacks(what);
}
}
/**
* Unschedule any events associated with the given Drawable. This can be
* used when selecting a new Drawable into a view, so that the previous
* one is completely unscheduled.
*
* @param who The Drawable to unschedule.
*
* @see #drawableStateChanged
*/
public void unscheduleDrawable(Drawable who) {
if (mAttachInfo != null && who != null) {
mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
Choreographer.CALLBACK_ANIMATION, null, who);
}
}
/**
* Resolve the Drawables depending on the layout direction. This is implicitly supposing
* that the View directionality can and will be resolved before its Drawables.
*
* Will call {@link View#onResolveDrawables} when resolution is done.
*
* @hide
*/
protected void resolveDrawables() {
// Drawables resolution may need to happen before resolving the layout direction (which is
// done only during the measure() call).
// If the layout direction is not resolved yet, we cannot resolve the Drawables except in
// one case: when the raw layout direction has not been defined as LAYOUT_DIRECTION_INHERIT.
// So, if the raw layout direction is LAYOUT_DIRECTION_LTR or LAYOUT_DIRECTION_RTL or
// LAYOUT_DIRECTION_LOCALE, we can "cheat" and we don't need to wait for the layout
// direction to be resolved as its resolved value will be the same as its raw value.
if (!isLayoutDirectionResolved() &&
getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT) {
return;
}
final int layoutDirection = isLayoutDirectionResolved() ?
getLayoutDirection() : getRawLayoutDirection();
if (mBackground != null) {
mBackground.setLayoutDirection(layoutDirection);
}
if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
mForegroundInfo.mDrawable.setLayoutDirection(layoutDirection);
}
if (mDefaultFocusHighlight != null) {
mDefaultFocusHighlight.setLayoutDirection(layoutDirection);
}
mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
onResolveDrawables(layoutDirection);
}
boolean areDrawablesResolved() {
return (mPrivateFlags2 & PFLAG2_DRAWABLE_RESOLVED) == PFLAG2_DRAWABLE_RESOLVED;
}
/**
* Called when layout direction has been resolved.
*
* The default implementation does nothing.
*
* @param layoutDirection The resolved layout direction.
*
* @see #LAYOUT_DIRECTION_LTR
* @see #LAYOUT_DIRECTION_RTL
*
* @hide
*/
public void onResolveDrawables(@ResolvedLayoutDir int layoutDirection) {
}
/**
* @hide
*/
@TestApi
protected void resetResolvedDrawables() {
resetResolvedDrawablesInternal();
}
void resetResolvedDrawablesInternal() {
mPrivateFlags2 &= ~PFLAG2_DRAWABLE_RESOLVED;
}
/**
* If your view subclass is displaying its own Drawable objects, it should
* override this function and return true for any Drawable it is
* displaying. This allows animations for those drawables to be
* scheduled.
*
* Be sure to call through to the super class when overriding this
* function.
*
* @param who The Drawable to verify. Return true if it is one you are
* displaying, else return the result of calling through to the
* super class.
*
* @return boolean If true then the Drawable is being displayed in the
* view; else false and it is not allowed to animate.
*
* @see #unscheduleDrawable(android.graphics.drawable.Drawable)
* @see #drawableStateChanged()
*/
@CallSuper
protected boolean verifyDrawable(@NonNull Drawable who) {
// Avoid verifying the scroll bar drawable so that we don't end up in
// an invalidation loop. This effectively prevents the scroll bar
// drawable from triggering invalidations and scheduling runnables.
return who == mBackground || (mForegroundInfo != null && mForegroundInfo.mDrawable == who)
|| (mDefaultFocusHighlight == who);
}
/**
* This function is called whenever the state of the view changes in such
* a way that it impacts the state of drawables being shown.
*
* If the View has a StateListAnimator, it will also be called to run necessary state
* change animations.
*
* Be sure to call through to the superclass when overriding this function.
*
* @see Drawable#setState(int[])
*/
@CallSuper
protected void drawableStateChanged() {
final int[] state = getDrawableState();
boolean changed = false;
final Drawable bg = mBackground;
if (bg != null && bg.isStateful()) {
changed |= bg.setState(state);
}
final Drawable hl = mDefaultFocusHighlight;
if (hl != null && hl.isStateful()) {
changed |= hl.setState(state);
}
final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
if (fg != null && fg.isStateful()) {
changed |= fg.setState(state);
}
if (mScrollCache != null) {
final Drawable scrollBar = mScrollCache.scrollBar;
if (scrollBar != null && scrollBar.isStateful()) {
changed |= scrollBar.setState(state)
&& mScrollCache.state != ScrollabilityCache.OFF;
}
}
if (mStateListAnimator != null) {
mStateListAnimator.setState(state);
}
if (!isAggregatedVisible()) {
// If we're not visible, skip any animated changes
jumpDrawablesToCurrentState();
}
if (changed) {
invalidate();
}
}
/**
* This function is called whenever the view hotspot changes and needs to
* be propagated to drawables or child views managed by the view.
*
* Dispatching to child views is handled by
* {@link #dispatchDrawableHotspotChanged(float, float)}.
*
* Be sure to call through to the superclass when overriding this function.
*
* @param x hotspot x coordinate
* @param y hotspot y coordinate
*/
@CallSuper
public void drawableHotspotChanged(float x, float y) {
if (mBackground != null) {
mBackground.setHotspot(x, y);
}
if (mDefaultFocusHighlight != null) {
mDefaultFocusHighlight.setHotspot(x, y);
}
if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
mForegroundInfo.mDrawable.setHotspot(x, y);
}
dispatchDrawableHotspotChanged(x, y);
}
/**
* Dispatches drawableHotspotChanged to all of this View's children.
*
* @param x hotspot x coordinate
* @param y hotspot y coordinate
* @see #drawableHotspotChanged(float, float)
*/
public void dispatchDrawableHotspotChanged(float x, float y) {
}
/**
* Call this to force a view to update its drawable state. This will cause
* drawableStateChanged to be called on this view. Views that are interested
* in the new state should call getDrawableState.
*
* @see #drawableStateChanged
* @see #getDrawableState
*/
public void refreshDrawableState() {
mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
drawableStateChanged();
ViewParent parent = mParent;
if (parent != null) {
parent.childDrawableStateChanged(this);
}
}
/**
* Create a default focus highlight if it doesn't exist.
* @return a default focus highlight.
*/
private Drawable getDefaultFocusHighlightDrawable() {
if (mDefaultFocusHighlightCache == null) {
if (mContext != null) {
final int[] attrs = new int[] { android.R.attr.selectableItemBackground };
final TypedArray ta = mContext.obtainStyledAttributes(attrs);
mDefaultFocusHighlightCache = ta.getDrawable(0);
ta.recycle();
}
}
return mDefaultFocusHighlightCache;
}
/**
* Set the current default focus highlight.
* @param highlight the highlight drawable, or {@code null} if it's no longer needed.
*/
private void setDefaultFocusHighlight(Drawable highlight) {
mDefaultFocusHighlight = highlight;
mDefaultFocusHighlightSizeChanged = true;
if (highlight != null) {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
highlight.setLayoutDirection(getLayoutDirection());
if (highlight.isStateful()) {
highlight.setState(getDrawableState());
}
if (isAttachedToWindow()) {
highlight.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
// Set callback last, since the view may still be initializing.
highlight.setCallback(this);
} else if ((mViewFlags & WILL_NOT_DRAW) != 0 && mBackground == null
&& (mForegroundInfo == null || mForegroundInfo.mDrawable == null)) {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
invalidate();
}
/**
* Check whether we need to draw a default focus highlight when this view gets focused,
* which requires:
*
* This always happens when this view is focused, and only at this moment the default focus
* highlight can be visible.
*/
private void switchDefaultFocusHighlight() {
if (isFocused()) {
final boolean needed = isDefaultFocusHighlightNeeded(mBackground,
mForegroundInfo == null ? null : mForegroundInfo.mDrawable);
final boolean active = mDefaultFocusHighlight != null;
if (needed && !active) {
setDefaultFocusHighlight(getDefaultFocusHighlightDrawable());
} else if (!needed && active) {
// The highlight is no longer needed, so tear it down.
setDefaultFocusHighlight(null);
}
}
}
/**
* Draw the default focus highlight onto the canvas if there is one and this view is focused.
* @param canvas the canvas where we're drawing the highlight.
*/
private void drawDefaultFocusHighlight(@NonNull Canvas canvas) {
if (mDefaultFocusHighlight != null && isFocused()) {
if (mDefaultFocusHighlightSizeChanged) {
mDefaultFocusHighlightSizeChanged = false;
final int l = mScrollX;
final int r = l + mRight - mLeft;
final int t = mScrollY;
final int b = t + mBottom - mTop;
mDefaultFocusHighlight.setBounds(l, t, r, b);
}
mDefaultFocusHighlight.draw(canvas);
}
}
/**
* Return an array of resource IDs of the drawable states representing the
* current state of the view.
*
* @return The current drawable state
*
* @see Drawable#setState(int[])
* @see #drawableStateChanged()
* @see #onCreateDrawableState(int)
*/
public final int[] getDrawableState() {
if ((mDrawableState != null) && ((mPrivateFlags & PFLAG_DRAWABLE_STATE_DIRTY) == 0)) {
return mDrawableState;
} else {
mDrawableState = onCreateDrawableState(0);
mPrivateFlags &= ~PFLAG_DRAWABLE_STATE_DIRTY;
return mDrawableState;
}
}
/**
* Generate the new {@link android.graphics.drawable.Drawable} state for
* this view. This is called by the view
* system when the cached Drawable state is determined to be invalid. To
* retrieve the current state, you should use {@link #getDrawableState}.
*
* @param extraSpace if non-zero, this is the number of extra entries you
* would like in the returned array in which you can place your own
* states.
*
* @return Returns an array holding the current {@link Drawable} state of
* the view.
*
* @see #mergeDrawableStates(int[], int[])
*/
protected int[] onCreateDrawableState(int extraSpace) {
if ((mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE &&
mParent instanceof View) {
return ((View) mParent).onCreateDrawableState(extraSpace);
}
int[] drawableState;
int privateFlags = mPrivateFlags;
int viewStateIndex = 0;
if ((privateFlags & PFLAG_PRESSED) != 0) viewStateIndex |= StateSet.VIEW_STATE_PRESSED;
if ((mViewFlags & ENABLED_MASK) == ENABLED) viewStateIndex |= StateSet.VIEW_STATE_ENABLED;
if (isFocused()) viewStateIndex |= StateSet.VIEW_STATE_FOCUSED;
if ((privateFlags & PFLAG_SELECTED) != 0) viewStateIndex |= StateSet.VIEW_STATE_SELECTED;
if (hasWindowFocus()) viewStateIndex |= StateSet.VIEW_STATE_WINDOW_FOCUSED;
if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= StateSet.VIEW_STATE_ACTIVATED;
if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested) {
// This is set if HW acceleration is requested, even if the current
// process doesn't allow it. This is just to allow app preview
// windows to better match their app.
viewStateIndex |= StateSet.VIEW_STATE_ACCELERATED;
}
if ((privateFlags & PFLAG_HOVERED) != 0) viewStateIndex |= StateSet.VIEW_STATE_HOVERED;
final int privateFlags2 = mPrivateFlags2;
if ((privateFlags2 & PFLAG2_DRAG_CAN_ACCEPT) != 0) {
viewStateIndex |= StateSet.VIEW_STATE_DRAG_CAN_ACCEPT;
}
if ((privateFlags2 & PFLAG2_DRAG_HOVERED) != 0) {
viewStateIndex |= StateSet.VIEW_STATE_DRAG_HOVERED;
}
drawableState = StateSet.get(viewStateIndex);
//noinspection ConstantIfStatement
if (false) {
Log.i("View", "drawableStateIndex=" + viewStateIndex);
Log.i("View", toString()
+ " pressed=" + ((privateFlags & PFLAG_PRESSED) != 0)
+ " en=" + ((mViewFlags & ENABLED_MASK) == ENABLED)
+ " fo=" + hasFocus()
+ " sl=" + ((privateFlags & PFLAG_SELECTED) != 0)
+ " wf=" + hasWindowFocus()
+ ": " + Arrays.toString(drawableState));
}
if (extraSpace == 0) {
return drawableState;
}
final int[] fullState;
if (drawableState != null) {
fullState = new int[drawableState.length + extraSpace];
System.arraycopy(drawableState, 0, fullState, 0, drawableState.length);
} else {
fullState = new int[extraSpace];
}
return fullState;
}
/**
* Merge your own state values in additionalState into the base
* state values baseState that were returned by
* {@link #onCreateDrawableState(int)}.
*
* @param baseState The base state values returned by
* {@link #onCreateDrawableState(int)}, which will be modified to also hold your
* own additional state values.
*
* @param additionalState The additional state values you would like
* added to baseState; this array is not modified.
*
* @return As a convenience, the baseState array you originally
* passed into the function is returned.
*
* @see #onCreateDrawableState(int)
*/
protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState) {
final int N = baseState.length;
int i = N - 1;
while (i >= 0 && baseState[i] == 0) {
i--;
}
System.arraycopy(additionalState, 0, baseState, i + 1, additionalState.length);
return baseState;
}
/**
* Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()}
* on all Drawable objects associated with this view.
*
* Also calls {@link StateListAnimator#jumpToCurrentState()} if there is a StateListAnimator
* attached to this view.
*/
@CallSuper
public void jumpDrawablesToCurrentState() {
if (mBackground != null) {
mBackground.jumpToCurrentState();
}
if (mStateListAnimator != null) {
mStateListAnimator.jumpToCurrentState();
}
if (mDefaultFocusHighlight != null) {
mDefaultFocusHighlight.jumpToCurrentState();
}
if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
mForegroundInfo.mDrawable.jumpToCurrentState();
}
}
/**
* Sets the background color for this view.
* @param color the color of the background
*/
@RemotableViewMethod
public void setBackgroundColor(@ColorInt int color) {
if (mBackground instanceof ColorDrawable) {
((ColorDrawable) mBackground.mutate()).setColor(color);
computeOpaqueFlags();
mBackgroundResource = 0;
} else {
setBackground(new ColorDrawable(color));
}
}
/**
* Set the background to a given resource. The resource should refer to
* a Drawable object or 0 to remove the background.
* @param resid The identifier of the resource.
*
* @attr ref android.R.styleable#View_background
*/
@RemotableViewMethod
public void setBackgroundResource(@DrawableRes int resid) {
if (resid != 0 && resid == mBackgroundResource) {
return;
}
Drawable d = null;
if (resid != 0) {
d = mContext.getDrawable(resid);
}
setBackground(d);
mBackgroundResource = resid;
}
/**
* Set the background to a given Drawable, or remove the background. If the
* background has padding, this View's padding is set to the background's
* padding. However, when a background is removed, this View's padding isn't
* touched. If setting the padding is desired, please use
* {@link #setPadding(int, int, int, int)}.
*
* @param background The Drawable to use as the background, or null to remove the
* background
*/
public void setBackground(Drawable background) {
//noinspection deprecation
setBackgroundDrawable(background);
}
/**
* @deprecated use {@link #setBackground(Drawable)} instead
*/
@Deprecated
public void setBackgroundDrawable(Drawable background) {
computeOpaqueFlags();
if (background == mBackground) {
return;
}
boolean requestLayout = false;
mBackgroundResource = 0;
/*
* Regardless of whether we're setting a new background or not, we want
* to clear the previous drawable. setVisible first while we still have the callback set.
*/
if (mBackground != null) {
if (isAttachedToWindow()) {
mBackground.setVisible(false, false);
}
mBackground.setCallback(null);
unscheduleDrawable(mBackground);
}
if (background != null) {
Rect padding = sThreadLocal.get();
if (padding == null) {
padding = new Rect();
sThreadLocal.set(padding);
}
resetResolvedDrawablesInternal();
background.setLayoutDirection(getLayoutDirection());
if (background.getPadding(padding)) {
resetResolvedPaddingInternal();
switch (background.getLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
mUserPaddingLeftInitial = padding.right;
mUserPaddingRightInitial = padding.left;
internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);
break;
case LAYOUT_DIRECTION_LTR:
default:
mUserPaddingLeftInitial = padding.left;
mUserPaddingRightInitial = padding.right;
internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);
}
mLeftPaddingDefined = false;
mRightPaddingDefined = false;
}
// Compare the minimum sizes of the old Drawable and the new. If there isn't an old or
// if it has a different minimum size, we should layout again
if (mBackground == null
|| mBackground.getMinimumHeight() != background.getMinimumHeight()
|| mBackground.getMinimumWidth() != background.getMinimumWidth()) {
requestLayout = true;
}
// Set mBackground before we set this as the callback and start making other
// background drawable state change calls. In particular, the setVisible call below
// can result in drawables attempting to start animations or otherwise invalidate,
// which requires the view set as the callback (us) to recognize the drawable as
// belonging to it as per verifyDrawable.
mBackground = background;
if (background.isStateful()) {
background.setState(getDrawableState());
}
if (isAttachedToWindow()) {
background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
applyBackgroundTint();
// Set callback last, since the view may still be initializing.
background.setCallback(this);
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
requestLayout = true;
}
} else {
/* Remove the background */
mBackground = null;
if ((mViewFlags & WILL_NOT_DRAW) != 0
&& (mDefaultFocusHighlight == null)
&& (mForegroundInfo == null || mForegroundInfo.mDrawable == null)) {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
/*
* When the background is set, we try to apply its padding to this
* View. When the background is removed, we don't touch this View's
* padding. This is noted in the Javadocs. Hence, we don't need to
* requestLayout(), the invalidate() below is sufficient.
*/
// The old background's minimum size could have affected this
// View's layout, so let's requestLayout
requestLayout = true;
}
computeOpaqueFlags();
if (requestLayout) {
requestLayout();
}
mBackgroundSizeChanged = true;
invalidate(true);
invalidateOutline();
}
/**
* Gets the background drawable
*
* @return The drawable used as the background for this view, if any.
*
* @see #setBackground(Drawable)
*
* @attr ref android.R.styleable#View_background
*/
@InspectableProperty
public Drawable getBackground() {
return mBackground;
}
/**
* Applies a tint to the background drawable. Does not modify the current tint
* mode, which is {@link BlendMode#SRC_IN} by default.
*
* Subsequent calls to {@link #setBackground(Drawable)} will automatically
* mutate the drawable and apply the specified tint and tint mode using
* {@link Drawable#setTintList(ColorStateList)}.
*
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#View_backgroundTint
* @see #getBackgroundTintList()
* @see Drawable#setTintList(ColorStateList)
*/
@RemotableViewMethod
public void setBackgroundTintList(@Nullable ColorStateList tint) {
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mTintList = tint;
mBackgroundTint.mHasTintList = true;
applyBackgroundTint();
}
/**
* Return the tint applied to the background drawable, if specified.
*
* @return the tint applied to the background drawable
* @attr ref android.R.styleable#View_backgroundTint
* @see #setBackgroundTintList(ColorStateList)
*/
@InspectableProperty(name = "backgroundTint")
@Nullable
public ColorStateList getBackgroundTintList() {
return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
}
/**
* Specifies the blending mode used to apply the tint specified by
* {@link #setBackgroundTintList(ColorStateList)}} to the background
* drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
*
* @param tintMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#View_backgroundTintMode
* @see #getBackgroundTintMode()
* @see Drawable#setTintMode(PorterDuff.Mode)
*/
public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
BlendMode mode = null;
if (tintMode != null) {
mode = BlendMode.fromValue(tintMode.nativeInt);
}
setBackgroundTintBlendMode(mode);
}
/**
* Specifies the blending mode used to apply the tint specified by
* {@link #setBackgroundTintList(ColorStateList)}} to the background
* drawable. The default mode is {@link BlendMode#SRC_IN}.
*
* @param blendMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#View_backgroundTintMode
* @see #getBackgroundTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
@RemotableViewMethod
public void setBackgroundTintBlendMode(@Nullable BlendMode blendMode) {
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mBlendMode = blendMode;
mBackgroundTint.mHasTintMode = true;
applyBackgroundTint();
}
/**
* Return the blending mode used to apply the tint to the background
* drawable, if specified.
*
* @return the blending mode used to apply the tint to the background
* drawable
* @attr ref android.R.styleable#View_backgroundTintMode
* @see #setBackgroundTintBlendMode(BlendMode)
*
*/
@Nullable
@InspectableProperty
public PorterDuff.Mode getBackgroundTintMode() {
PorterDuff.Mode porterDuffMode;
if (mBackgroundTint != null && mBackgroundTint.mBlendMode != null) {
porterDuffMode = BlendMode.blendModeToPorterDuffMode(mBackgroundTint.mBlendMode);
} else {
porterDuffMode = null;
}
return porterDuffMode;
}
/**
* Return the blending mode used to apply the tint to the background
* drawable, if specified.
*
* @return the blending mode used to apply the tint to the background
* drawable, null if no blend has previously been configured
* @attr ref android.R.styleable#View_backgroundTintMode
* @see #setBackgroundTintBlendMode(BlendMode)
*/
public @Nullable BlendMode getBackgroundTintBlendMode() {
return mBackgroundTint != null ? mBackgroundTint.mBlendMode : null;
}
private void applyBackgroundTint() {
if (mBackground != null && mBackgroundTint != null) {
final TintInfo tintInfo = mBackgroundTint;
if (tintInfo.mHasTintList || tintInfo.mHasTintMode) {
mBackground = mBackground.mutate();
if (tintInfo.mHasTintList) {
mBackground.setTintList(tintInfo.mTintList);
}
if (tintInfo.mHasTintMode) {
mBackground.setTintBlendMode(tintInfo.mBlendMode);
}
// The drawable (or one of its children) may not have been
// stateful before applying the tint, so let's try again.
if (mBackground.isStateful()) {
mBackground.setState(getDrawableState());
}
}
}
}
/**
* Returns the drawable used as the foreground of this View. The
* foreground drawable, if non-null, is always drawn on top of the view's content.
*
* @return a Drawable or null if no foreground was set
*
* @see #onDrawForeground(Canvas)
*/
@InspectableProperty
public Drawable getForeground() {
return mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
}
/**
* Supply a Drawable that is to be rendered on top of all of the content in the view.
*
* @param foreground the Drawable to be drawn on top of the children
*
* @attr ref android.R.styleable#View_foreground
*/
public void setForeground(Drawable foreground) {
if (mForegroundInfo == null) {
if (foreground == null) {
// Nothing to do.
return;
}
mForegroundInfo = new ForegroundInfo();
}
if (foreground == mForegroundInfo.mDrawable) {
// Nothing to do
return;
}
if (mForegroundInfo.mDrawable != null) {
if (isAttachedToWindow()) {
mForegroundInfo.mDrawable.setVisible(false, false);
}
mForegroundInfo.mDrawable.setCallback(null);
unscheduleDrawable(mForegroundInfo.mDrawable);
}
mForegroundInfo.mDrawable = foreground;
mForegroundInfo.mBoundsChanged = true;
if (foreground != null) {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
foreground.setLayoutDirection(getLayoutDirection());
if (foreground.isStateful()) {
foreground.setState(getDrawableState());
}
applyForegroundTint();
if (isAttachedToWindow()) {
foreground.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
// Set callback last, since the view may still be initializing.
foreground.setCallback(this);
} else if ((mViewFlags & WILL_NOT_DRAW) != 0 && mBackground == null
&& (mDefaultFocusHighlight == null)) {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
requestLayout();
invalidate();
}
/**
* Magic bit used to support features of framework-internal window decor implementation details.
* This used to live exclusively in FrameLayout.
*
* @return true if the foreground should draw inside the padding region or false
* if it should draw inset by the view's padding
* @hide internal use only; only used by FrameLayout and internal screen layouts.
*/
public boolean isForegroundInsidePadding() {
return mForegroundInfo != null ? mForegroundInfo.mInsidePadding : true;
}
/**
* Describes how the foreground is positioned.
*
* @return foreground gravity.
*
* @see #setForegroundGravity(int)
*
* @attr ref android.R.styleable#View_foregroundGravity
*/
@InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
public int getForegroundGravity() {
return mForegroundInfo != null ? mForegroundInfo.mGravity
: Gravity.START | Gravity.TOP;
}
/**
* Describes how the foreground is positioned. Defaults to START and TOP.
*
* @param gravity see {@link android.view.Gravity}
*
* @see #getForegroundGravity()
*
* @attr ref android.R.styleable#View_foregroundGravity
*/
public void setForegroundGravity(int gravity) {
if (mForegroundInfo == null) {
mForegroundInfo = new ForegroundInfo();
}
if (mForegroundInfo.mGravity != gravity) {
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.START;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.TOP;
}
mForegroundInfo.mGravity = gravity;
requestLayout();
}
}
/**
* Applies a tint to the foreground drawable. Does not modify the current tint
* mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
*
* Subsequent calls to {@link #setForeground(Drawable)} will automatically
* mutate the drawable and apply the specified tint and tint mode using
* {@link Drawable#setTintList(ColorStateList)}.
*
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#View_foregroundTint
* @see #getForegroundTintList()
* @see Drawable#setTintList(ColorStateList)
*/
@RemotableViewMethod
public void setForegroundTintList(@Nullable ColorStateList tint) {
if (mForegroundInfo == null) {
mForegroundInfo = new ForegroundInfo();
}
if (mForegroundInfo.mTintInfo == null) {
mForegroundInfo.mTintInfo = new TintInfo();
}
mForegroundInfo.mTintInfo.mTintList = tint;
mForegroundInfo.mTintInfo.mHasTintList = true;
applyForegroundTint();
}
/**
* Return the tint applied to the foreground drawable, if specified.
*
* @return the tint applied to the foreground drawable
* @attr ref android.R.styleable#View_foregroundTint
* @see #setForegroundTintList(ColorStateList)
*/
@InspectableProperty(name = "foregroundTint")
@Nullable
public ColorStateList getForegroundTintList() {
return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
? mForegroundInfo.mTintInfo.mTintList : null;
}
/**
* Specifies the blending mode used to apply the tint specified by
* {@link #setForegroundTintList(ColorStateList)}} to the background
* drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
*
* @param tintMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#View_foregroundTintMode
* @see #getForegroundTintMode()
* @see Drawable#setTintMode(PorterDuff.Mode)
*
*/
public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
BlendMode mode = null;
if (tintMode != null) {
mode = BlendMode.fromValue(tintMode.nativeInt);
}
setForegroundTintBlendMode(mode);
}
/**
* Specifies the blending mode used to apply the tint specified by
* {@link #setForegroundTintList(ColorStateList)}} to the background
* drawable. The default mode is {@link BlendMode#SRC_IN}.
*
* @param blendMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#View_foregroundTintMode
* @see #getForegroundTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
@RemotableViewMethod
public void setForegroundTintBlendMode(@Nullable BlendMode blendMode) {
if (mForegroundInfo == null) {
mForegroundInfo = new ForegroundInfo();
}
if (mForegroundInfo.mTintInfo == null) {
mForegroundInfo.mTintInfo = new TintInfo();
}
mForegroundInfo.mTintInfo.mBlendMode = blendMode;
mForegroundInfo.mTintInfo.mHasTintMode = true;
applyForegroundTint();
}
/**
* Return the blending mode used to apply the tint to the foreground
* drawable, if specified.
*
* @return the blending mode used to apply the tint to the foreground
* drawable
* @attr ref android.R.styleable#View_foregroundTintMode
* @see #setForegroundTintMode(PorterDuff.Mode)
*/
@InspectableProperty
@Nullable
public PorterDuff.Mode getForegroundTintMode() {
BlendMode blendMode = mForegroundInfo != null && mForegroundInfo.mTintInfo != null
? mForegroundInfo.mTintInfo.mBlendMode : null;
if (blendMode != null) {
return BlendMode.blendModeToPorterDuffMode(blendMode);
} else {
return null;
}
}
/**
* Return the blending mode used to apply the tint to the foreground
* drawable, if specified.
*
* @return the blending mode used to apply the tint to the foreground
* drawable
* @attr ref android.R.styleable#View_foregroundTintMode
* @see #setForegroundTintBlendMode(BlendMode)
*
*/
public @Nullable BlendMode getForegroundTintBlendMode() {
return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
? mForegroundInfo.mTintInfo.mBlendMode : null;
}
private void applyForegroundTint() {
if (mForegroundInfo != null && mForegroundInfo.mDrawable != null
&& mForegroundInfo.mTintInfo != null) {
final TintInfo tintInfo = mForegroundInfo.mTintInfo;
if (tintInfo.mHasTintList || tintInfo.mHasTintMode) {
mForegroundInfo.mDrawable = mForegroundInfo.mDrawable.mutate();
if (tintInfo.mHasTintList) {
mForegroundInfo.mDrawable.setTintList(tintInfo.mTintList);
}
if (tintInfo.mHasTintMode) {
mForegroundInfo.mDrawable.setTintBlendMode(tintInfo.mBlendMode);
}
// The drawable (or one of its children) may not have been
// stateful before applying the tint, so let's try again.
if (mForegroundInfo.mDrawable.isStateful()) {
mForegroundInfo.mDrawable.setState(getDrawableState());
}
}
}
}
/**
* Get the drawable to be overlayed when a view is autofilled
*
* @return The drawable
*
* @throws IllegalStateException if the drawable could not be found.
*/
@Nullable private Drawable getAutofilledDrawable() {
if (mAttachInfo == null) {
return null;
}
// Lazily load the isAutofilled drawable.
if (mAttachInfo.mAutofilledDrawable == null) {
Context rootContext = getRootView().getContext();
TypedArray a = rootContext.getTheme().obtainStyledAttributes(AUTOFILL_HIGHLIGHT_ATTR);
int attributeResourceId = a.getResourceId(0, 0);
mAttachInfo.mAutofilledDrawable = rootContext.getDrawable(attributeResourceId);
a.recycle();
}
return mAttachInfo.mAutofilledDrawable;
}
/**
* Draw {@link View#isAutofilled()} highlight over view if the view is autofilled, unless
* {@link #PFLAG4_AUTOFILL_HIDE_HIGHLIGHT} is enabled.
*
* @param canvas The canvas to draw on
*/
private void drawAutofilledHighlight(@NonNull Canvas canvas) {
if (isAutofilled() && !hideAutofillHighlight()) {
Drawable autofilledHighlight = getAutofilledDrawable();
if (autofilledHighlight != null) {
autofilledHighlight.setBounds(0, 0, getWidth(), getHeight());
autofilledHighlight.draw(canvas);
}
}
}
/**
* Draw any foreground content for this view.
*
* Foreground content may consist of scroll bars, a {@link #setForeground foreground}
* drawable or other view-specific decorations. The foreground is drawn on top of the
* primary view content. This method should be treated similarly to setMeasuredDimension and not as a general
* property. Views that compute their own optical insets should call it as part of measurement.
* This method does not request layout. If you are setting optical insets outside of
* measure/layout itself you will want to call requestLayout() yourself.
* Finds the topmost view in the current view hierarchy. In multi-window mode, the coordinate space encompasses the entire
* device screen, ignoring the bounds of the app window. For example, if the
* view is in the bottom portion of a horizontal split screen, the top edge
* of the screen—not the top edge of the window—is the origin
* from which the y-coordinate is calculated.
*
* In multiple-screen scenarios, the coordinate space can span screens.
* For example, if the app is spanning both screens of a dual-screen device
* and the view is located on the right-hand screen, the x-coordinate is
* calculated from the left edge of the left-hand screen to the left edge of
* the view. When the app is restricted to a single screen in a
* multiple-screen environment, the coordinate space includes only the
* screen on which the app is running.
*
* After the method returns, the argument array contains the x and y
* coordinates of the view relative to the view's left and top edges,
* respectively.
*
* @param outLocation A two-element integer array in which the view
* coordinates are stored. The x-coordinate is at index 0; the
* y-coordinate, at index 1.
*/
public void getLocationOnScreen(@Size(2) int[] outLocation) {
getLocationInWindow(outLocation);
final AttachInfo info = mAttachInfo;
if (info != null) {
outLocation[0] += info.mWindowLeft;
outLocation[1] += info.mWindowTop;
// If OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS override is enabled,
// applyViewLocationSandboxingIfNeeded sandboxes outLocation within window bounds.
info.mViewRootImpl.applyViewLocationSandboxingIfNeeded(outLocation);
}
}
/**
* Gets the coordinates of this view in the coordinate space of the window
* that contains the view, irrespective of system decorations.
*
* In multi-window mode, the origin of the coordinate space is the
* top left corner of the window that contains the view. In full screen
* mode, the origin is the top left corner of the device screen.
*
* In multiple-screen scenarios, if the app spans multiple screens, the
* coordinate space also spans multiple screens. But if the app is
* restricted to a single screen, the coordinate space includes only the
* screen on which the app is running.
*
* After the method returns, the argument array contains the x and y
* coordinates of the view relative to the view's left and top edges,
* respectively.
*
* @param outLocation A two-element integer array in which the view
* coordinates are stored. The x-coordinate is at index 0; the
* y-coordinate, at index 1.
*/
public void getLocationInWindow(@Size(2) int[] outLocation) {
if (outLocation == null || outLocation.length < 2) {
throw new IllegalArgumentException("outLocation must be an array of two integers");
}
outLocation[0] = 0;
outLocation[1] = 0;
transformFromViewToWindowSpace(outLocation);
}
/** @hide */
public void transformFromViewToWindowSpace(@Size(2) int[] inOutLocation) {
if (inOutLocation == null || inOutLocation.length < 2) {
throw new IllegalArgumentException("inOutLocation must be an array of two integers");
}
if (mAttachInfo == null) {
// When the view is not attached to a window, this method does not make sense
inOutLocation[0] = inOutLocation[1] = 0;
return;
}
float position[] = mAttachInfo.mTmpTransformLocation;
position[0] = inOutLocation[0];
position[1] = inOutLocation[1];
if (!hasIdentityMatrix()) {
getMatrix().mapPoints(position);
}
position[0] += mLeft;
position[1] += mTop;
ViewParent viewParent = mParent;
while (viewParent instanceof View) {
final View view = (View) viewParent;
position[0] -= view.mScrollX;
position[1] -= view.mScrollY;
if (!view.hasIdentityMatrix()) {
view.getMatrix().mapPoints(position);
}
position[0] += view.mLeft;
position[1] += view.mTop;
viewParent = view.mParent;
}
if (viewParent instanceof ViewRootImpl) {
// *cough*
final ViewRootImpl vr = (ViewRootImpl) viewParent;
position[1] -= vr.mCurScrollY;
}
inOutLocation[0] = Math.round(position[0]);
inOutLocation[1] = Math.round(position[1]);
}
/**
* @param id the id of the view to be found
* @return the view of the specified id, null if cannot be found
* @hide
*/
protected
* Note: In most cases -- depending on compiler support --
* the resulting view is automatically cast to the target class type. If
* the target class type is unconstrained, an explicit cast may be
* necessary.
*
* @param id the ID to search for
* @return a view with given ID if found, or {@code null} otherwise
* @see View#requireViewById(int)
*/
@Nullable
public final
* Note: In most cases -- depending on compiler support --
* the resulting view is automatically cast to the target class type. If
* the target class type is unconstrained, an explicit cast may be
* necessary.
*
* @param id the ID to search for
* @return a view with given ID
* @see View#findViewById(int)
*/
@NonNull
public final {@value #AUTOFILL_HINT_EMAIL_ADDRESS}
).
*
* {@value #AUTOFILL_HINT_NAME}
).
*
* {@value #AUTOFILL_HINT_USERNAME}
).
*
* {@value #AUTOFILL_HINT_PASSWORD}
).
*
* {@value #AUTOFILL_HINT_PHONE}
).
*
* {@value #AUTOFILL_HINT_POSTAL_ADDRESS}
).
*
* {@value #AUTOFILL_HINT_POSTAL_CODE}
).
*
* {@value #AUTOFILL_HINT_CREDIT_CARD_NUMBER}
).
*
* {@value #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}
).
*
* {@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}
).
*
*
*
*
*
*
*
* {@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH}
).
*
*
*
*
* {@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}
).
*
* {@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}
).
*
* {@value View#AUTOFILL_HINT_PASSWORD}
.
* @hide
*/
// TODO(229765029): unhide this for UI toolkit
public static final String AUTOFILL_HINT_PASSWORD_AUTO = "passwordAuto";
/**
* Hint indicating that the developer intends to fill this view with output from
* CredentialManager.
*
* @hide
*/
public static final String AUTOFILL_HINT_CREDENTIAL_MANAGER = "credential";
/**
* Hints for the autofill services that describes the content of the view.
*/
private @Nullable String[] mAutofillHints;
/**
* Autofill id, lazily created on calls to {@link #getAutofillId()}.
*/
private AutofillId mAutofillId;
/** @hide */
@IntDef(prefix = { "AUTOFILL_TYPE_" }, value = {
AUTOFILL_TYPE_NONE,
AUTOFILL_TYPE_TEXT,
AUTOFILL_TYPE_TOGGLE,
AUTOFILL_TYPE_LIST,
AUTOFILL_TYPE_DATE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutofillType {}
/**
* Autofill type for views that cannot be autofilled.
*
* SYSTEM_UI_FLAG_HIDE_NAVIGATION
will cause
* those to disappear. This is useful (in conjunction with the
* {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and
* {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN FLAG_LAYOUT_IN_SCREEN}
* window flags) for displaying content using every last pixel on the display.
*
* R.attr.buttonStyle
for defStyleAttr; this
* allows the theme's button style to modify all of the base view attributes
* (in particular its background) as well as the Button class's attributes.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
* @see #View(Context, AttributeSet)
*/
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
/**
* Perform inflation from XML and apply a class-specific base style from a
* theme attribute or style resource. This constructor of View allows
* subclasses to use their own base style when they are inflating.
*
*
* <Button * textColor="#ff000000">
* , then the button's text will always be black, regardless of
* what is specified in any of the styles.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
* @param defStyleRes A resource identifier of a style resource that
* supplies default values for the view, used only if
* defStyleAttr is 0 or can not be found in the theme. Can be 0
* to not look for defaults.
* @see #View(Context, AttributeSet, int)
*/
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this(context);
mSourceLayoutId = Resources.getAttributeSetSourceResId(attrs);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
retrieveExplicitStyle(context.getTheme(), attrs);
saveAttributeDataForStyleable(context, com.android.internal.R.styleable.View, attrs, a,
defStyleAttr, defStyleRes);
if (sDebugViewAttributes) {
saveAttributeData(attrs, a);
}
Drawable background = null;
int leftPadding = -1;
int topPadding = -1;
int rightPadding = -1;
int bottomPadding = -1;
int startPadding = UNDEFINED_PADDING;
int endPadding = UNDEFINED_PADDING;
int padding = -1;
int paddingHorizontal = -1;
int paddingVertical = -1;
int viewFlagValues = 0;
int viewFlagMasks = 0;
boolean setScrollContainer = false;
int x = 0;
int y = 0;
float tx = 0;
float ty = 0;
float tz = 0;
float elevation = 0;
float rotation = 0;
float rotationX = 0;
float rotationY = 0;
float sx = 1f;
float sy = 1f;
boolean transformSet = false;
int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
int overScrollMode = mOverScrollMode;
boolean initializeScrollbars = false;
boolean initializeScrollIndicators = false;
boolean startPaddingDefined = false;
boolean endPaddingDefined = false;
boolean leftPaddingDefined = false;
boolean rightPaddingDefined = false;
// Set default values.
viewFlagValues |= FOCUSABLE_AUTO;
viewFlagMasks |= FOCUSABLE_AUTO;
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_background:
background = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.View_padding:
padding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = padding;
mUserPaddingRightInitial = padding;
leftPaddingDefined = true;
rightPaddingDefined = true;
break;
case com.android.internal.R.styleable.View_paddingHorizontal:
paddingHorizontal = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = paddingHorizontal;
mUserPaddingRightInitial = paddingHorizontal;
leftPaddingDefined = true;
rightPaddingDefined = true;
break;
case com.android.internal.R.styleable.View_paddingVertical:
paddingVertical = a.getDimensionPixelSize(attr, -1);
break;
case com.android.internal.R.styleable.View_paddingLeft:
leftPadding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = leftPadding;
leftPaddingDefined = true;
break;
case com.android.internal.R.styleable.View_paddingTop:
topPadding = a.getDimensionPixelSize(attr, -1);
break;
case com.android.internal.R.styleable.View_paddingRight:
rightPadding = a.getDimensionPixelSize(attr, -1);
mUserPaddingRightInitial = rightPadding;
rightPaddingDefined = true;
break;
case com.android.internal.R.styleable.View_paddingBottom:
bottomPadding = a.getDimensionPixelSize(attr, -1);
break;
case com.android.internal.R.styleable.View_paddingStart:
startPadding = a.getDimensionPixelSize(attr, UNDEFINED_PADDING);
startPaddingDefined = (startPadding != UNDEFINED_PADDING);
break;
case com.android.internal.R.styleable.View_paddingEnd:
endPadding = a.getDimensionPixelSize(attr, UNDEFINED_PADDING);
endPaddingDefined = (endPadding != UNDEFINED_PADDING);
break;
case com.android.internal.R.styleable.View_scrollX:
x = a.getDimensionPixelOffset(attr, 0);
break;
case com.android.internal.R.styleable.View_scrollY:
y = a.getDimensionPixelOffset(attr, 0);
break;
case com.android.internal.R.styleable.View_alpha:
setAlpha(a.getFloat(attr, 1f));
break;
case com.android.internal.R.styleable.View_transformPivotX:
setPivotX(a.getDimension(attr, 0));
break;
case com.android.internal.R.styleable.View_transformPivotY:
setPivotY(a.getDimension(attr, 0));
break;
case com.android.internal.R.styleable.View_translationX:
tx = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_translationY:
ty = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_translationZ:
tz = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_elevation:
elevation = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_rotation:
rotation = a.getFloat(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_rotationX:
rotationX = a.getFloat(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_rotationY:
rotationY = a.getFloat(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_scaleX:
sx = a.getFloat(attr, 1f);
transformSet = true;
break;
case com.android.internal.R.styleable.View_scaleY:
sy = a.getFloat(attr, 1f);
transformSet = true;
break;
case com.android.internal.R.styleable.View_id:
mID = a.getResourceId(attr, NO_ID);
break;
case com.android.internal.R.styleable.View_tag:
mTag = a.getText(attr);
break;
case com.android.internal.R.styleable.View_fitsSystemWindows:
if (a.getBoolean(attr, false)) {
viewFlagValues |= FITS_SYSTEM_WINDOWS;
viewFlagMasks |= FITS_SYSTEM_WINDOWS;
}
break;
case com.android.internal.R.styleable.View_focusable:
viewFlagValues = (viewFlagValues & ~FOCUSABLE_MASK) | getFocusableAttribute(a);
if ((viewFlagValues & FOCUSABLE_AUTO) == 0) {
viewFlagMasks |= FOCUSABLE_MASK;
}
break;
case com.android.internal.R.styleable.View_focusableInTouchMode:
if (a.getBoolean(attr, false)) {
// unset auto focus since focusableInTouchMode implies explicit focusable
viewFlagValues &= ~FOCUSABLE_AUTO;
viewFlagValues |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE;
viewFlagMasks |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE_MASK;
}
break;
case com.android.internal.R.styleable.View_clickable:
if (a.getBoolean(attr, false)) {
viewFlagValues |= CLICKABLE;
viewFlagMasks |= CLICKABLE;
}
break;
case com.android.internal.R.styleable.View_allowClickWhenDisabled:
setAllowClickWhenDisabled(a.getBoolean(attr, false));
break;
case com.android.internal.R.styleable.View_longClickable:
if (a.getBoolean(attr, false)) {
viewFlagValues |= LONG_CLICKABLE;
viewFlagMasks |= LONG_CLICKABLE;
}
break;
case com.android.internal.R.styleable.View_contextClickable:
if (a.getBoolean(attr, false)) {
viewFlagValues |= CONTEXT_CLICKABLE;
viewFlagMasks |= CONTEXT_CLICKABLE;
}
break;
case com.android.internal.R.styleable.View_saveEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues |= SAVE_DISABLED;
viewFlagMasks |= SAVE_DISABLED_MASK;
}
break;
case com.android.internal.R.styleable.View_duplicateParentState:
if (a.getBoolean(attr, false)) {
viewFlagValues |= DUPLICATE_PARENT_STATE;
viewFlagMasks |= DUPLICATE_PARENT_STATE;
}
break;
case com.android.internal.R.styleable.View_visibility:
final int visibility = a.getInt(attr, 0);
if (visibility != 0) {
viewFlagValues |= VISIBILITY_FLAGS[visibility];
viewFlagMasks |= VISIBILITY_MASK;
}
break;
case com.android.internal.R.styleable.View_layoutDirection:
// Clear any layout direction flags (included resolved bits) already set
mPrivateFlags2 &=
~(PFLAG2_LAYOUT_DIRECTION_MASK | PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK);
// Set the layout direction flags depending on the value of the attribute
final int layoutDirection = a.getInt(attr, -1);
final int value = (layoutDirection != -1) ?
LAYOUT_DIRECTION_FLAGS[layoutDirection] : LAYOUT_DIRECTION_DEFAULT;
mPrivateFlags2 |= (value << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT);
break;
case com.android.internal.R.styleable.View_drawingCacheQuality:
final int cacheQuality = a.getInt(attr, 0);
if (cacheQuality != 0) {
viewFlagValues |= DRAWING_CACHE_QUALITY_FLAGS[cacheQuality];
viewFlagMasks |= DRAWING_CACHE_QUALITY_MASK;
}
break;
case com.android.internal.R.styleable.View_contentDescription:
setContentDescription(a.getString(attr));
break;
case com.android.internal.R.styleable.View_accessibilityTraversalBefore:
setAccessibilityTraversalBefore(a.getResourceId(attr, NO_ID));
break;
case com.android.internal.R.styleable.View_accessibilityTraversalAfter:
setAccessibilityTraversalAfter(a.getResourceId(attr, NO_ID));
break;
case com.android.internal.R.styleable.View_labelFor:
setLabelFor(a.getResourceId(attr, NO_ID));
break;
case com.android.internal.R.styleable.View_soundEffectsEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues &= ~SOUND_EFFECTS_ENABLED;
viewFlagMasks |= SOUND_EFFECTS_ENABLED;
}
break;
case com.android.internal.R.styleable.View_hapticFeedbackEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED;
viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED;
}
break;
case R.styleable.View_scrollbars:
final int scrollbars = a.getInt(attr, SCROLLBARS_NONE);
if (scrollbars != SCROLLBARS_NONE) {
viewFlagValues |= scrollbars;
viewFlagMasks |= SCROLLBARS_MASK;
initializeScrollbars = true;
}
break;
//noinspection deprecation
case R.styleable.View_fadingEdge:
break;
case R.styleable.View_requiresFadingEdge:
final int fadingEdge = a.getInt(attr, FADING_EDGE_NONE);
if (fadingEdge != FADING_EDGE_NONE) {
viewFlagValues |= fadingEdge;
viewFlagMasks |= FADING_EDGE_MASK;
initializeFadingEdgeInternal(a);
}
break;
case R.styleable.View_scrollbarStyle:
scrollbarStyle = a.getInt(attr, SCROLLBARS_INSIDE_OVERLAY);
if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
viewFlagValues |= scrollbarStyle & SCROLLBARS_STYLE_MASK;
viewFlagMasks |= SCROLLBARS_STYLE_MASK;
}
break;
case R.styleable.View_isScrollContainer:
setScrollContainer = true;
if (a.getBoolean(attr, false)) {
setScrollContainer(true);
}
break;
case com.android.internal.R.styleable.View_keepScreenOn:
if (a.getBoolean(attr, false)) {
viewFlagValues |= KEEP_SCREEN_ON;
viewFlagMasks |= KEEP_SCREEN_ON;
}
break;
case R.styleable.View_filterTouchesWhenObscured:
if (a.getBoolean(attr, false)) {
viewFlagValues |= FILTER_TOUCHES_WHEN_OBSCURED;
viewFlagMasks |= FILTER_TOUCHES_WHEN_OBSCURED;
}
break;
case R.styleable.View_nextFocusLeft:
mNextFocusLeftId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextFocusRight:
mNextFocusRightId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextFocusUp:
mNextFocusUpId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextFocusDown:
mNextFocusDownId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextFocusForward:
mNextFocusForwardId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextClusterForward:
mNextClusterForwardId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_minWidth:
mMinWidth = a.getDimensionPixelSize(attr, 0);
break;
case R.styleable.View_minHeight:
mMinHeight = a.getDimensionPixelSize(attr, 0);
break;
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new DeclaredOnClickListener(this, handlerName));
}
break;
case R.styleable.View_overScrollMode:
overScrollMode = a.getInt(attr, OVER_SCROLL_IF_CONTENT_SCROLLS);
break;
case R.styleable.View_verticalScrollbarPosition:
mVerticalScrollbarPosition = a.getInt(attr, SCROLLBAR_POSITION_DEFAULT);
break;
case R.styleable.View_layerType:
setLayerType(a.getInt(attr, LAYER_TYPE_NONE), null);
break;
case R.styleable.View_textDirection:
// Clear any text direction flag already set
mPrivateFlags2 &= ~PFLAG2_TEXT_DIRECTION_MASK;
// Set the text direction flags depending on the value of the attribute
final int textDirection = a.getInt(attr, -1);
if (textDirection != -1) {
mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_FLAGS[textDirection];
}
break;
case R.styleable.View_textAlignment:
// Clear any text alignment flag already set
mPrivateFlags2 &= ~PFLAG2_TEXT_ALIGNMENT_MASK;
// Set the text alignment flag depending on the value of the attribute
final int textAlignment = a.getInt(attr, TEXT_ALIGNMENT_DEFAULT);
mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_FLAGS[textAlignment];
break;
case R.styleable.View_importantForAccessibility:
setImportantForAccessibility(a.getInt(attr,
IMPORTANT_FOR_ACCESSIBILITY_DEFAULT));
break;
case R.styleable.View_accessibilityDataSensitive:
setAccessibilityDataSensitive(a.getInt(attr,
ACCESSIBILITY_DATA_SENSITIVE_AUTO));
break;
case R.styleable.View_accessibilityLiveRegion:
setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT));
break;
case R.styleable.View_transitionName:
setTransitionName(a.getString(attr));
break;
case R.styleable.View_nestedScrollingEnabled:
setNestedScrollingEnabled(a.getBoolean(attr, false));
break;
case R.styleable.View_stateListAnimator:
setStateListAnimator(AnimatorInflater.loadStateListAnimator(context,
a.getResourceId(attr, 0)));
break;
case R.styleable.View_backgroundTint:
// This will get applied later during setBackground().
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mTintList = a.getColorStateList(
R.styleable.View_backgroundTint);
mBackgroundTint.mHasTintList = true;
break;
case R.styleable.View_backgroundTintMode:
// This will get applied later during setBackground().
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mBlendMode = Drawable.parseBlendMode(a.getInt(
R.styleable.View_backgroundTintMode, -1), null);
mBackgroundTint.mHasTintMode = true;
break;
case R.styleable.View_outlineProvider:
setOutlineProviderFromAttribute(a.getInt(R.styleable.View_outlineProvider,
PROVIDER_BACKGROUND));
break;
case R.styleable.View_foreground:
setForeground(a.getDrawable(attr));
break;
case R.styleable.View_foregroundGravity:
setForegroundGravity(a.getInt(attr, Gravity.NO_GRAVITY));
break;
case R.styleable.View_foregroundTintMode:
setForegroundTintBlendMode(
Drawable.parseBlendMode(a.getInt(attr, -1),
null));
break;
case R.styleable.View_foregroundTint:
setForegroundTintList(a.getColorStateList(attr));
break;
case R.styleable.View_foregroundInsidePadding:
if (mForegroundInfo == null) {
mForegroundInfo = new ForegroundInfo();
}
mForegroundInfo.mInsidePadding = a.getBoolean(attr,
mForegroundInfo.mInsidePadding);
break;
case R.styleable.View_scrollIndicators:
final int scrollIndicators =
(a.getInt(attr, 0) << SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT)
& SCROLL_INDICATORS_PFLAG3_MASK;
if (scrollIndicators != 0) {
mPrivateFlags3 |= scrollIndicators;
initializeScrollIndicators = true;
}
break;
case R.styleable.View_pointerIcon:
final int resourceId = a.getResourceId(attr, 0);
if (resourceId != 0) {
setPointerIcon(PointerIcon.load(
context.getResources(), resourceId));
} else {
final int pointerType = a.getInt(attr, PointerIcon.TYPE_NOT_SPECIFIED);
if (pointerType != PointerIcon.TYPE_NOT_SPECIFIED) {
setPointerIcon(PointerIcon.getSystemIcon(context, pointerType));
}
}
break;
case R.styleable.View_forceHasOverlappingRendering:
if (a.peekValue(attr) != null) {
forceHasOverlappingRendering(a.getBoolean(attr, true));
}
break;
case R.styleable.View_tooltipText:
setTooltipText(a.getText(attr));
break;
case R.styleable.View_keyboardNavigationCluster:
if (a.peekValue(attr) != null) {
setKeyboardNavigationCluster(a.getBoolean(attr, true));
}
break;
case R.styleable.View_focusedByDefault:
if (a.peekValue(attr) != null) {
setFocusedByDefault(a.getBoolean(attr, true));
}
break;
case R.styleable.View_autofillHints:
if (a.peekValue(attr) != null) {
CharSequence[] rawHints = null;
String rawString = null;
if (a.getType(attr) == TypedValue.TYPE_REFERENCE) {
int resId = a.getResourceId(attr, 0);
try {
rawHints = a.getTextArray(attr);
} catch (Resources.NotFoundException e) {
rawString = getResources().getString(resId);
}
} else {
rawString = a.getString(attr);
}
if (rawHints == null) {
if (rawString == null) {
throw new IllegalArgumentException(
"Could not resolve autofillHints");
} else {
rawHints = rawString.split(",");
}
}
String[] hints = new String[rawHints.length];
int numHints = rawHints.length;
for (int rawHintNum = 0; rawHintNum < numHints; rawHintNum++) {
hints[rawHintNum] = rawHints[rawHintNum].toString().trim();
}
setAutofillHints(hints);
}
break;
case R.styleable.View_importantForAutofill:
if (a.peekValue(attr) != null) {
setImportantForAutofill(a.getInt(attr, IMPORTANT_FOR_AUTOFILL_AUTO));
}
break;
case R.styleable.View_importantForContentCapture:
if (a.peekValue(attr) != null) {
setImportantForContentCapture(a.getInt(attr,
IMPORTANT_FOR_CONTENT_CAPTURE_AUTO));
}
break;
case R.styleable.View_isCredential:
if (a.peekValue(attr) != null) {
setIsCredential(a.getBoolean(attr, false));
}
break;
case R.styleable.View_defaultFocusHighlightEnabled:
if (a.peekValue(attr) != null) {
setDefaultFocusHighlightEnabled(a.getBoolean(attr, true));
}
break;
case R.styleable.View_screenReaderFocusable:
if (a.peekValue(attr) != null) {
setScreenReaderFocusable(a.getBoolean(attr, false));
}
break;
case R.styleable.View_accessibilityPaneTitle:
if (a.peekValue(attr) != null) {
setAccessibilityPaneTitle(a.getString(attr));
}
break;
case R.styleable.View_outlineSpotShadowColor:
setOutlineSpotShadowColor(a.getColor(attr, Color.BLACK));
break;
case R.styleable.View_outlineAmbientShadowColor:
setOutlineAmbientShadowColor(a.getColor(attr, Color.BLACK));
break;
case com.android.internal.R.styleable.View_accessibilityHeading:
setAccessibilityHeading(a.getBoolean(attr, false));
break;
case R.styleable.View_forceDarkAllowed:
mRenderNode.setForceDarkAllowed(a.getBoolean(attr, true));
break;
case R.styleable.View_scrollCaptureHint:
setScrollCaptureHint((a.getInt(attr, SCROLL_CAPTURE_HINT_AUTO)));
break;
case R.styleable.View_clipToOutline:
setClipToOutline(a.getBoolean(attr, false));
break;
case R.styleable.View_preferKeepClear:
setPreferKeepClear(a.getBoolean(attr, false));
break;
case R.styleable.View_autoHandwritingEnabled:
setAutoHandwritingEnabled(a.getBoolean(attr, false));
break;
case R.styleable.View_handwritingBoundsOffsetLeft:
mHandwritingBoundsOffsetLeft = a.getDimension(attr, 0);
break;
case R.styleable.View_handwritingBoundsOffsetTop:
mHandwritingBoundsOffsetTop = a.getDimension(attr, 0);
break;
case R.styleable.View_handwritingBoundsOffsetRight:
mHandwritingBoundsOffsetRight = a.getDimension(attr, 0);
break;
case R.styleable.View_handwritingBoundsOffsetBottom:
mHandwritingBoundsOffsetBottom = a.getDimension(attr, 0);
break;
case R.styleable.View_contentSensitivity:
setContentSensitivity(a.getInt(attr, CONTENT_SENSITIVITY_AUTO));
break;
}
}
setOverScrollMode(overScrollMode);
// Cache start/end user padding as we cannot fully resolve padding here (we don't have yet
// the resolved layout direction). Those cached values will be used later during padding
// resolution.
mUserPaddingStart = startPadding;
mUserPaddingEnd = endPadding;
if (background != null) {
setBackground(background);
}
// setBackground above will record that padding is currently provided by the background.
// If we have padding specified via xml, record that here instead and use it.
mLeftPaddingDefined = leftPaddingDefined;
mRightPaddingDefined = rightPaddingDefined;
// Valid paddingHorizontal/paddingVertical beats leftPadding, rightPadding, topPadding,
// bottomPadding, and padding set by background. Valid padding beats everything.
if (padding >= 0) {
leftPadding = padding;
topPadding = padding;
rightPadding = padding;
bottomPadding = padding;
mUserPaddingLeftInitial = padding;
mUserPaddingRightInitial = padding;
} else {
if (paddingHorizontal >= 0) {
leftPadding = paddingHorizontal;
rightPadding = paddingHorizontal;
mUserPaddingLeftInitial = paddingHorizontal;
mUserPaddingRightInitial = paddingHorizontal;
}
if (paddingVertical >= 0) {
topPadding = paddingVertical;
bottomPadding = paddingVertical;
}
}
if (isRtlCompatibilityMode()) {
// RTL compatibility mode: pre Jelly Bean MR1 case OR no RTL support case.
// left / right padding are used if defined (meaning here nothing to do). If they are not
// defined and start / end padding are defined (e.g. in Frameworks resources), then we use
// start / end and resolve them as left / right (layout direction is not taken into account).
// Padding from the background drawable is stored at this point in mUserPaddingLeftInitial
// and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if
// defined.
if (!mLeftPaddingDefined && startPaddingDefined) {
leftPadding = startPadding;
}
mUserPaddingLeftInitial = (leftPadding >= 0) ? leftPadding : mUserPaddingLeftInitial;
if (!mRightPaddingDefined && endPaddingDefined) {
rightPadding = endPadding;
}
mUserPaddingRightInitial = (rightPadding >= 0) ? rightPadding : mUserPaddingRightInitial;
} else {
// Jelly Bean MR1 and after case: if start/end defined, they will override any left/right
// values defined. Otherwise, left /right values are used.
// Padding from the background drawable is stored at this point in mUserPaddingLeftInitial
// and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if
// defined.
final boolean hasRelativePadding = startPaddingDefined || endPaddingDefined;
if (mLeftPaddingDefined && !hasRelativePadding) {
mUserPaddingLeftInitial = leftPadding;
}
if (mRightPaddingDefined && !hasRelativePadding) {
mUserPaddingRightInitial = rightPadding;
}
}
// mPaddingTop and mPaddingBottom may have been set by setBackground(Drawable) so must pass
// them on if topPadding or bottomPadding are not valid.
internalSetPadding(
mUserPaddingLeftInitial,
topPadding >= 0 ? topPadding : mPaddingTop,
mUserPaddingRightInitial,
bottomPadding >= 0 ? bottomPadding : mPaddingBottom);
if (viewFlagMasks != 0) {
setFlags(viewFlagValues, viewFlagMasks);
}
if (initializeScrollbars) {
initializeScrollbarsInternal(a);
}
if (initializeScrollIndicators) {
initializeScrollIndicatorsInternal();
}
a.recycle();
// Needs to be called after mViewFlags is set
if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
recomputePadding();
}
if (x != 0 || y != 0) {
scrollTo(x, y);
}
if (transformSet) {
setTranslationX(tx);
setTranslationY(ty);
setTranslationZ(tz);
setElevation(elevation);
setRotation(rotation);
setRotationX(rotationX);
setRotationY(rotationY);
setScaleX(sx);
setScaleY(sy);
}
if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) {
setScrollContainer(true);
}
computeOpaqueFlags();
}
/**
* Returns the ordered list of resource ID that are considered when resolving attribute values
* for this {@link View}. The list will include layout resource ID if the View is inflated from
* XML. It will also include a set of explicit styles if specified in XML using
* {@code style="..."}. Finally, it will include the default styles resolved from the theme.
*
*
*
* @see #setScrollIndicators(int)
* @see #getScrollIndicators()
* @attr ref android.R.styleable#View_scrollIndicators
*/
public void setScrollIndicators(@ScrollIndicators int indicators, @ScrollIndicators int mask) {
// Shift and sanitize mask.
mask <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
mask &= SCROLL_INDICATORS_PFLAG3_MASK;
// Shift and mask indicators.
indicators <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
indicators &= mask;
// Merge with non-masked flags.
final int updatedFlags = indicators | (mPrivateFlags3 & ~mask);
if (mPrivateFlags3 != updatedFlags) {
mPrivateFlags3 = updatedFlags;
if (indicators != 0) {
initializeScrollIndicatorsInternal();
}
invalidate();
}
}
/**
* Returns a bitmask representing the enabled scroll indicators.
* outRect
with the hotspot bounds. By default,
* the hotspot bounds are identical to the screen bounds.
*
* @param outRect rect to populate with hotspot bounds
* @hide Only for internal use by views and widgets.
*/
public void getHotspotBounds(Rect outRect) {
final Drawable background = getBackground();
if (background != null) {
background.getHotspotBounds(outRect);
} else {
getBoundsOnScreen(outRect);
}
}
/**
* Request that a rectangle of this view be visible on the screen,
* scrolling if necessary just enough.
*
* immediate
is set to true, scrolling will not be
* animated.
*
* @param rectangle The rectangle in the View's content coordinate space
* @param immediate True to forbid animated scrolling, false otherwise
* @return Whether any parent scrolled.
*/
public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) {
if (mParent == null) {
return false;
}
View child = this;
RectF position = (mAttachInfo != null) ? mAttachInfo.mTmpTransformRect : new RectF();
position.set(rectangle);
ViewParent parent = mParent;
boolean scrolled = false;
while (parent != null) {
rectangle.set((int) position.left, (int) position.top,
(int) position.right, (int) position.bottom);
scrolled |= parent.requestChildRectangleOnScreen(child, rectangle, immediate);
if (!(parent instanceof View)) {
break;
}
// move it from child's content coordinate space to parent's content coordinate space
position.offset(child.mLeft - child.getScrollX(), child.mTop -child.getScrollY());
child = (View) parent;
parent = child.getParent();
}
return scrolled;
}
/**
* Called when this view wants to give up focus. If focus is cleared
* {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called.
* null
otherwise.
*/
@CallSuper
protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
@Nullable Rect previouslyFocusedRect) {
if (DBG) {
Log.d(VIEW_LOG_TAG, "onFocusChanged() entered. gainFocus: "
+ gainFocus);
}
if (gainFocus) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
} else {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
// Here we check whether we still need the default focus highlight, and switch it on/off.
switchDefaultFocusHighlight();
if (!gainFocus) {
if (isPressed()) {
setPressed(false);
}
if (hasWindowFocus()) {
notifyFocusChangeToImeFocusController(false /* hasFocus */);
}
onFocusLost();
} else if (hasWindowFocus()) {
notifyFocusChangeToImeFocusController(true /* hasFocus */);
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null) {
if (mIsHandwritingDelegate) {
viewRoot.getHandwritingInitiator().onDelegateViewFocused(this);
} else if (initiationWithoutInputConnection() && onCheckIsTextEditor()) {
viewRoot.getHandwritingInitiator().onEditorFocused(this);
}
}
}
invalidate(true);
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnFocusChangeListener != null) {
li.mOnFocusChangeListener.onFocusChange(this, gainFocus);
}
if (mAttachInfo != null) {
mAttachInfo.mKeyDispatchState.reset(this);
}
if (mParent != null) {
mParent.onDescendantUnbufferedRequested();
}
notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
updatePreferKeepClearForFocus();
}
/**
* Notify {@link ImeFocusController} about the focus change of the {@link View}.
*
* @param hasFocus {@code true} when the {@link View} is being focused.
*/
private void notifyFocusChangeToImeFocusController(boolean hasFocus) {
if (mAttachInfo == null) {
return;
}
mAttachInfo.mViewRootImpl.getImeFocusController().onViewFocusChanged(this, hasFocus);
}
/** @hide */
public void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
if (canNotifyAutofillEnterExitEvent()) {
AutofillManager afm = getAutofillManager();
if (afm != null) {
if (DBG) {
Log.d(VIEW_LOG_TAG, this + " afm is not null");
}
if (enter) {
// We have not been laid out yet, hence cannot evaluate
// whether this view is visible to the user, we will do
// the evaluation once layout is complete.
// Sometimes, views are already laid out, but it's still
// not visible to the user, we also do the evaluation once
// the view is visible. ex: There is a fade-in animation
// for the activity, the view will be laid out when the
// animation beginning. On the time, the view is not visible
// to the user. And then as the animation progresses, the view
// becomes visible to the user.
if (DBG) {
Log.d(VIEW_LOG_TAG,
"notifyEnterOrExitForAutoFillIfNeeded:"
+ " isLaidOut(): " + isLaidOut()
+ " isVisibleToUser(): " + isVisibleToUser()
+ " isFocused(): " + isFocused());
}
if (!isLaidOut() || !isVisibleToUser()) {
mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
} else if (isVisibleToUser()) {
if (isFocused()) {
// TODO This is a potential problem that View gets focus before it's
// visible to User. Ideally View should handle the event when
// isVisibleToUser() becomes true where it should issue
// notifyViewEntered().
afm.notifyViewEntered(this);
} else {
afm.notifyViewEnteredForFillDialog(this);
}
}
} else if (!isFocused()) {
afm.notifyViewExited(this);
}
}
}
}
/**
* Visually distinct portion of a window with window-like semantics are considered panes for
* accessibility purposes. One example is the content view of a large fragment that is replaced.
* In order for accessibility services to understand a pane's window-like behavior, panes
* should have descriptive titles. Views with pane titles produce
* {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}s when they appear, disappear, or change
* title.
*
*
*
* public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
* super.onPopulateAccessibilityEvent(event);
* final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY;
* String selectedDateUtterance = DateUtils.formatDateTime(mContext,
* mCurrentDate.getTimeInMillis(), flags);
* event.getText().add(selectedDateUtterance);
* }
* public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
* super.onInitializeAccessibilityEvent(event);
* event.setPassword(true);
* }
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
}
/** @hide */
protected void onProvideStructure(@NonNull ViewStructure structure,
@ViewStructureType int viewFor, int flags) {
final int id = mID;
if (id != NO_ID && !isViewIdGenerated(id)) {
String pkg, type, entry;
try {
final Resources res = getResources();
entry = res.getResourceEntryName(id);
type = res.getResourceTypeName(id);
pkg = res.getResourcePackageName(id);
} catch (Resources.NotFoundException e) {
entry = type = pkg = null;
}
structure.setId(id, pkg, type, entry);
} else {
structure.setId(id, null, null, null);
}
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
|| viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
final @AutofillType int autofillType = getAutofillType();
// Don't need to fill autofill info if view does not support it.
// For example, only TextViews that are editable support autofill
if (autofillType != AUTOFILL_TYPE_NONE) {
structure.setAutofillType(autofillType);
structure.setAutofillHints(getAutofillHints());
structure.setAutofillValue(getAutofillValue());
structure.setIsCredential(isCredential());
}
if (getViewCredentialHandler() != null) {
structure.setPendingCredentialRequest(
getViewCredentialHandler().getRequest(),
getViewCredentialHandler().getCallback());
}
structure.setImportantForAutofill(getImportantForAutofill());
structure.setReceiveContentMimeTypes(getReceiveContentMimeTypes());
}
int ignoredParentLeft = 0;
int ignoredParentTop = 0;
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
&& (flags & AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) {
View parentGroup = null;
ViewParent viewParent = getParent();
if (viewParent instanceof View) {
parentGroup = (View) viewParent;
}
while (parentGroup != null && !parentGroup.isImportantForAutofill()) {
ignoredParentLeft += parentGroup.mLeft - parentGroup.mScrollX;
ignoredParentTop += parentGroup.mTop - parentGroup.mScrollY;
viewParent = parentGroup.getParent();
if (viewParent instanceof View) {
parentGroup = (View) viewParent;
} else {
break;
}
}
}
structure.setDimens(ignoredParentLeft + mLeft, ignoredParentTop + mTop, mScrollX, mScrollY,
mRight - mLeft, mBottom - mTop);
if (viewFor == VIEW_STRUCTURE_FOR_ASSIST) {
if (!hasIdentityMatrix()) {
structure.setTransformation(getMatrix());
}
structure.setElevation(getZ());
}
structure.setVisibility(getVisibility());
structure.setEnabled(isEnabled());
if (isClickable()) {
structure.setClickable(true);
}
if (isFocusable()) {
structure.setFocusable(true);
}
if (isFocused()) {
structure.setFocused(true);
}
if (isAccessibilityFocused()) {
structure.setAccessibilityFocused(true);
}
if (isSelected()) {
structure.setSelected(true);
}
if (isActivated()) {
structure.setActivated(true);
}
if (isLongClickable()) {
structure.setLongClickable(true);
}
if (this instanceof Checkable) {
structure.setCheckable(true);
if (((Checkable)this).isChecked()) {
structure.setChecked(true);
}
}
if (isOpaque()) {
structure.setOpaque(true);
}
if (isContextClickable()) {
structure.setContextClickable(true);
}
structure.setClassName(getAccessibilityClassName().toString());
structure.setContentDescription(getContentDescription());
}
/**
* Called when assist structure is being retrieved from a view as part of
* {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to
* generate additional virtual structure under this view. The default implementation
* uses {@link #getAccessibilityNodeProvider()} to try to generate this from the
* view's virtual accessibility nodes, if any. You can override this for a more
* optimal implementation providing this data.
*/
public void onProvideVirtualStructure(ViewStructure structure) {
onProvideVirtualStructureCompat(structure, false);
}
/**
* Fallback implementation to populate a ViewStructure from accessibility state.
*
* @param structure The structure to populate.
* @param forAutofill Whether the structure is needed for autofill.
*/
private void onProvideVirtualStructureCompat(ViewStructure structure, boolean forAutofill) {
final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
if (provider != null) {
if (forAutofill && Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
Log.v(AUTOFILL_LOG_TAG, "onProvideVirtualStructureCompat() for " + this);
}
final AccessibilityNodeInfo info = createAccessibilityNodeInfo();
structure.setChildCount(1);
final ViewStructure root = structure.newChild(0);
if (info != null) {
populateVirtualStructure(root, provider, info, forAutofill);
info.recycle();
} else {
Log.w(AUTOFILL_LOG_TAG, "AccessibilityNodeInfo is null.");
}
}
}
/**
* Populates a {@link ViewStructure} containing virtual children to fullfil an autofill
* request.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* @Override
* public void autofill(AutofillValue value) {
* if (!value.isText() || !this.isEditable()) {
* return;
* }
* CharSequence text = value.getTextValue();
* if (text != null) {
* this.setText(text);
* }
* }
*
*
*
*
* ?android:attr/autofilledHighlight
should be drawn over it until the data
* changes.
*
* @param values map of values to be autofilled, keyed by virtual child id.
*
* @attr ref android.R.styleable#Theme_autofilledHighlight
*/
public void autofill(@NonNull @SuppressWarnings("unused") SparseArray
* EditText reusableView = ...;
* ViewGroup parentView = ...;
* AutofillManager afm = ...;
*
* // Swap out the view and change its contents
* AutofillId oldId = reusableView.getAutofillId();
* CharSequence oldText = reusableView.getText();
* parentView.removeView(reusableView);
* AutofillId newId = afm.getNextAutofillId();
* reusableView.setText("New I am");
* reusableView.setAutofillId(newId);
* parentView.addView(reusableView);
*
* // Later, swap the old content back in
* parentView.removeView(reusableView);
* reusableView.setAutofillId(oldId);
* reusableView.setText(oldText);
* parentView.addView(reusableView);
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* @return whether the view is considered important for autofill.
*
* @see #setImportantForAutofill(int)
* @see #IMPORTANT_FOR_AUTOFILL_AUTO
* @see #IMPORTANT_FOR_AUTOFILL_YES
* @see #IMPORTANT_FOR_AUTOFILL_NO
* @see #IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS
* @see #IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
* @see AutofillManager#requestAutofill(View)
*/
public final boolean isImportantForAutofill() {
// Check parent mode to ensure we're not hidden.
ViewParent parent = mParent;
while (parent instanceof View) {
final int parentImportance = ((View) parent).getImportantForAutofill();
if (parentImportance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
|| parentImportance == IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS) {
if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
Log.v(AUTOFILL_LOG_TAG, "View (" + this + ") is not important for autofill "
+ "because parent " + parent + "'s importance is " + parentImportance);
}
return false;
}
parent = parent.getParent();
}
final int importance = getImportantForAutofill();
// First, check the explicit states.
if (importance == IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS
|| importance == IMPORTANT_FOR_AUTOFILL_YES) {
return true;
}
if (importance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
|| importance == IMPORTANT_FOR_AUTOFILL_NO) {
if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
Log.v(AUTOFILL_LOG_TAG, "View (" + this + ") is not important for autofill "
+ "because its importance is " + importance);
}
return false;
}
// Then use some heuristics to handle AUTO.
if (importance != IMPORTANT_FOR_AUTOFILL_AUTO) {
Log.w(AUTOFILL_LOG_TAG, "invalid autofill importance (" + importance + " on view "
+ this);
return false;
}
// Always include views that have an explicit resource id.
final int id = mID;
if (id != NO_ID && !isViewIdGenerated(id)) {
final Resources res = getResources();
String entry = null;
String pkg = null;
try {
entry = res.getResourceEntryName(id);
pkg = res.getResourcePackageName(id);
} catch (Resources.NotFoundException e) {
// ignore
}
if (entry != null && pkg != null && pkg.equals(mContext.getPackageName())) {
return true;
}
}
// If the app developer explicitly set hints for it, it's important.
if (getAutofillHints() != null) {
return true;
}
// Otherwise, assume it's not important...
return false;
}
/**
* Sets content sensitivity mode to determine whether this view displays sensitive content
* (e.g. username, password etc.). The system will improve user privacy i.e. hide content
* drawn by a sensitive view from screen sharing and recording.
*
*
*
*
*
*
*
*
*
*
*
*/
private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
AttachInfo ai = mAttachInfo;
// Skip it while the view is being laid out for the first time
if (ai != null && !ai.mReadyForContentCaptureUpdates) return;
// First check if context has client, so it saves a service lookup when it doesn't
if (mContext.getContentCaptureOptions() == null) return;
if (appeared) {
// The appeared event stops sending to AiAi.
// 1. The view is hidden.
// 2. The same event was sent.
// 3. The view is not laid out, and it will be laid out in the future.
// Some recycled views cached its layout and a relayout is unnecessary. In this case,
// system still needs to notify content capture the view appeared. When a view is
// recycled, it will set the flag PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED.
final boolean isRecycledWithoutRelayout = getNotifiedContentCaptureDisappeared()
&& getVisibility() == VISIBLE
&& !isLayoutRequested();
if (getVisibility() != VISIBLE || getNotifiedContentCaptureAppeared()
|| !(isLaidOut() || isRecycledWithoutRelayout)) {
if (DEBUG_CONTENT_CAPTURE) {
Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
+ isLaidOut() + ", visibleToUser=" + isVisibleToUser()
+ ", visible=" + (getVisibility() == VISIBLE)
+ ": alreadyNotifiedAppeared=" + getNotifiedContentCaptureAppeared()
+ ", alreadyNotifiedDisappeared="
+ getNotifiedContentCaptureDisappeared());
}
return;
}
} else {
if (!getNotifiedContentCaptureAppeared() || getNotifiedContentCaptureDisappeared()) {
if (DEBUG_CONTENT_CAPTURE) {
Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid="
+ isLaidOut() + ", visibleToUser=" + isVisibleToUser()
+ ", visible=" + (getVisibility() == VISIBLE)
+ ": alreadyNotifiedAppeared=" + getNotifiedContentCaptureAppeared()
+ ", alreadyNotifiedDisappeared="
+ getNotifiedContentCaptureDisappeared());
}
return;
}
}
ContentCaptureSession session = getContentCaptureSession();
if (session == null) return;
// ... and finally at the view level
// NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled()
if (!isImportantForContentCapture()) return;
if (appeared) {
setNotifiedContentCaptureAppeared();
if (ai != null) {
makeParentImportantAndNotifyAppearedEventIfNeed();
ai.delayNotifyContentCaptureEvent(session, this, appeared);
} else {
if (DEBUG_CONTENT_CAPTURE) {
Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this);
}
}
} else {
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
if (ai != null) {
ai.delayNotifyContentCaptureEvent(session, this, appeared);
} else {
if (DEBUG_CONTENT_CAPTURE) {
Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this);
}
}
// We reset any translation state as views may be re-used (e.g., as in ListView and
// RecyclerView). We only need to do this for views important for content capture since
// views unimportant for content capture won't be translated anyway.
if (!isTemporarilyDetached()) {
clearTranslationState();
}
}
}
private void makeParentImportantAndNotifyAppearedEventIfNeed() {
// If view sent the appeared event to Content Capture, Content Capture also
// would like to receive its parents' appeared events. So checks its parents
// whether the appeared event is sent or not. If not, send the appeared event.
final ViewParent parent = getParent();
if (parent instanceof View) {
View p = ((View) parent);
if (p.getNotifiedContentCaptureAppeared()) {
return;
}
// Set important for content capture in the cache.
p.mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
p.notifyAppearedOrDisappearedForContentCaptureIfNeeded(/* appeared */ true);
}
}
private void setNotifiedContentCaptureAppeared() {
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
}
/** @hide */
protected boolean getNotifiedContentCaptureAppeared() {
return (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0;
}
private boolean getNotifiedContentCaptureDisappeared() {
return (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0;
}
/**
* Sets the (optional) {@link ContentCaptureSession} associated with this view.
*
*
* ContentCaptureSession mainSession = rootView.getContentCaptureSession();
* mainSession.setContentCaptureContext(ContentCaptureContext.forLocusId(Uri.parse(myUrl));
*
*
*
* ContentCaptureSession iframeSession = mainSession.createContentCaptureSession(
* ContentCaptureContext.forLocusId(Uri.parse(iframeUrl)));
* iframeView.setContentCaptureSession(iframeSession);
*
*
* @param contentCaptureSession a session created by
* {@link ContentCaptureSession#createContentCaptureSession(
* android.view.contentcapture.ContentCaptureContext)}.
*/
public void setContentCaptureSession(@Nullable ContentCaptureSession contentCaptureSession) {
mContentCaptureSession = contentCaptureSession;
}
/**
* Gets the session used to notify content capture events.
*
* @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)},
* inherited by ancestors, default session or {@code null} if content capture is disabled for
* this view.
*/
@Nullable
public final ContentCaptureSession getContentCaptureSession() {
if (mContentCaptureSessionCached) {
return mContentCaptureSession;
}
mContentCaptureSession = getAndCacheContentCaptureSession();
mContentCaptureSessionCached = true;
return mContentCaptureSession;
}
@Nullable
private ContentCaptureSession getAndCacheContentCaptureSession() {
// First try the session explicitly set by setContentCaptureSession()
if (mContentCaptureSession != null) {
return mContentCaptureSession;
}
// Then the session explicitly set in an ancestor
ContentCaptureSession session = null;
if (mParent instanceof View) {
session = ((View) mParent).getContentCaptureSession();
}
// Finally, if no session was explicitly set, use the context's default session.
if (session == null) {
final ContentCaptureManager ccm = mContext
.getSystemService(ContentCaptureManager.class);
return ccm == null ? null : ccm.getMainContentCaptureSession();
}
return session;
}
@Nullable
private AutofillManager getAutofillManager() {
return mContext.getSystemService(AutofillManager.class);
}
/**
* Check whether current activity / package is in autofill denylist.
*
* Called by viewGroup#populateChildrenForAutofill() to determine whether to include view in
* assist structure
*/
final boolean isActivityDeniedForAutofillForUnimportantView() {
final AutofillManager afm = getAutofillManager();
if (afm == null) return false;
return afm.isActivityDeniedForAutofill();
}
/**
* Check whether current view matches autofillable heuristics
*
* Called by viewGroup#populateChildrenForAutofill() to determine whether to include view in
* assist structure
*/
final boolean isMatchingAutofillableHeuristics() {
final AutofillManager afm = getAutofillManager();
if (afm == null) return false;
// check the flag to see if trigger fill request on not important views is enabled
return afm.isTriggerFillRequestOnUnimportantViewEnabled()
? afm.isAutofillable(this) : false;
}
private boolean isAutofillable() {
if (DBG) {
Log.d(VIEW_LOG_TAG, "isAutofillable() entered.");
}
if (getAutofillType() == AUTOFILL_TYPE_NONE) {
if (DBG) {
Log.d(VIEW_LOG_TAG, "getAutofillType() returns AUTOFILL_TYPE_NONE");
}
return false;
}
final AutofillManager afm = getAutofillManager();
if (afm == null) {
if (DBG) {
Log.d(VIEW_LOG_TAG, "AutofillManager is null");
}
return false;
}
// Check whether view is not part of an activity. If it's not, return false.
if (getAutofillViewId() <= LAST_APP_AUTOFILL_ID) {
if (DBG) {
Log.d(VIEW_LOG_TAG, "getAutofillViewId()<=LAST_APP_AUTOFILL_ID");
}
return false;
}
// If view is important and filter important view flag is turned on, or view is not
// important and trigger fill request on not important view flag is turned on, then use
// AutofillManager.isAutofillable() to decide whether view is autofillable instead.
if ((isImportantForAutofill() && afm.isTriggerFillRequestOnFilteredImportantViewsEnabled())
|| (!isImportantForAutofill()
&& afm.isTriggerFillRequestOnUnimportantViewEnabled())) {
if (DBG) {
Log.d(VIEW_LOG_TAG, "isImportantForAutofill(): " + isImportantForAutofill()
+ "afm.isAutofillable(): " + afm.isAutofillable(this));
}
return afm.isAutofillable(this) ? true : notifyAugmentedAutofillIfNeeded(afm);
}
// If the previous condition is not met, fall back to the previous way to trigger fill
// request based on autofill importance instead.
if (DBG) {
Log.d(VIEW_LOG_TAG, "isImportantForAutofill(): " + isImportantForAutofill());
}
return isImportantForAutofill() ? true : notifyAugmentedAutofillIfNeeded(afm);
}
private boolean notifyAugmentedAutofillIfNeeded(AutofillManager afm) {
final AutofillOptions options = mContext.getAutofillOptions();
if (options == null || !options.isAugmentedAutofillEnabled(mContext)) {
return false;
}
afm.notifyViewEnteredForAugmentedAutofill(this);
return true;
}
/** @hide */
public boolean canNotifyAutofillEnterExitEvent() {
if (DBG) {
Log.d(VIEW_LOG_TAG, "canNotifyAutofillEnterExitEvent() entered. "
+ " isAutofillable(): " + isAutofillable()
+ " isAttachedToWindow(): " + isAttachedToWindow());
}
return isAutofillable() && isAttachedToWindow();
}
private void populateVirtualStructure(ViewStructure structure,
AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
boolean forAutofill) {
structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
null, null, info.getViewIdResourceName());
Rect rect = structure.getTempRect();
info.getBoundsInParent(rect);
structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
structure.setVisibility(VISIBLE);
structure.setEnabled(info.isEnabled());
if (info.isClickable()) {
structure.setClickable(true);
}
if (info.isFocusable()) {
structure.setFocusable(true);
}
if (info.isFocused()) {
structure.setFocused(true);
}
if (info.isAccessibilityFocused()) {
structure.setAccessibilityFocused(true);
}
if (info.isSelected()) {
structure.setSelected(true);
}
if (info.isLongClickable()) {
structure.setLongClickable(true);
}
if (info.isCheckable()) {
structure.setCheckable(true);
if (info.isChecked()) {
structure.setChecked(true);
}
}
if (info.isContextClickable()) {
structure.setContextClickable(true);
}
if (forAutofill) {
structure.setAutofillId(new AutofillId(getAutofillId(),
AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
}
if (getViewCredentialHandler() != null) {
structure.setPendingCredentialRequest(
getViewCredentialHandler().getRequest(),
getViewCredentialHandler().getCallback());
}
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
structure.setContentDescription(info.getContentDescription());
if (forAutofill) {
final int maxTextLength = info.getMaxTextLength();
if (maxTextLength != -1) {
structure.setMaxTextLength(maxTextLength);
}
structure.setHint(info.getHintText());
}
CharSequence text = info.getText();
boolean hasText = text != null || info.getError() != null;
if (hasText) {
structure.setText(text, info.getTextSelectionStart(), info.getTextSelectionEnd());
}
if (forAutofill) {
if (info.isEditable()) {
structure.setDataIsSensitive(true);
if (hasText) {
structure.setAutofillType(AUTOFILL_TYPE_TEXT);
structure.setAutofillValue(AutofillValue.forText(text));
}
int inputType = info.getInputType();
if (inputType == 0 && info.isPassword()) {
inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD;
}
structure.setInputType(inputType);
} else {
structure.setDataIsSensitive(false);
}
}
final int NCHILDREN = info.getChildCount();
if (NCHILDREN > 0) {
structure.setChildCount(NCHILDREN);
for (int i=0; i
*
*
*
*
*
* @param structure fill in with structured view data for autofill purposes.
* @param flags optional flags.
*
* @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
*/
public void dispatchProvideAutofillStructure(@NonNull ViewStructure structure,
@AutofillFlags int flags) {
dispatchProvideStructure(structure, VIEW_STRUCTURE_FOR_AUTOFILL, flags);
}
private void dispatchProvideStructure(@NonNull ViewStructure structure,
@ViewStructureType int viewFor, @AutofillFlags int flags) {
if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) {
structure.setAutofillId(getAutofillId());
onProvideAutofillStructure(structure, flags);
onProvideAutofillVirtualStructure(structure, flags);
} else if (!isAssistBlocked()) {
onProvideStructure(structure);
onProvideVirtualStructure(structure);
} else {
structure.setClassName(getAccessibilityClassName().toString());
structure.setAssistBlocked(true);
}
}
/**
* Dispatches the initial content capture events for a view structure.
*
* @hide
*/
public void dispatchInitialProvideContentCaptureStructure() {
AttachInfo ai = mAttachInfo;
if (ai == null) {
Log.w(CONTENT_CAPTURE_LOG_TAG,
"dispatchProvideContentCaptureStructure(): no AttachInfo for " + this);
return;
}
ContentCaptureManager ccm = ai.mContentCaptureManager;
if (ccm == null) {
Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): "
+ "no ContentCaptureManager for " + this);
return;
}
// We must set it before checkign if the view itself is important, because it might
// initially not be (for example, if it's empty), although that might change later (for
// example, if important views are added)
ai.mReadyForContentCaptureUpdates = true;
if (!isImportantForContentCapture()) {
if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
Log.d(CONTENT_CAPTURE_LOG_TAG,
"dispatchProvideContentCaptureStructure(): decorView is not important");
}
return;
}
ai.mContentCaptureManager = ccm;
ContentCaptureSession session = getContentCaptureSession();
if (session == null) {
if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
Log.d(CONTENT_CAPTURE_LOG_TAG,
"dispatchProvideContentCaptureStructure(): no session for " + this);
}
return;
}
session.notifyViewTreeEvent(/* started= */ true);
try {
dispatchProvideContentCaptureStructure();
} finally {
session.notifyViewTreeEvent(/* started= */ false);
}
}
/** @hide */
void dispatchProvideContentCaptureStructure() {
ContentCaptureSession session = getContentCaptureSession();
if (session != null) {
ViewStructure structure = session.newViewStructure(this);
onProvideContentCaptureStructure(structure, /* flags= */ 0);
setNotifiedContentCaptureAppeared();
session.notifyViewAppeared(structure);
}
}
/**
* @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
*
* Note: Called from the default {@link AccessibilityDelegate}.
*
* @hide
*/
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
if (mAttachInfo == null) {
return;
}
Rect bounds = mAttachInfo.mTmpInvalRect;
getDrawingRect(bounds);
info.setBoundsInParent(bounds);
getBoundsOnScreen(bounds, true);
info.setBoundsInScreen(bounds);
getBoundsInWindow(bounds, true);
info.setBoundsInWindow(bounds);
ViewParent parent = getParentForAccessibility();
if (parent instanceof View) {
info.setParent((View) parent);
}
if (mID != View.NO_ID) {
View rootView = getRootView();
if (rootView == null) {
rootView = this;
}
View label = rootView.findLabelForView(this, mID);
if (label != null) {
info.setLabeledBy(label);
}
if ((mAttachInfo.mAccessibilityFetchFlags
& AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS) != 0
&& Resources.resourceHasPackage(mID)) {
try {
String viewId = getResources().getResourceName(mID);
info.setViewIdResourceName(viewId);
} catch (Resources.NotFoundException nfe) {
/* ignore */
}
}
}
if (mLabelForId != View.NO_ID) {
View rootView = getRootView();
if (rootView == null) {
rootView = this;
}
View labeled = rootView.findViewInsideOutShouldExist(this, mLabelForId);
if (labeled != null) {
info.setLabelFor(labeled);
}
}
if (mAccessibilityTraversalBeforeId != View.NO_ID) {
View rootView = getRootView();
if (rootView == null) {
rootView = this;
}
View next = rootView.findViewInsideOutShouldExist(this,
mAccessibilityTraversalBeforeId);
if (next != null && next.includeForAccessibility()) {
info.setTraversalBefore(next);
}
}
if (mAccessibilityTraversalAfterId != View.NO_ID) {
View rootView = getRootView();
if (rootView == null) {
rootView = this;
}
View next = rootView.findViewInsideOutShouldExist(this,
mAccessibilityTraversalAfterId);
if (next != null && next.includeForAccessibility()) {
info.setTraversalAfter(next);
}
}
info.setVisibleToUser(isVisibleToUser());
info.setImportantForAccessibility(isImportantForAccessibility());
info.setAccessibilityDataSensitive(isAccessibilityDataSensitive());
info.setPackageName(mContext.getPackageName());
info.setClassName(getAccessibilityClassName());
info.setStateDescription(getStateDescription());
info.setContentDescription(getContentDescription());
info.setEnabled(isEnabled());
info.setClickable(isClickable());
info.setFocusable(isFocusable());
info.setScreenReaderFocusable(isScreenReaderFocusable());
info.setFocused(isFocused());
info.setAccessibilityFocused(isAccessibilityFocused());
info.setSelected(isSelected());
info.setLongClickable(isLongClickable());
info.setContextClickable(isContextClickable());
info.setLiveRegion(getAccessibilityLiveRegion());
if ((mTooltipInfo != null) && (mTooltipInfo.mTooltipText != null)) {
info.setTooltipText(mTooltipInfo.mTooltipText);
info.addAction((mTooltipInfo.mTooltipPopup == null)
? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP
: AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP);
}
// TODO: These make sense only if we are in an AdapterView but all
// views can be selected. Maybe from accessibility perspective
// we should report as selectable view in an AdapterView.
info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
if (isFocusable()) {
if (isFocused()) {
info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
} else {
info.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
}
}
if (!isAccessibilityFocused()) {
info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
} else {
info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
}
if (isClickable() && isEnabled()) {
info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
}
if (isLongClickable() && isEnabled()) {
info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
}
if (isContextClickable() && isEnabled()) {
info.addAction(AccessibilityAction.ACTION_CONTEXT_CLICK);
}
CharSequence text = getIterableTextForAccessibility();
if (text != null && text.length() > 0) {
info.setTextSelection(getAccessibilitySelectionStart(), getAccessibilitySelectionEnd());
info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
}
info.addAction(AccessibilityAction.ACTION_SHOW_ON_SCREEN);
populateAccessibilityNodeInfoDrawingOrderInParent(info);
info.setPaneTitle(mAccessibilityPaneTitle);
info.setHeading(isAccessibilityHeading());
if (mTouchDelegate != null) {
info.setTouchDelegateInfo(mTouchDelegate.getTouchDelegateInfo());
}
if (startedSystemDragForAccessibility()) {
info.addAction(AccessibilityAction.ACTION_DRAG_CANCEL);
}
if (canAcceptAccessibilityDrop()) {
info.addAction(AccessibilityAction.ACTION_DRAG_DROP);
}
}
/**
* Adds extra data to an {@link AccessibilityNodeInfo} based on an explicit request for the
* additional data.
* null
, and the entire view will be tested in this case.
* When true
is returned by the function, the actual visible
* region will be stored in this parameter; that is, if boundInView is fully
* contained within the view, no modification will be made, otherwise regions
* outside of the visible area of the view will be clipped.
*
* @return Whether the specified portion of the view is visible on the screen.
*
* @hide
*/
@UnsupportedAppUsage(trackingBug = 171933273)
protected boolean isVisibleToUser(Rect boundInView) {
if (mAttachInfo != null) {
// Attached to invisible window means this view is not visible.
if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
return false;
}
// An invisible predecessor or one with alpha zero means
// that this view is not visible to the user.
Object current = this;
while (current instanceof View) {
View view = (View) current;
// We have attach info so this view is attached and there is no
// need to check whether we reach to ViewRootImpl on the way up.
if (view.getAlpha() <= 0 || view.getTransitionAlpha() <= 0 ||
view.getVisibility() != VISIBLE) {
return false;
}
current = view.mParent;
}
// Check if the view is entirely covered by its predecessors.
Rect visibleRect = mAttachInfo.mTmpInvalRect;
Point offset = mAttachInfo.mPoint;
if (!getGlobalVisibleRect(visibleRect, offset)) {
return false;
}
// Check if the visible portion intersects the rectangle of interest.
if (boundInView != null) {
visibleRect.offset(-offset.x, -offset.y);
return boundInView.intersect(visibleRect);
}
return true;
}
return false;
}
/**
* Returns the delegate for implementing accessibility support via
* composition. For more details see {@link AccessibilityDelegate}.
*
* @return The delegate, or null if none set.
*/
public AccessibilityDelegate getAccessibilityDelegate() {
return mAccessibilityDelegate;
}
/**
* Sets a delegate for implementing accessibility support via composition
* (as opposed to inheritance). For more details, see
* {@link AccessibilityDelegate}.
* ScrollView
or for simple press and release click targets such as
* Button
. Mark an exclusion rect when interacting with a view requires
* a precision touch gesture in a small area in either the X or Y dimension, such as
* an edge swipe or dragging a SeekBar
thumb.200dp
on the vertical extent of the
* exclusions it takes into account. The limit does not apply while the navigation
* bar is {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY stickily} hidden, nor to the
* {@link android.inputmethodservice.InputMethodService input method} and
* {@link Intent#CATEGORY_HOME home activity}.
*
* delegatorView.setHandwritingDelegatorCallback(callback);
* delegatorView.setAllowedHandwritingDelegatePackage(package2);
*
* Then to configure the corresponding delegate editor view in package 2:
*
*
* delegateEditorView.setIsHandwritingDelegate(true);
* delegateEditorView.setAllowedHandwritingDelegatorPackage(package1);
*
* @param allowedPackageName the package name of a delegate editor view linked to this delegator
* view, or {@code null} to restore the default behavior of only allowing delegate editor
* views from the same package as this delegator view
*/
public void setAllowedHandwritingDelegatePackage(@Nullable String allowedPackageName) {
mAllowedHandwritingDelegatePackageName = allowedPackageName;
}
/**
* Returns the allowed package for delegate editor views for which this view may act as a
* handwriting delegator, as set by {@link #setAllowedHandwritingDelegatePackage}. If {@link
* #setAllowedHandwritingDelegatePackage} has not been called, or called with {@code null}
* argument, this will return {@code null}, meaning that this delegator view may only be used to
* initiate handwriting mode for a delegate editor view from the same package as this delegator
* view.
*/
@Nullable
public String getAllowedHandwritingDelegatePackageName() {
return mAllowedHandwritingDelegatePackageName;
}
/**
* Sets this view to be a handwriting delegate. If a delegate view creates an input connection
* while a stylus {@link MotionEvent} sequence from a delegator view is ongoing, handwriting
* mode will be initiated for the delegate view.
*
* @param isHandwritingDelegate whether this view is a handwriting initiation delegate
* @see #setHandwritingDelegatorCallback(Runnable)
*/
public void setIsHandwritingDelegate(boolean isHandwritingDelegate) {
mIsHandwritingDelegate = isHandwritingDelegate;
}
/**
* Returns whether this view has been set as a handwriting delegate by {@link
* #setIsHandwritingDelegate}.
*/
public boolean isHandwritingDelegate() {
return mIsHandwritingDelegate;
}
/**
* Specifies that a view from the specified package may act as a handwriting delegator for this
* delegate editor view. If this method is not called, only views from the same package as this
* delegate editor view may act as a handwriting delegator. This method allows specifying a
* different trusted package which may contain a delegator view linked to this delegate editor
* view.
*
* public boolean onGenericMotionEvent(MotionEvent event) {
* if (event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)) {
* if (event.getAction() == MotionEvent.ACTION_MOVE) {
* // process the joystick movement...
* return true;
* }
* }
* if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
* switch (event.getAction()) {
* case MotionEvent.ACTION_HOVER_MOVE:
* // process the hover movement...
* return true;
* case MotionEvent.ACTION_SCROLL:
* // process the scroll wheel movement...
* return true;
* }
* }
* return super.onGenericMotionEvent(event);
* }
*
* @param event The generic motion event being processed.
* @return True if the event was handled, false otherwise.
*/
public boolean onGenericMotionEvent(MotionEvent event) {
return false;
}
/**
* Dispatching hover events to {@link TouchDelegate} to improve accessibility.
*
*
*
*
*
* @param event The motion event.
* @return True if the event was handled, false otherwise.
*/
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
if ((viewFlags & ENABLED_MASK) == DISABLED
&& (mPrivateFlags4 & PFLAG4_ALLOW_CLICK_WHEN_DISABLED) == 0) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return clickable;
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
if ((viewFlags & TOOLTIP) == TOOLTIP) {
handleTooltipUp();
}
if (!clickable) {
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
}
mHasPerformedLongPress = false;
if (!clickable) {
checkForLongClick(
ViewConfiguration.getLongPressTimeout(),
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
break;
}
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
checkForLongClick(
ViewConfiguration.getLongPressTimeout(),
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
break;
case MotionEvent.ACTION_CANCEL:
if (clickable) {
setPressed(false);
}
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
break;
case MotionEvent.ACTION_MOVE:
if (clickable) {
drawableHotspotChanged(x, y);
}
final int motionClassification = event.getClassification();
final boolean ambiguousGesture =
motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;
int touchSlop = mTouchSlop;
if (ambiguousGesture && hasPendingLongPressCallback()) {
if (!pointInView(x, y, touchSlop)) {
// The default action here is to cancel long press. But instead, we
// just extend the timeout here, in case the classification
// stays ambiguous.
removeLongPressCallback();
long delay = (long) (ViewConfiguration.getLongPressTimeout()
* mAmbiguousGestureMultiplier);
// Subtract the time already spent
delay -= event.getEventTime() - event.getDownTime();
checkForLongClick(
delay,
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
touchSlop *= mAmbiguousGestureMultiplier;
}
// Be lenient about moving outside of buttons
if (!pointInView(x, y, touchSlop)) {
// Outside button
// Remove any future long press/tap checks
removeTapCallback();
removeLongPressCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
}
final boolean deepPress =
motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
if (deepPress && hasPendingLongPressCallback()) {
// process the long click action immediately
removeLongPressCallback();
checkForLongClick(
0 /* send immediately */,
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS);
}
break;
}
return true;
}
return false;
}
/**
* Called by {@link #measure(int, int)} to check if the current frame presentation got
* delayed by an expensive view mesures during the input event dispatching. (e.g. scrolling)
*/
private boolean hasExpensiveMeasuresDuringInputEvent() {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo == null || attachInfo.mRootView == null) {
return false;
}
if (!attachInfo.mHandlingPointerEvent) {
return false;
}
final ViewFrameInfo info = attachInfo.mViewRootImpl.mViewFrameInfo;
final long durationFromVsyncTimeMs = (System.nanoTime()
- Choreographer.getInstance().getLastFrameTimeNanos()) / TimeUtils.NANOS_PER_MS;
return durationFromVsyncTimeMs > 3L || info.getAndIncreaseViewMeasuredCount() > 10;
}
/**
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isInScrollingContainer() {
ViewParent p = getParent();
while (p != null && p instanceof ViewGroup) {
if (((ViewGroup) p).shouldDelayChildPressedState()) {
return true;
}
p = p.getParent();
}
return false;
}
/**
* Remove the longpress detection timer.
*/
private void removeLongPressCallback() {
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
}
/**
* Return true if the long press callback is scheduled to run sometime in the future.
* Return false if there is no scheduled long press callback at the moment.
*/
private boolean hasPendingLongPressCallback() {
if (mPendingCheckForLongPress == null) {
return false;
}
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo == null) {
return false;
}
return attachInfo.mHandler.hasCallbacks(mPendingCheckForLongPress);
}
/**
* Remove the pending click action
*/
@UnsupportedAppUsage
private void removePerformClickCallback() {
if (mPerformClick != null) {
removeCallbacks(mPerformClick);
}
}
/**
* Remove the prepress detection timer.
*/
private void removeUnsetPressCallback() {
if ((mPrivateFlags & PFLAG_PRESSED) != 0 && mUnsetPressedState != null) {
setPressed(false);
removeCallbacks(mUnsetPressedState);
}
}
/**
* Remove the tap detection timer.
*/
private void removeTapCallback() {
if (mPendingCheckForTap != null) {
mPrivateFlags &= ~PFLAG_PREPRESSED;
removeCallbacks(mPendingCheckForTap);
}
}
/**
* Cancels a pending long press. Your subclass can use this if you
* want the context menu to come up if the user presses and holds
* at the same place, but you don't want it to come up if they press
* and then move around enough to cause scrolling.
*/
public void cancelLongPress() {
removeLongPressCallback();
/*
* The prepressed state handled by the tap callback is a display
* construct, but the tap callback will post a long press callback
* less its own timeout. Remove it here.
*/
removeTapCallback();
}
/**
* Sets the TouchDelegate for this View.
*/
public void setTouchDelegate(TouchDelegate delegate) {
mTouchDelegate = delegate;
}
/**
* Gets the TouchDelegate for this View.
*/
public TouchDelegate getTouchDelegate() {
return mTouchDelegate;
}
/**
* Request unbuffered dispatch of the given stream of MotionEvents to this View.
*
* Until this View receives a corresponding {@link MotionEvent#ACTION_UP}, ask that the input
* system not batch {@link MotionEvent}s but instead deliver them as soon as they're
* available. This method should only be called for touch events.
*
*
* float scale = context.getResources().getDisplayMetrics().density;
* view.setCameraDistance(distance * scale);
*
*
* false
if appropriate, or setting a
* {@link #setLayerType(int, android.graphics.Paint) layer type} on the view for the duration
* of the animation. On versions {@link android.os.Build.VERSION_CODES#M} and below,
* the default path for rendering an unlayered View with alpha could add multiple milliseconds
* of rendering cost, even for simple or small views. Starting with
* {@link android.os.Build.VERSION_CODES#M}, {@link #LAYER_TYPE_HARDWARE} is automatically
* applied to the view at the rendering level.false
from
* {@link #hasOverlappingRendering}.null
as the matrix parameter.
* Application developers should use transformation methods like {@link #setRotation(float)},
* {@link #setScaleX(float)}, {@link #setScaleX(float)}, {@link #setTranslationX(float)}}
* and {@link #setTranslationY(float)} (float)}} instead.
*
* @param matrix The matrix, null indicates that the matrix should be cleared.
* @see #getAnimationMatrix()
*/
public void setAnimationMatrix(@Nullable Matrix matrix) {
invalidateViewProperty(true, false);
mRenderNode.setAnimationMatrix(matrix);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
}
/**
* Return the current transformation matrix of the view. This is used in animation frameworks,
* such as {@link android.transition.Transition}. Returns null
when there is no
* transformation provided by {@link #setAnimationMatrix(Matrix)}.
* Application developers should use transformation methods like {@link #setRotation(float)},
* {@link #setScaleX(float)}, {@link #setScaleX(float)}, {@link #setTranslationX(float)}}
* and {@link #setTranslationY(float)} (float)}} instead.
*
* @return the Matrix, null indicates there is no transformation
* @see #setAnimationMatrix(Matrix)
*/
@Nullable
public Matrix getAnimationMatrix() {
return mRenderNode.getAnimationMatrix();
}
/**
* Returns the current StateListAnimator if exists.
*
* @return StateListAnimator or null if it does not exists
* @see #setStateListAnimator(android.animation.StateListAnimator)
*/
@InspectableProperty
public StateListAnimator getStateListAnimator() {
return mStateListAnimator;
}
/**
* Attaches the provided StateListAnimator to this View.
* super.onCancelPendingInputEvents()
and remove those callbacks as appropriate.
*
*
*
*
*
*
* super.destroyHardwareResources()
when overriding
* this method.
*
* @hide
*/
@CallSuper
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void destroyHardwareResources() {
if (mOverlay != null) {
mOverlay.getOverlayView().destroyHardwareResources();
}
if (mGhostView != null) {
mGhostView.destroyHardwareResources();
}
}
/**
* true
and calling
* {@link #getDrawingCache()}.getDrawingCache(false)
.buildDrawingCache(false)
.
*
* @return {@code true} if a default focus highlight is needed.
* @hide
*/
@TestApi
public boolean isDefaultFocusHighlightNeeded(Drawable background, Drawable foreground) {
final boolean lackFocusState = (background == null || !background.isStateful()
|| !background.hasFocusStateSpecified())
&& (foreground == null || !foreground.isStateful()
|| !foreground.hasFocusStateSpecified());
return !isInTouchMode() && getDefaultFocusHighlightEnabled() && lackFocusState
&& isAttachedToWindow() && sUseDefaultFocusHighlight;
}
/**
* When this view is focused, switches on/off the default focused highlight.
*