package android.view; import android.R; import android.animation.StateListAnimator; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.GskCanvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Parcelable; import android.os.SystemClock; import android.os.Vibrator; import android.util.AttributeSet; import android.util.LayoutDirection; import android.util.Property; import android.util.Slog; import android.util.SparseArray; import android.view.animation.Animation; import java.lang.CharSequence; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; class WindowId {} public class View implements Drawable.Callback { // --- constants from android source /** * The logging tag used by this class with android.util.Log. */ protected static final String TAG = "View"; /** * When set to true, apps will draw debugging information about their layouts. * * @hide */ public static final String DEBUG_LAYOUT_PROPERTY = "debug.layout"; /** * Used to mark a View that has no ID. */ public static final int NO_ID = -1; /** * Signals that compatibility booleans have been initialized according to * target SDK versions. */ private static boolean sCompatibilityDone = false; /** * Use the old (broken) way of building MeasureSpecs. */ private static boolean sUseBrokenMakeMeasureSpec = false; /** * Ignore any optimizations using the measure cache. */ private static boolean sIgnoreMeasureCache = false; /** * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when * calling setFlags. */ private static final int NOT_FOCUSABLE = 0x00000000; /** * This view wants keystrokes. Use with TAKES_FOCUS_MASK when calling * setFlags. */ private static final int FOCUSABLE = 0x00000001; /** * Mask for use with setFlags indicating bits used for focus. */ private static final int FOCUSABLE_MASK = 0x00000001; /** * This view will adjust its padding to fit sytem windows (e.g. status bar) */ private static final int FITS_SYSTEM_WINDOWS = 0x00000002; /** * 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}; /** * 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.

* {@hide} */ static final int SCROLLBARS_NONE = 0x00000000; /** *

This view shows horizontal scrollbars.

* {@hide} */ static final int SCROLLBARS_HORIZONTAL = 0x00000100; /** *

This view shows vertical scrollbars.

* {@hide} */ static final int SCROLLBARS_VERTICAL = 0x00000200; /** *

Mask for use with setFlags indicating bits used for indicating which * scrollbars are enabled.

* {@hide} */ static final int SCROLLBARS_MASK = 0x00000300; /** * Indicates that the view should filter touches when its window is obscured. * Refer to the class comments for more information about this security feature. * {@hide} */ static final int FILTER_TOUCHES_WHEN_OBSCURED = 0x00000400; /** * Set for framework elements that use FITS_SYSTEM_WINDOWS, to indicate * that they are optional and should be skipped if the window has * requested system UI flags that ignore those insets for layout. */ static final int OPTIONAL_FITS_SYSTEM_WINDOWS = 0x00000800; /** *

This view doesn't show fading edges.

* {@hide} */ static final int FADING_EDGE_NONE = 0x00000000; /** *

This view shows horizontal fading edges.

* {@hide} */ static final int FADING_EDGE_HORIZONTAL = 0x00001000; /** *

This view shows vertical fading edges.

* {@hide} */ static final int FADING_EDGE_VERTICAL = 0x00002000; /** *

Mask for use with setFlags indicating bits used for indicating which * fading edges are enabled.

* {@hide} */ static final int FADING_EDGE_MASK = 0x00003000; /** *

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.

* {@hide} */ static final int DRAWING_CACHE_ENABLED = 0x00008000; /** *

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.

* {@hide} */ static final int SAVE_DISABLED_MASK = 0x000010000; /** *

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.

* {@hide} */ static final int FOCUSABLE_IN_TOUCH_MODE = 0x00040000; /** *

Enables low quality mode for the drawing cache.

*/ public static final int DRAWING_CACHE_QUALITY_LOW = 0x00080000; /** *

Enables high quality mode for the drawing cache.

*/ public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000; /** *

Enables automatic quality mode for the drawing cache.

*/ public static final int DRAWING_CACHE_QUALITY_AUTO = 0x00000000; private static final int[] DRAWING_CACHE_QUALITY_FLAGS = { DRAWING_CACHE_QUALITY_AUTO, DRAWING_CACHE_QUALITY_LOW, DRAWING_CACHE_QUALITY_HIGH}; /** *

Mask for use with setFlags indicating bits used for the cache * quality property.

* {@hide} */ static final int DRAWING_CACHE_QUALITY_MASK = 0x00180000; /** *

* 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. *

* {@hide} */ static final int LONG_CLICKABLE = 0x00200000; /** *

Indicates that this view gets its drawable states from its direct parent * and ignores its original internal states.

* * @hide */ static final int DUPLICATE_PARENT_STATE = 0x00400000; /** * The scrollbar style to display the scrollbars inside the content area, * without increasing the padding. The scrollbars will be overlaid with * translucency on the view's content. */ public static final int SCROLLBARS_INSIDE_OVERLAY = 0; /** * The scrollbar style to display the scrollbars inside the padded area, * increasing the padding of the view. The scrollbars will not overlap the * content area of the view. */ public static final int SCROLLBARS_INSIDE_INSET = 0x01000000; /** * The scrollbar style to display the scrollbars at the edge of the view, * without increasing the padding. The scrollbars will be overlaid with * translucency. */ public static final int SCROLLBARS_OUTSIDE_OVERLAY = 0x02000000; /** * The scrollbar style to display the scrollbars at the edge of the view, * increasing the padding of the view. The scrollbars will only overlap the * background, if any. */ public static final int SCROLLBARS_OUTSIDE_INSET = 0x03000000; /** * Mask to check if the scrollbar style is overlay or inset. * {@hide} */ static final int SCROLLBARS_INSET_MASK = 0x01000000; /** * Mask to check if the scrollbar style is inside or outside. * {@hide} */ static final int SCROLLBARS_OUTSIDE_MASK = 0x02000000; /** * Mask for scrollbar style. * {@hide} */ static final int SCROLLBARS_STYLE_MASK = 0x03000000; /** * View flag indicating that the screen should remain on while the * window containing this view is visible to the user. This effectively * takes care of automatically setting the WindowManager's * {@link WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON}. */ public static final int KEEP_SCREEN_ON = 0x04000000; /** * View flag indicating whether this view should have sound effects enabled * for events such as clicking and touching. */ public static final int SOUND_EFFECTS_ENABLED = 0x08000000; /** * View flag indicating whether this view should have haptic feedback * enabled for events such as long presses. */ public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000; /** *

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.

* {@hide} */ static final int PARENT_SAVE_DISABLED_MASK = 0x20000000; /** * 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; /** * 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; // --- and some flags /** * Indicates that this view has reported that it can accept the current drag's content. * Cleared when the drag operation concludes. * @hide */ static final int PFLAG2_DRAG_CAN_ACCEPT = 0x00000001; /** * Indicates that this view is currently directly under the drag location in a * drag-and-drop operation involving content that it can accept. Cleared when * the drag exits the view, or when the drag operation concludes. * @hide */ static final int PFLAG2_DRAG_HOVERED = 0x00000002; /** * Horizontal layout direction of this view is from Left to Right. * Use with {@link #setLayoutDirection}. */ public static final int LAYOUT_DIRECTION_LTR = LayoutDirection.LTR; /** * Horizontal layout direction of this view is from Right to Left. * Use with {@link #setLayoutDirection}. */ public static final int LAYOUT_DIRECTION_RTL = LayoutDirection.RTL; /** * Horizontal layout direction of this view is inherited from its parent. * Use with {@link #setLayoutDirection}. */ public static final int LAYOUT_DIRECTION_INHERIT = LayoutDirection.INHERIT; /** * Horizontal layout direction of this view is from deduced from the default language * script for the locale. Use with {@link #setLayoutDirection}. */ public static final int LAYOUT_DIRECTION_LOCALE = LayoutDirection.LOCALE; /** * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED) * @hide */ static final int PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT = 2; /** * Mask for use with private flags indicating bits used for horizontal layout direction. * @hide */ static final int PFLAG2_LAYOUT_DIRECTION_MASK = 0x00000003 << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; /** * Indicates whether the view horizontal layout direction has been resolved and drawn to the * right-to-left direction. * @hide */ static final int PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL = 4 << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; /** * Indicates whether the view horizontal layout direction has been resolved. * @hide */ static final int PFLAG2_LAYOUT_DIRECTION_RESOLVED = 8 << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; /** * Mask for use with private flags indicating bits used for resolved horizontal layout direction. * @hide */ static final int PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK = 0x0000000C << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; // --- apparently there's more... @Deprecated public static final int STATUS_BAR_HIDDEN = 1; // 0x1 @Deprecated public static final int STATUS_BAR_VISIBLE = 0; // 0x0 public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4 public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2 public static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048; // 0x800 public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096; // 0x1000 public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400 public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200 public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100 public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1 public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0 public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600 public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4 public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1 public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0 public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3 public static final int TEXT_ALIGNMENT_TEXT_START = 2; // 0x2 public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6 public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5 public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2 public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1 public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0 public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5 public static final int TEXT_DIRECTION_LTR = 3; // 0x3 public static final int TEXT_DIRECTION_RTL = 4; // 0x4 // --- end of constants from android source // --- interfaces from android source public interface OnTouchListener { /** * Called when a touch event is dispatched to a view. This allows listeners to * get a chance to respond before the target view. * * @param v The view the touch event has been dispatched to. * @param event The MotionEvent object containing full information about * the event. * @return True if the listener has consumed the event, false otherwise. */ boolean onTouch(View v, MotionEvent event); } public interface OnClickListener { void onClick(View v); } public interface OnAttachStateChangeListener { // TODO } public interface OnGenericMotionListener { // TODO } public interface OnSystemUiVisibilityChangeListener { // TODO } public static interface OnKeyListener { // TODO // boolean onKey(View v, int keyCode, KeyEvent event); } public interface OnLongClickListener { public boolean onLongClick(View v); } public interface OnHoverListener { // TODO } public interface OnApplyWindowInsetsListener { // TODO } public interface OnLayoutChangeListener { // TODO } public interface OnUnhandledKeyEventListener { // TODO } public interface OnFocusChangeListener { // TODO } public interface OnCreateContextMenuListener { /** * Called when the context menu for this view is being built. It is not * safe to hold onto the menu after this method returns. * * @param menu The context menu that is being built * @param v The view for which the context menu is being built * @param menuInfo Extra information about the item for which the * context menu should be shown. This information will vary * depending on the class of v. */ // void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo); } public interface OnDragListener {} // --- end of interfaces // --- subclasses /** * A MeasureSpec encapsulates the layout requirements passed from parent to child. * Each MeasureSpec represents a requirement for either the width or the height. * A MeasureSpec is comprised of a size and a mode. There are three possible * modes: *
*
UNSPECIFIED
*
* The parent has not imposed any constraint on the child. It can be whatever size * it wants. *
* *
EXACTLY
*
* The parent has determined an exact size for the child. The child is going to be * given those bounds regardless of how big it wants to be. *
* *
AT_MOST
*
* The child can be as large as it wants up to the specified size. *
*
* * MeasureSpecs are implemented as ints to reduce object allocation. This class * is provided to pack and unpack the <size, mode> tuple into the int. */ public static class MeasureSpec { private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; /** * Measure specification mode: The parent has not imposed any constraint * on the child. It can be whatever size it wants. */ public static final int UNSPECIFIED = 0 << MODE_SHIFT; /** * Measure specification mode: The parent has determined an exact size * for the child. The child is going to be given those bounds regardless * of how big it wants to be. */ public static final int EXACTLY = 1 << MODE_SHIFT; /** * Measure specification mode: The child can be as large as it wants up * to the specified size. */ public static final int AT_MOST = 2 << MODE_SHIFT; /** * Creates a measure specification based on the supplied size and mode. * * The mode must always be one of the following: * * *

Note: On API level 17 and lower, makeMeasureSpec's * implementation was such that the order of arguments did not matter * and overflow in either value could impact the resulting MeasureSpec. * {@link android.widget.RelativeLayout} was affected by this bug. * Apps targeting API levels greater than 17 will get the fixed, more strict * behavior.

* * @param size the size of the measure specification * @param mode the mode of the measure specification * @return the measure specification based on size and mode */ public static int makeMeasureSpec(int size, int mode) { if (sUseBrokenMakeMeasureSpec) { return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); } } /** * Extracts the mode from the supplied measure specification. * * @param measureSpec the measure specification to extract the mode from * @return {@link android.view.View.MeasureSpec#UNSPECIFIED}, * {@link android.view.View.MeasureSpec#AT_MOST} or * {@link android.view.View.MeasureSpec#EXACTLY} */ public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); } /** * Extracts the size from the supplied measure specification. * * @param measureSpec the measure specification to extract the size from * @return the size in pixels defined in the supplied measure specification */ public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); } static int adjust(int measureSpec, int delta) { return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec)); } /** * Returns a String representation of the specified measure * specification. * * @param measureSpec the measure specification to convert to a String * @return a String with the following format: "MeasureSpec: MODE SIZE" */ public static String toString(int measureSpec) { int mode = getMode(measureSpec); int size = getSize(measureSpec); StringBuilder sb = new StringBuilder("MeasureSpec: "); if (mode == UNSPECIFIED) sb.append("UNSPECIFIED "); else if (mode == EXACTLY) sb.append("EXACTLY "); else if (mode == AT_MOST) sb.append("AT_MOST "); else sb.append(mode).append(" "); sb.append(size); return sb.toString(); } } // --- end of subclasses public int id = NO_ID; private int system_ui_visibility = 0; public ViewGroup parent; public AttributeSet attrs; protected ViewGroup.LayoutParams layout_params; private Context context; private Map tags = new HashMap<>(); private Object tag; int gravity = -1; // fallback gravity for layout childs int measuredWidth = 0; int measuredHeight = 0; private int left; private int top; private int right; private int bottom; private int scrollX = 0; private int scrollY = 0; private boolean attachedToWindow = false; public long widget; // pointer private int oldWidthMeasureSpec = -1; private int oldHeightMeasureSpec = -1; private boolean layoutRequested = true; private int oldWidth; private int oldHeight; protected boolean haveCustomMeasure = true; private int visibility = View.VISIBLE; private float alpha = 1.0f; private boolean pressed = false; private Drawable background; private int minWidth = 0; private int minHeight = 0; public static final Property TRANSLATION_X = new Property(Float.class, "translationX") { @Override public Float get(View object) { return 0.f; } }; public static final Property TRANSLATION_Y = new Property(Float.class, "translationY") { @Override public Float get(View object) { return 0.f; } }; public static final Property TRANSLATION_Z = new Property(Float.class, "translationZ") { @Override public Float get(View object) { return 0.f; } }; public static final Property ALPHA = new Property(Float.class, "alpha") { @Override public Float get(View object) { return 0.f; } }; public View(Context context, AttributeSet attrs) { this(context, attrs, 0); } public View(Context context, AttributeSet attrs, int defStyle) { this.context = context; widget = native_constructor(context, attrs); if (attrs != null) { TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, defStyle, 0); this.id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); if (a.hasValue(com.android.internal.R.styleable.View_background)) { try { Drawable background = a.getDrawable(com.android.internal.R.styleable.View_background); if(background != null) { if(background instanceof ColorDrawable) { setBackgroundColor(((ColorDrawable)background).getColor()); } else { setBackgroundDrawable(background); } } } catch (Exception e) { e.printStackTrace(); } } if (a.hasValue(com.android.internal.R.styleable.View_visibility)) { setVisibility(VISIBILITY_FLAGS[a.getInt(com.android.internal.R.styleable.View_visibility, 0)]); } if (a.hasValue(com.android.internal.R.styleable.View_minWidth)) { minWidth = a.getDimensionPixelSize(com.android.internal.R.styleable.View_minWidth, 0); } if (a.hasValue(com.android.internal.R.styleable.View_minHeight)) { minHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.View_minHeight, 0); } } onCreateDrawableState(0); } protected int[] onCreateDrawableState(int extraSpace) { return new int[0]; } protected static int[] mergeDrawableStates(int[] curState, int[] newState) { return new int[0]; } public View findViewById(int id) { if (this.id == id) return this; else return null; } public void onDraw(Canvas canvas) { if (canvas instanceof GskCanvas) native_drawContent(widget, ((GskCanvas)canvas).snapshot); } public void draw(Canvas canvas) { if (canvas instanceof GskCanvas) native_drawBackground(widget, ((GskCanvas)canvas).snapshot); onDraw(canvas); if (canvas instanceof GskCanvas) native_drawChildren(widget, ((GskCanvas)canvas).snapshot); } public View(Context context) { this(context, null); } public final ViewParent getParent() { return parent; } public void setLayoutParams(ViewGroup.LayoutParams params) { if (params == null) { throw new NullPointerException("Layout parameters cannot be null"); } int gravity = params.gravity; if (gravity == -1 && parent != null) gravity = parent.gravity; int leftMargin = 0; int topMargin = 0; int rightMargin = 0; int bottomMargin = 0; if (params instanceof ViewGroup.MarginLayoutParams) { leftMargin = ((ViewGroup.MarginLayoutParams) params).leftMargin; topMargin = ((ViewGroup.MarginLayoutParams) params).topMargin; rightMargin = ((ViewGroup.MarginLayoutParams) params).rightMargin; bottomMargin = ((ViewGroup.MarginLayoutParams) params).bottomMargin; } native_setLayoutParams(widget, params.width, params.height, gravity, params.weight, leftMargin, topMargin, rightMargin, bottomMargin); layout_params = params; } public ViewGroup.LayoutParams getLayoutParams() { return layout_params; } protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { this.measuredWidth = measuredWidth; this.measuredHeight = measuredHeight; } public Resources getResources() { return Context.this_application.getResources(); } public void setGravity(int gravity) { this.gravity = gravity; } private OnTouchListener on_touch_listener = null; public boolean onTouchEvent(MotionEvent event) { if (on_touch_listener != null) return on_touch_listener.onTouch(this, event); else return false; } public void setOnTouchListener(OnTouchListener l) { nativeSetOnTouchListener(widget); on_touch_listener = l; } protected native void nativeSetOnTouchListener(long widget); private OnClickListener on_click_listener = null; public void setOnClickListener(OnClickListener l) { nativeSetOnClickListener(widget); on_click_listener = l; } protected native void nativeSetOnClickListener(long widget); public /*native*/ void setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener l) {} public native final int getWidth(); public native final int getHeight(); protected native long native_constructor(Context context, AttributeSet attrs); // will create a custom GtkWidget with a custom drawing function public native void native_setLayoutParams(long widget, int width, int height, int gravity, float weight, int leftMargin, int topMargin, int rightMargin, int bottomMargin); protected native void native_destructor(long widget); /** * We decide between simple widgets which handles MEASURE_SPEC_AT_MOST the same way as * MEASURE_SPEC_EXACTLY, and complex widgets which handles MEASURE_SPEC_AT_MOST by measuring the content */ protected native void native_measure(long widget, int widthMeasureSpec, int heightMeasureSpec); protected native void native_layout(long widget, int l, int t, int r, int b); protected native void native_requestLayout(long widget); protected native void native_setBackgroundDrawable(long widget, long paintable); protected native void native_queueAllocate(long widget); protected native void native_drawBackground(long widget, long snapshot); protected native void native_drawContent(long widget, long snapshot); protected void native_drawChildren(long widget, long snapshot) {} // override in ViewGroup // --- stubs public void setContentDescription(CharSequence contentDescription) {} public void setId(int id) { this.id = id; } public void setOnKeyListener(OnKeyListener l) {} public void setFocusable(boolean focusable) {} public void setFocusableInTouchMode(boolean focusableInTouchMode) {} public final boolean requestFocus() { return requestFocus(View.FOCUS_DOWN); } public final boolean requestFocus(int direction) { return requestFocus(direction, null); } public boolean requestFocus(int direction, Rect previouslyFocusedRect) { return true; } private native void nativeSetFullscreen(long widget, boolean fullscreen); public void setSystemUiVisibility(int visibility) { nativeSetFullscreen(widget, (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0); system_ui_visibility = visibility; } public int getSystemUiVisibility() { return system_ui_visibility; }; protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (haveCustomMeasure) // calling native_measure here would create infinite loop setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); else native_measure(widget, widthMeasureSpec, heightMeasureSpec); } public void setPressed(boolean pressed) { Slog.w(TAG, "calling setPressed on " + this + " with value: " + pressed); if (this.pressed != pressed) { this.pressed = pressed; if (background != null && background.isStateful()) background.setState(getDrawableState()); } } public void setSelected(boolean selected) {} public ViewTreeObserver getViewTreeObserver() { return new ViewTreeObserver(); } protected void onFinishInflate() {} public void invalidateDrawable(Drawable drawable) { nativeInvalidate(widget); } public void scheduleDrawable(Drawable drawable, Runnable runnable, long time) { postDelayed(runnable, time - SystemClock.uptimeMillis()); } public void unscheduleDrawable(Drawable drawable, Runnable runnable) { /* TODO */ } public void invalidate(Rect dirty) { nativeInvalidate(widget); } public void invalidate(int l, int t, int r, int b) { nativeInvalidate(widget); } public void invalidate() { nativeInvalidate(widget); } private static native void nativeInvalidate(long widget); public native void setBackgroundColor(int color); public native void native_setVisibility(long widget, int visibility, float alpha); public void setVisibility(int visibility) { native_setVisibility(widget, visibility, alpha); if ((visibility == View.GONE) != (this.visibility == View.GONE)) { requestLayout(); } this.visibility = visibility; } public void setPadding(int left, int top, int right, int bottom) {} public void setBackgroundResource(int resid) { setBackgroundDrawable(resid == 0 ? null : getResources().getDrawable(resid)); } public void getHitRect(Rect outRect) { outRect.set(left, top, right, bottom); } public final boolean getLocalVisibleRect(Rect r) { return false; } public final int getScrollX() { return scrollX; } public final int getScrollY() { return scrollY; } public void scrollTo(int x, int y) { scrollX = x; scrollY = y; invalidate(); } protected void onScrollChanged(int l, int t, int oldl, int oldt) {} public void getLocationOnScreen(int[] location) { Rect rect = new Rect(); getGlobalVisibleRect(rect); location[0] = rect.left; location[1] = rect.top; } public boolean performHapticFeedback(int feedbackConstant) { return performHapticFeedback(feedbackConstant, 0); } public boolean performHapticFeedback(int feedbackConstant, int flags) { // arbitrary; TODO: better mimic what AOSP does Vibrator vibrator = (Vibrator)getContext().getSystemService("vibrator"); vibrator.vibrate(10); return true; // FIXME why is it not void } public int getPaddingLeft() { return 0; } public int getPaddingRight() { return 0; } public int getPaddingTop() { return 0; } public int getPaddingBottom() { return 0; } public int getPaddingStart() { return 0; } public int getPaddingEnd() { return 0; } public void postInvalidate() { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { invalidate(); } }); } public void postInvalidate(final int left, final int top, final int right, final int bottom) { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { invalidate(left, top, right, bottom); } }); } public void setOnGenericMotionListener(View.OnGenericMotionListener l) {} public IBinder getWindowToken() { return new IBinder(); } public void getLocationInWindow(int[] location) { getLocationOnScreen(location); } public void addOnAttachStateChangeListener(OnAttachStateChangeListener l) {} public Context getContext() { return this.context; } public void refreshDrawableState() { } public void setDescendantFocusability(int value) {} public void setAccessibilityDelegate(AccessibilityDelegate delegate) {} public static class AccessibilityDelegate { public void sendAccessibilityEvent(View view, int eventType) {} } public Drawable getBackground() { return background; } public void setClickable(boolean clickable) {} public void setEnabled(boolean enabled) {} public CharSequence getContentDescription() { return null; } private OnLongClickListener on_long_click_listener = null; public boolean performLongClick(float x, float y) { if (on_long_click_listener != null) { return on_long_click_listener.onLongClick(this); } return false; } public void setOnLongClickListener(OnLongClickListener listener) { nativeSetOnLongClickListener(widget); on_long_click_listener = listener; } protected native void nativeSetOnLongClickListener(long widget); public void setLongClickable(boolean longClickable) {} public void setOnHoverListener(OnHoverListener listener) {} public final void measure(int widthMeasureSpec, int heightMeasureSpec) { if (layoutRequested || widthMeasureSpec != oldWidthMeasureSpec || heightMeasureSpec != oldHeightMeasureSpec) { oldWidthMeasureSpec = widthMeasureSpec; oldHeightMeasureSpec = heightMeasureSpec; onMeasure(widthMeasureSpec, heightMeasureSpec); layoutRequested = false; } } public final int getMeasuredState() { return 0; } public static int combineMeasuredStates(int curState, int newState) { return curState | newState; } protected int getSuggestedMinimumHeight() { return getMinimumHeight(); } protected int getSuggestedMinimumWidth() { return getMinimumWidth(); } /** * Utility to reconcile a desired size and state, with constraints imposed * by a MeasureSpec. Will take the desired size, unless a different size * is imposed by the constraints. The returned value is a compound integer, * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting * size is smaller than the size the view wants to be. * * @param size How big the view wants to be * @param measureSpec Constraints imposed by the parent * @return Size information bit mask as defined by * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}. */ public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: if (specSize < size) { result = specSize | MEASURED_STATE_TOO_SMALL; } else { result = size; } break; case MeasureSpec.EXACTLY: result = specSize; break; } return result | (childMeasuredState&MEASURED_STATE_MASK); } public static int resolveSize(int size, int measureSpec) { return resolveSizeAndState(size, measureSpec, 0) & MEASURED_SIZE_MASK; } public final int getMeasuredWidth() { return this.measuredWidth & MEASURED_SIZE_MASK; } public final int getMeasuredHeight() { return this.measuredHeight & MEASURED_SIZE_MASK; } protected void onLayout(boolean changed, int l, int t, int r, int b) {} public void layout(int l, int t, int r, int b) { this.left = l; this.top = t; this.right = r; this.bottom = b; native_layout(widget, l, t, r, b); } /** Helper function to be called from GTKs LayoutManager via JNI */ private void layoutInternal(int width, int height) { // if the layout is triggered from a native widget, we might not have measured yet if (width != getMeasuredWidth() || height != getMeasuredHeight()) { measure(width | MeasureSpec.EXACTLY, height | MeasureSpec.EXACTLY); } boolean changed = oldWidth != width || oldHeight != height; if (changed) onSizeChanged(width, height, oldWidth, oldHeight); onLayout(changed, 0, 0, width, height); oldWidth = width; oldHeight = height; } public int getLeft() { return left; } public int getTop() { return top; } public int getRight() { return right; } public int getBottom() { return bottom; } public void offsetTopAndBottom(int offset) { layout(left, top + offset, right, bottom + offset); } public void offsetLeftAndRight(int offset) { layout(left + offset, top, right + offset, bottom); } public void setBackgroundDrawable(Drawable backgroundDrawable) { this.background = backgroundDrawable; if(backgroundDrawable != null) backgroundDrawable.setCallback(this); native_setBackgroundDrawable(widget, backgroundDrawable != null ? backgroundDrawable.paintable : 0); } public int getOverScrollMode() {return 0;} public void setFitsSystemWindows(boolean fitsSystemWindows) {} public void setWillNotDraw(boolean value) {} public void setScrollContainer(boolean isScrollContainer) {} public boolean removeCallbacks(Runnable action) {return false;} public void requestLayout() { layoutRequested = true; if (parent != null) { parent.requestLayout(); } native_requestLayout(widget); }; public void setOverScrollMode(int mode) {} public int getId() {return id;} public boolean postDelayed(Runnable action, long delayMillis) { new Handler(Looper.getMainLooper()).postDelayed(action, delayMillis); return true; } public boolean post(Runnable action) { new Handler(Looper.getMainLooper()).post(action); return true; } public void setSaveFromParentEnabled(boolean enabled) {} public void setTag(int key, Object tag) { tags.put(key, tag); } public Object getTag(int key) { return tags.get(key); } public void setTag(Object tag) { this.tag = tag; } public Object getTag() { return tag; } public void addOnLayoutChangeListener(OnLayoutChangeListener listener) {} public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) {} public boolean isSelected() {return false;} public void sendAccessibilityEvent(int eventType) {} public void setMinimumHeight(int minHeight) { this.minHeight = minHeight; } public void setMinimumWidth(int minWidth) { this.minWidth = minWidth; } public void setActivated (boolean activated) {} public int getVisibility() {return visibility;} public boolean isInEditMode() {return false;} @Override @SuppressWarnings("deprecation") protected void finalize() throws Throwable { try { native_destructor(widget); } finally { super.finalize(); } } public final int[] getDrawableState() { int[] state = new int[2]; state[0] = R.attr.state_enabled; if (pressed) { state[1] = R.attr.state_pressed; } return state; } public float getRotation() {return 0.f;} public void bringToFront() {} public boolean isEnabled() {return true;} public boolean hasFocus() {return false;} public boolean isLayoutRequested() {return layoutRequested;} public int getBaseline() {return -1;} public boolean hasFocusable() {return false;} public boolean isFocused() {return false;} public void clearAnimation() {} private ViewPropertyAnimator viewPropertyAnimator; public ViewPropertyAnimator animate() { if (viewPropertyAnimator == null) viewPropertyAnimator = new ViewPropertyAnimator(this); return viewPropertyAnimator; } public float getTranslationX() {return 0.f;} public float getTranslationY() {return 0.f;} public void setTranslationX(float translationX) {} public void setTranslationY(float translationY) { // CoordinatorLayout abuses this method to trigger a layout pass if (getClass().getName().equals("androidx.coordinatorlayout.widget.CoordinatorLayout")) native_queueAllocate(widget); } public void setAlpha(float alpha) { native_setVisibility(widget, visibility, alpha); this.alpha = alpha; } public boolean onGenericMotionEvent(MotionEvent event) {return false;} protected boolean awakenScrollBars() {return false;} protected native boolean native_getMatrix(long widget, long matrix); public Matrix getMatrix() { return Matrix.IDENTITY_MATRIX; } protected static final int[] EMPTY_STATE_SET = new int[0]; protected static final int[] PRESSED_ENABLED_STATE_SET = new int[]{R.attr.state_pressed, R.attr.state_enabled}; protected static final int[] SELECTED_STATE_SET = new int[]{R.attr.state_selected}; /** * Utility to return a default size. Uses the supplied size if the * MeasureSpec imposed no constraints. Will get larger if allowed * by the MeasureSpec. * * @param size Default size for this view * @param measureSpec Constraints imposed by the parent * @return The size this view should be. */ public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result; } public static class BaseSavedState extends AbsSavedState { } public void clearFocus() {} public static View inflate(Context context, int resource, ViewGroup root) { LayoutInflater factory = LayoutInflater.from(context); return factory.inflate(resource, root); } public void saveHierarchyState(SparseArray array) {} public void setDuplicateParentStateEnabled(boolean enabled) {} public boolean performClick() { if (on_click_listener != null) { on_click_listener.onClick(this); return true; } else { return false; } } public void playSoundEffect(int soundConstant) {} public void computeScroll() {} public void jumpDrawablesToCurrentState() {} public void setOnFocusChangeListener (View.OnFocusChangeListener l) {} public boolean hasWindowFocus() {return true;} public void setSaveEnabled (boolean enabled) {} public boolean willNotDraw() {return false;} public void setOnCreateContextMenuListener (View.OnCreateContextMenuListener l) {} public void startAnimation(Animation animation) {} public void getDrawingRect(Rect rect) { rect.left = getScrollX(); rect.top = getScrollY(); rect.right = getScrollX() + getWidth(); rect.bottom = getScrollY() + getHeight(); } public Display getDisplay() { return new Display(); } private int importantForAccessibility; public void setImportantForAccessibility(int importantForAccessibility) { this.importantForAccessibility = importantForAccessibility; } public int getImportantForAccessibility() { return importantForAccessibility; } public boolean getFitsSystemWindows() {return true;} public void setOnApplyWindowInsetsListener(View.OnApplyWindowInsetsListener l) {} public final boolean isFocusable() {return true;} public boolean isClickable() {return true;} public boolean isLongClickable() {return true;} public int getLayoutDirection() {return LAYOUT_DIRECTION_LTR;} public void setBackground(Drawable background) { setBackgroundDrawable(background); } private float elevation; public void setElevation(float elevation) { this.elevation = elevation; } public float getElevation() { return elevation; } public boolean isLaidOut() {return true;} public void postOnAnimation(Runnable action) { postDelayed(action, 1000 / 60); } public void postOnAnimationDelayed(Runnable action, long delayMillis) { if (delayMillis < 1000 / 60) delayMillis = 1000 / 60; postDelayed(action, delayMillis); } public void setHorizontalScrollBarEnabled(boolean enabled) {} public void postInvalidateOnAnimation() { postInvalidate(); } public void setPaddingRelative(int start, int top, int end, int bottom) {} public boolean isAttachedToWindow() {return attachedToWindow;} public void requestApplyInsets() {} public View getRootView() { View view = this; while (view.parent != null) { view = view.parent; } return view; } public boolean isShown() {return true;} public int getWindowVisibility() {return VISIBLE;} public float getAlpha() {return alpha;} public View findFocus() {return this;} public int getMinimumHeight() {return minHeight;} public int getMinimumWidth() {return minWidth;} public boolean isNestedScrollingEnabled() {return false;} public void setClipToOutline(boolean clipToOutline) {} public boolean hasTransientState() {return false;} public final void cancelPendingInputEvents() {} public ViewOutlineProvider getOutlineProvider() {return new ViewOutlineProvider();} public void setOutlineProvider(ViewOutlineProvider provider) {} public void setStateListAnimator(StateListAnimator stateListAnimator) {} private static final AtomicInteger nextGeneratedId = new AtomicInteger(1); /** * Generate a value suitable for use in {@link #setId(int)}. * This value will not collide with ID values generated at build time by aapt for R.id. * * @return a generated ID value */ public static int generateViewId() { for (;;) { final int result = nextGeneratedId.get(); // aapt-generated IDs have the high byte nonzero; clamp to the range under that. int newValue = result + 1; if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0. if (nextGeneratedId.compareAndSet(result, newValue)) { return result; } } } public boolean isLayoutDirectionResolved() {return true;} public boolean isPaddingRelative() {return false;} public void setForeground(Drawable foreground) {} public boolean canScrollVertically(int value) {return true;} public boolean isInTouchMode() {return false;} public void stopNestedScroll() {} public long getDrawingTime() { return System.currentTimeMillis(); } public void setKeepScreenOn(boolean screenOn) {} protected void onAttachedToWindow () { attachedToWindow = true; } protected void onDetachedFromWindow() { attachedToWindow = false; } public void setLayerType(int layerType, Paint paint) {} public float getZ() {return 0.f;} protected void onSizeChanged(int w, int h, int oldw, int oldh) {} public void setBackgroundTintList(ColorStateList tint) {} protected int computeHorizontalScrollRange() { return getWidth(); } protected int computeHorizontalScrollExtent() { return getWidth(); } protected int computeVerticalScrollRange() { return getHeight(); } protected int computeVerticalScrollExtent() { return getHeight(); } public void setAccessibilityLiveRegion(int mode) {} public void invalidateOutline() {} public int getMeasuredWidthAndState() { return measuredWidth; } public void forceLayout() { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { requestLayout(); } }); } public void removeOnAttachStateChangeListener(OnAttachStateChangeListener listener) {} public boolean onInterceptTouchEvent(MotionEvent event) {return false;} public boolean dispatchTouchEvent(MotionEvent event) {return false;} public boolean canScrollHorizontally(int direction) {return false;} protected native boolean native_getGlobalVisibleRect(long widget, Rect visibleRect); public boolean getGlobalVisibleRect(Rect visibleRect) { return native_getGlobalVisibleRect(widget, visibleRect); } public boolean onCheckIsTextEditor() {return false;} public boolean hasOnClickListeners() {return false;} public void setTextAlignment(int textAlignment) {} public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) {} public StateListAnimator getStateListAnimator() {return null;} public void requestFitSystemWindows() {} public boolean isPressed() {return false;} public void getWindowVisibleDisplayFrame(Rect rect) {} public void setRotation(float rotation) {} public void setRotationX(float deg) {} public void setRotationY(float deg) {} public float getRotationX() {return 0.f;} public float getRotationY() {return 0.f;} public void setScaleX(float scaleX) {} public void setScaleY(float scaleY) {} public float getScaleX() {return 1.f;} public float getScaleY() {return 1.f;} public void setPivotX(float pivot_x) {} public void setPivotY(float pivot_y) {} public float getPivotX() {return 0.f;} public float getPivotY() {return 0.f;} public float getTranslationZ() {return 0.f;} public void setTranslationZ(float translationZ) {} public int getWindowSystemUiVisibility() { return 0; } public void setScrollIndicators(int indicators, int mask) {} public void setLayoutDirection(int layoutDirection) {} public ColorStateList getBackgroundTintList() {return null;} public PorterDuff.Mode getBackgroundTintMode() {return null;} public String getTransitionName() {return null;} public WindowId getWindowId() {return null;} public boolean isInLayout() { return false; // FIXME } public void setTextDirection(int textDirection) {} public Drawable getForeground() {return null;} public void setScrollbarFadingEnabled(boolean fadeEnabled) {} public void setScrollBarStyle(int style) {} public void setVerticalScrollbarPosition(int position) {} public void setNestedScrollingEnabled(boolean enabled) {} public void setTouchDelegate(TouchDelegate touchDelegate) {} public void setOnDragListener(OnDragListener onDragListener) {} public void setTransitionName(String transitionName) {} public Animation getAnimation() {return null;} public ViewOverlay getOverlay() { return new ViewOverlay(); } public void cancelLongPress() {} public int getTextAlignment() {return 0;} public float getY() {return 0.f;} public View findViewWithTag(Object tag) { if (Objects.equals(tag, this.tag)) return this; else return null; } public void setTooltipText(CharSequence tooltip) {} public int getImportantForAutofill() {return 0;} public void setImportantForAutofill(int flag) {} }