diff --git a/AndroidCompat/src/main/java/android/os/Handler.java b/AndroidCompat/src/main/java/android/os/Handler.java new file mode 100644 index 00000000..5881e927 --- /dev/null +++ b/AndroidCompat/src/main/java/android/os/Handler.java @@ -0,0 +1,1043 @@ +/* + * 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.os; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.TestApi; +import android.util.Log; +import android.util.Printer; + +import java.lang.reflect.Modifier; + +/** + * A Handler allows you to send and process {@link Message} and Runnable + * objects associated with a thread's {@link MessageQueue}. Each Handler + * instance is associated with a single thread and that thread's message + * queue. When you create a new Handler it is bound to a {@link Looper}. + * It will deliver messages and runnables to that Looper's message + * queue and execute them on that Looper's thread. + * + *

There are two main uses for a Handler: (1) to schedule messages and + * runnables to be executed at some point in the future; and (2) to enqueue + * an action to be performed on a different thread than your own. + * + *

Scheduling messages is accomplished with the + * {@link #post}, {@link #postAtTime(Runnable, long)}, + * {@link #postDelayed}, {@link #sendEmptyMessage}, + * {@link #sendMessage}, {@link #sendMessageAtTime}, and + * {@link #sendMessageDelayed} methods. The post versions allow + * you to enqueue Runnable objects to be called by the message queue when + * they are received; the sendMessage versions allow you to enqueue + * a {@link Message} object containing a bundle of data that will be + * processed by the Handler's {@link #handleMessage} method (requiring that + * you implement a subclass of Handler). + * + *

When posting or sending to a Handler, you can either + * allow the item to be processed as soon as the message queue is ready + * to do so, or specify a delay before it gets processed or absolute time for + * it to be processed. The latter two allow you to implement timeouts, + * ticks, and other timing-based behavior. + * + *

When a + * process is created for your application, its main thread is dedicated to + * running a message queue that takes care of managing the top-level + * application objects (activities, broadcast receivers, etc) and any windows + * they create. You can create your own threads, and communicate back with + * the main application thread through a Handler. This is done by calling + * the same post or sendMessage methods as before, but from + * your new thread. The given Runnable or Message will then be scheduled + * in the Handler's message queue and processed when appropriate. + */ +public class Handler { + /* + * Set this flag to true to detect anonymous, local or member classes + * that extend this Handler class and that are not static. These kind + * of classes can potentially create leaks. + */ + private static final boolean FIND_POTENTIAL_LEAKS = false; + private static final String TAG = "Handler"; + private static Handler MAIN_THREAD_HANDLER = null; + + /** + * Callback interface you can use when instantiating a Handler to avoid + * having to implement your own subclass of Handler. + */ + public interface Callback { + /** + * @param msg A {@link android.os.Message Message} object + * @return True if no further handling is desired + */ + boolean handleMessage(@NonNull Message msg); + } + + /** + * Subclasses must implement this to receive messages. + */ + public void handleMessage(@NonNull Message msg) { + } + + /** + * Handle system messages here. + */ + public void dispatchMessage(@NonNull Message msg) { + if (msg.callback != null) { + handleCallback(msg); + } else { + if (mCallback != null) { + if (mCallback.handleMessage(msg)) { + return; + } + } + handleMessage(msg); + } + } + + /** + * Default constructor associates this handler with the {@link Looper} for the + * current thread. + * + * If this thread does not have a looper, this handler won't be able to receive messages + * so an exception is thrown. + * + * @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs + * where operations are silently lost (if the Handler is not expecting new tasks and quits), + * crashes (if a handler is sometimes created on a thread without a Looper active), or race + * conditions, where the thread a handler is associated with is not what the author + * anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper + * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or + * similar. If the implicit thread local behavior is required for compatibility, use + * {@code new Handler(Looper.myLooper())} to make it clear to readers. + * + */ + @Deprecated + public Handler() { + this(null, false); + } + + /** + * Constructor associates this handler with the {@link Looper} for the + * current thread and takes a callback interface in which you can handle + * messages. + * + * If this thread does not have a looper, this handler won't be able to receive messages + * so an exception is thrown. + * + * @param callback The callback interface in which to handle messages, or null. + * + * @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs + * where operations are silently lost (if the Handler is not expecting new tasks and quits), + * crashes (if a handler is sometimes created on a thread without a Looper active), or race + * conditions, where the thread a handler is associated with is not what the author + * anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper + * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or + * similar. If the implicit thread local behavior is required for compatibility, use + * {@code new Handler(Looper.myLooper(), callback)} to make it clear to readers. + */ + @Deprecated + public Handler(@Nullable Callback callback) { + this(callback, false); + } + + /** + * Use the provided {@link Looper} instead of the default one. + * + * @param looper The looper, must not be null. + */ + public Handler(@NonNull Looper looper) { + this(looper, null, false); + } + + /** + * Use the provided {@link Looper} instead of the default one and take a callback + * interface in which to handle messages. + * + * @param looper The looper, must not be null. + * @param callback The callback interface in which to handle messages, or null. + */ + public Handler(@NonNull Looper looper, @Nullable Callback callback) { + this(looper, callback, false); + } + + /** + * Use the {@link Looper} for the current thread + * and set whether the handler should be asynchronous. + * + * Handlers are synchronous by default unless this constructor is used to make + * one that is strictly asynchronous. + * + * Asynchronous messages represent interrupts or events that do not require global ordering + * with respect to synchronous messages. Asynchronous messages are not subject to + * the synchronization barriers introduced by {@link MessageQueue#postSyncBarrier()}. + * + * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for + * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. + * + * @hide + */ + public Handler(boolean async) { + this(null, async); + } + + /** + * Use the {@link Looper} for the current thread with the specified callback interface + * and set whether the handler should be asynchronous. + * + * Handlers are synchronous by default unless this constructor is used to make + * one that is strictly asynchronous. + * + * Asynchronous messages represent interrupts or events that do not require global ordering + * with respect to synchronous messages. Asynchronous messages are not subject to + * the synchronization barriers introduced by {@link MessageQueue#postSyncBarrier()}. + * + * @param callback The callback interface in which to handle messages, or null. + * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for + * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. + * + * @hide + */ + public Handler(@Nullable Callback callback, boolean async) { + if (FIND_POTENTIAL_LEAKS) { + final Class klass = getClass(); + if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && + (klass.getModifiers() & Modifier.STATIC) == 0) { + Log.w(TAG, "The following Handler class should be static or leaks might occur: " + + klass.getCanonicalName()); + } + } + + mLooper = Looper.myLooper(); + if (mLooper == null) { + throw new RuntimeException( + "Can't create handler inside thread " + Thread.currentThread() + + " that has not called Looper.prepare()"); + } + mQueue = mLooper.mQueue; + mCallback = callback; + mAsynchronous = async; + mIsShared = false; + } + + /** + * Use the provided {@link Looper} instead of the default one and take a callback + * interface in which to handle messages. Also set whether the handler + * should be asynchronous. + * + * Handlers are synchronous by default unless this constructor is used to make + * one that is strictly asynchronous. + * + * Asynchronous messages represent interrupts or events that do not require global ordering + * with respect to synchronous messages. Asynchronous messages are not subject to + * the synchronization barriers introduced by conditions such as display vsync. + * + * @param looper The looper, must not be null. + * @param callback The callback interface in which to handle messages, or null. + * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for + * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. + * + * @hide + */ + public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) { + this(looper, callback, async, /* shared= */ false); + } + + /** @hide */ + public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async, + boolean shared) { + mLooper = looper; + mQueue = looper.mQueue; + mCallback = callback; + mAsynchronous = async; + mIsShared = shared; + } + + /** + * Create a new Handler whose posted messages and runnables are not subject to + * synchronization barriers such as display vsync. + * + *

Messages sent to an async handler are guaranteed to be ordered with respect to one another, + * but not necessarily with respect to messages from other Handlers.

+ * + * @see #createAsync(Looper, Callback) to create an async Handler with custom message handling. + * + * @param looper the Looper that the new Handler should be bound to + * @return a new async Handler instance + */ + @NonNull + public static Handler createAsync(@NonNull Looper looper) { + if (looper == null) throw new NullPointerException("looper must not be null"); + return new Handler(looper, null, true); + } + + /** + * Create a new Handler whose posted messages and runnables are not subject to + * synchronization barriers such as display vsync. + * + *

Messages sent to an async handler are guaranteed to be ordered with respect to one another, + * but not necessarily with respect to messages from other Handlers.

+ * + * @see #createAsync(Looper) to create an async Handler without custom message handling. + * + * @param looper the Looper that the new Handler should be bound to + * @return a new async Handler instance + */ + @NonNull + public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) { + if (looper == null) throw new NullPointerException("looper must not be null"); + if (callback == null) throw new NullPointerException("callback must not be null"); + return new Handler(looper, callback, true); + } + + /** @hide */ + @NonNull + public static Handler getMain() { + if (MAIN_THREAD_HANDLER == null) { + MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper()); + } + return MAIN_THREAD_HANDLER; + } + + /** @hide */ + @NonNull + public static Handler mainIfNull(@Nullable Handler handler) { + return handler == null ? getMain() : handler; + } + + /** {@hide} */ + @NonNull + public String getTraceName(@NonNull Message message) { + if (message.callback instanceof TraceNameSupplier) { + return ((TraceNameSupplier) message.callback).getTraceName(); + } + + final StringBuilder sb = new StringBuilder(); + sb.append(getClass().getName()).append(": "); + if (message.callback != null) { + sb.append(message.callback.getClass().getName()); + } else { + sb.append("#").append(message.what); + } + return sb.toString(); + } + + /** + * Returns a string representing the name of the specified message. + * The default implementation will either return the class name of the + * message callback if any, or the hexadecimal representation of the + * message "what" field. + * + * @param message The message whose name is being queried + */ + @NonNull + public String getMessageName(@NonNull Message message) { + if (message.callback != null) { + return message.callback.getClass().getName(); + } + return "0x" + Integer.toHexString(message.what); + } + + /** + * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than + * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). + * If you don't want that facility, just call Message.obtain() instead. + */ + @NonNull + public final Message obtainMessage() + { + return Message.obtain(this); + } + + /** + * Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message. + * + * @param what Value to assign to the returned Message.what field. + * @return A Message from the global message pool. + */ + @NonNull + public final Message obtainMessage(int what) + { + return Message.obtain(this, what); + } + + /** + * + * Same as {@link #obtainMessage()}, except that it also sets the what and obj members + * of the returned Message. + * + * @param what Value to assign to the returned Message.what field. + * @param obj Value to assign to the returned Message.obj field. + * @return A Message from the global message pool. + */ + @NonNull + public final Message obtainMessage(int what, @Nullable Object obj) { + return Message.obtain(this, what, obj); + } + + /** + * + * Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned + * Message. + * @param what Value to assign to the returned Message.what field. + * @param arg1 Value to assign to the returned Message.arg1 field. + * @param arg2 Value to assign to the returned Message.arg2 field. + * @return A Message from the global message pool. + */ + @NonNull + public final Message obtainMessage(int what, int arg1, int arg2) + { + return Message.obtain(this, what, arg1, arg2); + } + + /** + * + * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the + * returned Message. + * @param what Value to assign to the returned Message.what field. + * @param arg1 Value to assign to the returned Message.arg1 field. + * @param arg2 Value to assign to the returned Message.arg2 field. + * @param obj Value to assign to the returned Message.obj field. + * @return A Message from the global message pool. + */ + @NonNull + public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) { + return Message.obtain(this, what, arg1, arg2, obj); + } + + /** + * Causes the Runnable r to be added to the message queue. + * The runnable will be run on the thread to which this handler is + * attached. + * + * @param r The Runnable that will be executed. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean post(@NonNull Runnable r) { + return sendMessageDelayed(getPostMessage(r), 0); + } + + /** + * Causes the Runnable r to be added to the message queue, to be run + * at a specific time given by uptimeMillis. + * The time-base is {@link android.os.SystemClock#uptimeMillis}. + * Time spent in deep sleep will add an additional delay to execution. + * The runnable will be run on the thread to which this handler is attached. + * + * @param r The Runnable that will be executed. + * @param uptimeMillis The absolute time at which the callback should run, + * using the {@link android.os.SystemClock#uptimeMillis} time-base. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the Runnable will be processed -- if + * the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) { + return sendMessageAtTime(getPostMessage(r), uptimeMillis); + } + + /** + * Causes the Runnable r to be added to the message queue, to be run + * at a specific time given by uptimeMillis. + * The time-base is {@link android.os.SystemClock#uptimeMillis}. + * Time spent in deep sleep will add an additional delay to execution. + * The runnable will be run on the thread to which this handler is attached. + * + * @param r The Runnable that will be executed. + * @param token An instance which can be used to cancel {@code r} via + * {@link #removeCallbacksAndMessages}. + * @param uptimeMillis The absolute time at which the callback should run, + * using the {@link android.os.SystemClock#uptimeMillis} time-base. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the Runnable will be processed -- if + * the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + * + * @see android.os.SystemClock#uptimeMillis + */ + public final boolean postAtTime( + @NonNull Runnable r, @Nullable Object token, long uptimeMillis) { + return sendMessageAtTime(getPostMessage(r, token), uptimeMillis); + } + + /** + * Causes the Runnable r to be added to the message queue, to be run + * after the specified amount of time elapses. + * The runnable will be run on the thread to which this handler + * is attached. + * The time-base is {@link android.os.SystemClock#uptimeMillis}. + * Time spent in deep sleep will add an additional delay to execution. + * + * @param r The Runnable that will be executed. + * @param delayMillis The delay (in milliseconds) until the Runnable + * will be executed. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the Runnable will be processed -- + * if the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public final boolean postDelayed(@NonNull Runnable r, long delayMillis) { + return sendMessageDelayed(getPostMessage(r), delayMillis); + } + + /** @hide */ + public final boolean postDelayed(Runnable r, int what, long delayMillis) { + return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis); + } + + /** + * Causes the Runnable r to be added to the message queue, to be run + * after the specified amount of time elapses. + * The runnable will be run on the thread to which this handler + * is attached. + * The time-base is {@link android.os.SystemClock#uptimeMillis}. + * Time spent in deep sleep will add an additional delay to execution. + * + * @param r The Runnable that will be executed. + * @param token An instance which can be used to cancel {@code r} via + * {@link #removeCallbacksAndMessages}. + * @param delayMillis The delay (in milliseconds) until the Runnable + * will be executed. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the Runnable will be processed -- + * if the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public final boolean postDelayed( + @NonNull Runnable r, @Nullable Object token, long delayMillis) { + return sendMessageDelayed(getPostMessage(r, token), delayMillis); + } + + /** + * Posts a message to an object that implements Runnable. + * Causes the Runnable r to executed on the next iteration through the + * message queue. The runnable will be run on the thread to which this + * handler is attached. + * This method is only for use in very special circumstances -- it + * can easily starve the message queue, cause ordering problems, or have + * other unexpected side-effects. + * + * @param r The Runnable that will be executed. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean postAtFrontOfQueue(@NonNull Runnable r) { + return sendMessageAtFrontOfQueue(getPostMessage(r)); + } + + /** + * Runs the specified task synchronously. + *

+ * If the current thread is the same as the handler thread, then the runnable + * runs immediately without being enqueued. Otherwise, posts the runnable + * to the handler and waits for it to complete before returning. + *

+ * This method is dangerous! Improper use can result in deadlocks. + * Never call this method while any locks are held or use it in a + * possibly re-entrant manner. + *

+ * This method is occasionally useful in situations where a background thread + * must synchronously await completion of a task that must run on the + * handler's thread. However, this problem is often a symptom of bad design. + * Consider improving the design (if possible) before resorting to this method. + *

+ * One example of where you might want to use this method is when you just + * set up a Handler thread and need to perform some initialization steps on + * it before continuing execution. + *

+ * If timeout occurs then this method returns false but the runnable + * will remain posted on the handler and may already be in progress or + * complete at a later time. + *

+ * When using this method, be sure to use {@link Looper#quitSafely} when + * quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely. + * (TODO: We should fix this by making MessageQueue aware of blocking runnables.) + *

+ * + * @param r The Runnable that will be executed synchronously. + * @param timeout The timeout in milliseconds, or 0 to wait indefinitely. + * + * @return Returns true if the Runnable was successfully executed. + * Returns false on failure, usually because the + * looper processing the message queue is exiting. + * + * @hide This method is prone to abuse and should probably not be in the API. + * If we ever do make it part of the API, we might want to rename it to something + * less funny like runUnsafe(). + */ + public final boolean runWithScissors(@NonNull Runnable r, long timeout) { + if (r == null) { + throw new IllegalArgumentException("runnable must not be null"); + } + if (timeout < 0) { + throw new IllegalArgumentException("timeout must be non-negative"); + } + + if (Looper.myLooper() == mLooper) { + r.run(); + return true; + } + + BlockingRunnable br = new BlockingRunnable(r); + return br.postAndWait(this, timeout); + } + + /** + * Remove any pending posts of Runnable r that are in the message queue. + */ + public final void removeCallbacks(@NonNull Runnable r) { + mQueue.removeMessages(this, r, null); + } + + /** + * Remove any pending posts of Runnable r with Object + * token that are in the message queue. If token is null, + * all callbacks will be removed. + */ + public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token) { + mQueue.removeMessages(this, r, token); + } + + /** + * Pushes a message onto the end of the message queue after all pending messages + * before the current time. It will be received in {@link #handleMessage}, + * in the thread attached to this handler. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean sendMessage(@NonNull Message msg) { + return sendMessageDelayed(msg, 0); + } + + /** + * Sends a Message containing only the what value. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean sendEmptyMessage(int what) + { + return sendEmptyMessageDelayed(what, 0); + } + + /** + * Sends a Message containing only the what value, to be delivered + * after the specified amount of time elapses. + * @see #sendMessageDelayed(android.os.Message, long) + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { + Message msg = Message.obtain(); + msg.what = what; + return sendMessageDelayed(msg, delayMillis); + } + + /** + * Sends a Message containing only the what value, to be delivered + * at a specific time. + * @see #sendMessageAtTime(android.os.Message, long) + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + + public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { + Message msg = Message.obtain(); + msg.what = what; + return sendMessageAtTime(msg, uptimeMillis); + } + + /** + * Enqueue a message into the message queue after all pending messages + * before (current time + delayMillis). You will receive it in + * {@link #handleMessage}, in the thread attached to this handler. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the message will be processed -- if + * the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) { + if (delayMillis < 0) { + delayMillis = 0; + } + return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); + } + + /** + * Enqueue a message into the message queue after all pending messages + * before the absolute time (in milliseconds) uptimeMillis. + * The time-base is {@link android.os.SystemClock#uptimeMillis}. + * Time spent in deep sleep will add an additional delay to execution. + * You will receive it in {@link #handleMessage}, in the thread attached + * to this handler. + * + * @param uptimeMillis The absolute time at which the message should be + * delivered, using the + * {@link android.os.SystemClock#uptimeMillis} time-base. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the message will be processed -- if + * the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { + MessageQueue queue = mQueue; + if (queue == null) { + RuntimeException e = new RuntimeException( + this + " sendMessageAtTime() called with no mQueue"); + Log.w("Looper", e.getMessage(), e); + return false; + } + return enqueueMessage(queue, msg, uptimeMillis); + } + + /** + * Enqueue a message at the front of the message queue, to be processed on + * the next iteration of the message loop. You will receive it in + * {@link #handleMessage}, in the thread attached to this handler. + * This method is only for use in very special circumstances -- it + * can easily starve the message queue, cause ordering problems, or have + * other unexpected side-effects. + * + * @return Returns true if the message was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + */ + public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) { + MessageQueue queue = mQueue; + if (queue == null) { + RuntimeException e = new RuntimeException( + this + " sendMessageAtFrontOfQueue() called with no mQueue"); + Log.w("Looper", e.getMessage(), e); + return false; + } + return enqueueMessage(queue, msg, 0); + } + + /** + * Executes the message synchronously if called on the same thread this handler corresponds to, + * or {@link #sendMessage pushes it to the queue} otherwise + * + * @return Returns true if the message was successfully ran or placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. + * @hide + */ + public final boolean executeOrSendMessage(@NonNull Message msg) { + if (mLooper == Looper.myLooper()) { + dispatchMessage(msg); + return true; + } + return sendMessage(msg); + } + + private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, + long uptimeMillis) { + msg.target = this; + msg.workSourceUid = ThreadLocalWorkSource.getUid(); + + if (mAsynchronous) { + msg.setAsynchronous(true); + } + return queue.enqueueMessage(msg, uptimeMillis); + } + + private Object disallowNullArgumentIfShared(@Nullable Object arg) { + if (mIsShared && arg == null) { + throw new IllegalArgumentException("Null argument disallowed for shared handler." + + " Consider creating your own Handler instance."); + } + return arg; + } + + /** + * Remove any pending posts of messages with code 'what' that are in the + * message queue. + * + * Note that `Message#what` is 0 unless otherwise set. + * When calling `postMessage(Runnable)` or `postAtTime(Runnable, long)`, + * the `Runnable` is internally wrapped with a `Message` whose `what` is 0. + * Calling `removeMessages(0)` will remove all messages without a `what`, + * including posted `Runnable`s. + */ + public final void removeMessages(int what) { + mQueue.removeMessages(this, what, null); + } + + /** + * Remove any pending posts of messages with code 'what' and whose obj is + * 'object' that are in the message queue. If object is null, + * all messages will be removed. + */ + public final void removeMessages(int what, @Nullable Object object) { + mQueue.removeMessages(this, what, disallowNullArgumentIfShared(object)); + } + + /** + * WARNING: This API is dangerous because if the implementation + * of equals() is broken, it would delete unrelated events. For example, + * if object.equals() always returns true, it'd remove all messages. + * + * For this reason, never expose this API to non-platform code. i.e. + * this shouldn't be exposed to SystemApi.PRIVILEGED_APPS. + * + * Remove any pending posts of messages with code 'what' and whose obj is + * 'object' that are in the message queue. If object is null, + * all messages will be removed. + * + *

Similar to {@link #removeMessages(int, Object)} but uses object equality + * ({@link Object#equals(Object)}) instead of reference equality (==) in + * determining whether object is the message's obj'. + * + *@hide + */ + @TestApi + public final void removeEqualMessages(int what, @Nullable Object object) { + mQueue.removeEqualMessages(this, what, disallowNullArgumentIfShared(object)); + } + + /** + * Remove any pending posts of callbacks and sent messages whose + * obj is token. If token is null, + * all callbacks and messages will be removed. + */ + public final void removeCallbacksAndMessages(@Nullable Object token) { + mQueue.removeCallbacksAndMessages(this, disallowNullArgumentIfShared(token)); + } + + /** + * WARNING: This API is dangerous because if the implementation + * of equals() is broken, it would delete unrelated events. For example, + * if object.equals() always returns true, it'd remove all messages. + * + * For this reason, never expose this API to non-platform code. i.e. + * this shouldn't be exposed to SystemApi.PRIVILEGED_APPS. + * + * Remove any pending posts of callbacks and sent messages whose + * obj is token. If token is null, + * all callbacks and messages will be removed. + * + *

Similar to {@link #removeCallbacksAndMessages(Object)} but uses object + * equality ({@link Object#equals(Object)}) instead of reference equality (==) in + * determining whether object is the message's obj'. + * + *@hide + */ + @TestApi + public final void removeCallbacksAndEqualMessages(@Nullable Object token) { + mQueue.removeCallbacksAndEqualMessages(this, disallowNullArgumentIfShared(token)); + } + /** + * Check if there are any pending posts of messages with code 'what' in + * the message queue. + */ + public final boolean hasMessages(int what) { + return mQueue.hasMessages(this, what, null); + } + + /** + * Return whether there are any messages or callbacks currently scheduled on this handler. + * @hide + */ + @TestApi + public final boolean hasMessagesOrCallbacks() { + return mQueue.hasMessages(this); + } + + /** + * Check if there are any pending posts of messages with code 'what' and + * whose obj is 'object' in the message queue. + */ + public final boolean hasMessages(int what, @Nullable Object object) { + return mQueue.hasMessages(this, what, object); + } + + /** + * Check if there are any pending posts of messages with code 'what' and + * whose obj is 'object' in the message queue. + * + *@hide + */ + public final boolean hasEqualMessages(int what, @Nullable Object object) { + return mQueue.hasEqualMessages(this, what, object); + } + + /** + * Check if there are any pending posts of messages with callback r in + * the message queue. + */ + public final boolean hasCallbacks(@NonNull Runnable r) { + return mQueue.hasMessages(this, r, null); + } + + // if we can get rid of this method, the handler need not remember its loop + // we could instead export a getMessageQueue() method... + @NonNull + public final Looper getLooper() { + return mLooper; + } + + public final void dump(@NonNull Printer pw, @NonNull String prefix) { + pw.println(prefix + this + " @ " + SystemClock.uptimeMillis()); + if (mLooper == null) { + pw.println(prefix + "looper uninitialized"); + } else { + mLooper.dump(pw, prefix + " "); + } + } + + /** + * @hide + */ + public final void dumpMine(@NonNull Printer pw, @NonNull String prefix) { + pw.println(prefix + this + " @ " + SystemClock.uptimeMillis()); + if (mLooper == null) { + pw.println(prefix + "looper uninitialized"); + } else { + mLooper.dump(pw, prefix + " ", this); + } + } + + @Override + public String toString() { + return "Handler (" + getClass().getName() + ") {" + + Integer.toHexString(System.identityHashCode(this)) + + "}"; + } + + final IMessenger getIMessenger() { + throw new RuntimeException("Stub!"); + // synchronized (mQueue) { + // if (mMessenger != null) { + // return mMessenger; + // } + // mMessenger = new MessengerImpl(); + // return mMessenger; + // } + } + + // private final class MessengerImpl extends IMessenger.Stub { + // public void send(Message msg) { + // msg.sendingUid = Binder.getCallingUid(); + // Handler.this.sendMessage(msg); + // } + // } + + private static Message getPostMessage(Runnable r) { + Message m = Message.obtain(); + m.callback = r; + return m; + } + + private static Message getPostMessage(Runnable r, Object token) { + Message m = Message.obtain(); + m.obj = token; + m.callback = r; + return m; + } + + private static void handleCallback(Message message) { + message.callback.run(); + } + + final Looper mLooper; + final MessageQueue mQueue; + final Callback mCallback; + final boolean mAsynchronous; + IMessenger mMessenger; + + /** If it's a shared handler, we disallow certain dangeraous operations. */ + private final boolean mIsShared; + + private static final class BlockingRunnable implements Runnable { + private final Runnable mTask; + private boolean mDone; + + public BlockingRunnable(Runnable task) { + mTask = task; + } + + @Override + public void run() { + try { + mTask.run(); + } finally { + synchronized (this) { + mDone = true; + notifyAll(); + } + } + } + + public boolean postAndWait(Handler handler, long timeout) { + if (!handler.post(this)) { + return false; + } + + synchronized (this) { + if (timeout > 0) { + final long expirationTime = SystemClock.uptimeMillis() + timeout; + while (!mDone) { + long delay = expirationTime - SystemClock.uptimeMillis(); + if (delay <= 0) { + return false; // timeout + } + try { + wait(delay); + } catch (InterruptedException ex) { + } + } + } else { + while (!mDone) { + try { + wait(); + } catch (InterruptedException ex) { + } + } + } + } + return true; + } + } +} diff --git a/AndroidCompat/src/main/java/android/os/IMessenger.java b/AndroidCompat/src/main/java/android/os/IMessenger.java new file mode 100644 index 00000000..fb883867 --- /dev/null +++ b/AndroidCompat/src/main/java/android/os/IMessenger.java @@ -0,0 +1,25 @@ +/* //device/java/android/android/app/IActivityPendingResult.aidl +** +** Copyright 2007, 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.os; + +import android.os.Message; + +/** @hide */ +/* oneway */ interface IMessenger { + void send(/* in */ Message msg); +} diff --git a/AndroidCompat/src/main/java/android/os/Looper.java b/AndroidCompat/src/main/java/android/os/Looper.java new file mode 100644 index 00000000..6b2bd970 --- /dev/null +++ b/AndroidCompat/src/main/java/android/os/Looper.java @@ -0,0 +1,575 @@ +/* + * 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.os; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Log; +import android.util.Printer; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; + +import java.util.Objects; + +/** + * Class used to run a message loop for a thread. Threads by default do + * not have a message loop associated with them; to create one, call + * {@link #prepare} in the thread that is to run the loop, and then + * {@link #loop} to have it process messages until the loop is stopped. + * + *

Most interaction with a message loop is through the + * {@link Handler} class. + * + *

This is a typical example of the implementation of a Looper thread, + * using the separation of {@link #prepare} and {@link #loop} to create an + * initial Handler to communicate with the Looper. + * + *

+  *  class LooperThread extends Thread {
+  *      public Handler mHandler;
+  *
+  *      public void run() {
+  *          Looper.prepare();
+  *
+  *          mHandler = new Handler(Looper.myLooper()) {
+  *              public void handleMessage(Message msg) {
+  *                  // process incoming messages here
+  *              }
+  *          };
+  *
+  *          Looper.loop();
+  *      }
+  *  }
+ */ +public final class Looper { + /* + * API Implementation Note: + * + * This class contains the code required to set up and manage an event loop + * based on MessageQueue. APIs that affect the state of the queue should be + * defined on MessageQueue or Handler rather than on Looper itself. For example, + * idle handlers and sync barriers are defined on the queue whereas preparing the + * thread, looping, and quitting are defined on the looper. + */ + + private static final String TAG = "Looper"; + + private static class NoImagePreloadHolder { + // Enable/Disable verbose logging with a system prop. e.g. + // adb shell 'setprop log.looper.slow.verbose false && stop && start' + private static final boolean sVerboseLogging = + SystemProperties.getBoolean("log.looper.slow.verbose", false); + } + + // sThreadLocal.get() will return null unless you've called prepare(). + static final ThreadLocal sThreadLocal = new ThreadLocal(); + private static Looper sMainLooper; // guarded by Looper.class + private static Observer sObserver; + + final MessageQueue mQueue; + final Thread mThread; + private boolean mInLoop; + + private Printer mLogging; + private long mTraceTag; + + /** + * If set, the looper will show a warning log if a message dispatch takes longer than this. + */ + private long mSlowDispatchThresholdMs; + + /** + * If set, the looper will show a warning log if a message delivery (actual delivery time - + * post time) takes longer than this. + */ + private long mSlowDeliveryThresholdMs; + + /** + * True if a message delivery takes longer than {@link #mSlowDeliveryThresholdMs}. + */ + private boolean mSlowDeliveryDetected; + + /** Initialize the current thread as a looper. + * This gives you a chance to create handlers that then reference + * this looper, before actually starting the loop. Be sure to call + * {@link #loop()} after calling this method, and end it by calling + * {@link #quit()}. + */ + public static void prepare() { + prepare(true); + } + + private static void prepare(boolean quitAllowed) { + if (sThreadLocal.get() != null) { + throw new RuntimeException("Only one Looper may be created per thread"); + } + sThreadLocal.set(new Looper(quitAllowed)); + } + + /** + * Initialize the current thread as a looper, marking it as an + * application's main looper. See also: {@link #prepare()} + * + * @deprecated The main looper for your application is created by the Android environment, + * so you should never need to call this function yourself. + */ + @Deprecated + public static void prepareMainLooper() { + prepare(false); + synchronized (Looper.class) { + if (sMainLooper != null) { + throw new IllegalStateException("The main Looper has already been prepared."); + } + sMainLooper = myLooper(); + } + } + + /** + * Returns the application's main looper, which lives in the main thread of the application. + */ + public static Looper getMainLooper() { + synchronized (Looper.class) { + return sMainLooper; + } + } + + /** + * Force the application's main looper to the given value. The main looper is typically + * configured automatically by the OS, so this capability is only intended to enable testing. + * + * @hide + */ + public static void setMainLooperForTest(@NonNull Looper looper) { + synchronized (Looper.class) { + sMainLooper = Objects.requireNonNull(looper); + } + } + + /** + * Clear the application's main looper to be undefined. The main looper is typically + * configured automatically by the OS, so this capability is only intended to enable testing. + * + * @hide + */ + public static void clearMainLooperForTest() { + synchronized (Looper.class) { + sMainLooper = null; + } + } + + /** + * Set the transaction observer for all Loopers in this process. + * + * @hide + */ + public static void setObserver(@Nullable Observer observer) { + sObserver = observer; + } + + /** + * Poll and deliver single message, return true if the outer loop should continue. + */ + @SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity", + "ClearIdentityCallNotFollowedByTryFinally"}) + private static boolean loopOnce(final Looper me, + final long ident, final int thresholdOverride) { + Message msg = me.mQueue.next(); // might block + if (msg == null) { + // No message indicates that the message queue is quitting. + return false; + } + + // This must be in a local variable, in case a UI event sets the logger + final Printer logging = me.mLogging; + if (logging != null) { + logging.println(">>>>> Dispatching to " + msg.target + " " + + msg.callback + ": " + msg.what); + } + // Make sure the observer won't change while processing a transaction. + final Observer observer = sObserver; + + final long traceTag = me.mTraceTag; + long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; + long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; + + final boolean hasOverride = thresholdOverride >= 0; + if (hasOverride) { + slowDispatchThresholdMs = thresholdOverride; + slowDeliveryThresholdMs = thresholdOverride; + } + final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0 || hasOverride) + && (msg.when > 0); + final boolean logSlowDispatch = (slowDispatchThresholdMs > 0 || hasOverride); + + final boolean needStartTime = logSlowDelivery || logSlowDispatch; + final boolean needEndTime = logSlowDispatch; + + final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; + final long dispatchEnd; + Object token = null; + if (observer != null) { + token = observer.messageDispatchStarting(); + } + long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); + try { + msg.target.dispatchMessage(msg); + if (observer != null) { + observer.messageDispatched(token, msg); + } + } catch (Exception exception) { + if (observer != null) { + observer.dispatchingThrewException(token, msg, exception); + } + Log.e(TAG, "Loop handler threw", exception); + // throw exception; + } finally { + dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; + ThreadLocalWorkSource.restore(origWorkSource); + } + if (logSlowDelivery) { + boolean slow = false; + + if (!me.mSlowDeliveryDetected || NoImagePreloadHolder.sVerboseLogging) { + slow = showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, + "delivery", msg); + } + if (me.mSlowDeliveryDetected) { + if (!slow && (dispatchStart - msg.when) <= 10) { + Slog.w(TAG, "Drained"); + me.mSlowDeliveryDetected = false; + } + } else if (slow) { + // A slow delivery is detected, suppressing further logs unless verbose logging + // is enabled. + me.mSlowDeliveryDetected = true; + } + } + if (logSlowDispatch) { + showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg); + } + + if (logging != null) { + logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); + } + + // Make sure that during the course of dispatching the + // identity of the thread wasn't corrupted. + // final long newIdent = Binder.clearCallingIdentity(); + // if (ident != newIdent) { + // Log.wtf(TAG, "Thread identity changed from 0x" + // + Long.toHexString(ident) + " to 0x" + // + Long.toHexString(newIdent) + " while dispatching to " + // + msg.target.getClass().getName() + " " + // + msg.callback + " what=" + msg.what); + // } + + msg.recycleUnchecked(); + + return true; + } + + /** + * Run the message queue in this thread. Be sure to call + * {@link #quit()} to end the loop. + */ + @SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity", + "ClearIdentityCallNotFollowedByTryFinally", + "ResultOfClearIdentityCallNotStoredInVariable"}) + public static void loop() { + final Looper me = myLooper(); + if (me == null) { + throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); + } + if (me.mInLoop) { + Slog.w(TAG, "Loop again would have the queued messages be executed" + + " before this one completed."); + } + + me.mInLoop = true; + + // Make sure the identity of this thread is that of the local process, + // and keep track of what that identity token actually is. + // Binder.clearCallingIdentity(); + // final long ident = Binder.clearCallingIdentity(); + final long ident = 0; + + // Allow overriding a threshold with a system prop. e.g. + // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start' + final int thresholdOverride = getThresholdOverride(); + + me.mSlowDeliveryDetected = false; + + for (;;) { + if (!loopOnce(me, ident, thresholdOverride)) { + return; + } + } + } + + private static int getThresholdOverride() { + return -1; + // // Allow overriding the threshold for all processes' main looper with a system prop. + // // e.g. adb shell 'setprop log.looper.any.main.slow 1 && stop && start' + // if (myLooper() == getMainLooper()) { + // final int globalOverride = SystemProperties.getInt("log.looper.any.main.slow", -1); + // if (globalOverride >= 0) { + // return globalOverride; + // } + // } + + // // Allow overriding the threshold for all threads within a process with a system prop. + // // e.g. adb shell 'setprop log.looper.1000.any.slow 1 && stop && start' + // final int processOverride = SystemProperties.getInt("log.looper." + // + Process.myUid() + ".any.slow", -1); + // if (processOverride >= 0) { + // return processOverride; + // } + + // return SystemProperties.getInt("log.looper." + // + Process.myUid() + "." + // + Thread.currentThread().getName() + // + ".slow", -1); + } + + private static int getThresholdOverride$ravenwood() { + return -1; + } + + private static int getThreadGroup() { + int threadGroup = Process.THREAD_GROUP_DEFAULT; + + if (!Process.isIsolated()) { + threadGroup = Process.getProcessGroup(Process.myTid()); + } + return threadGroup; + } + + private static String threadGroupToString(int threadGroup) { + switch (threadGroup) { + case Process.THREAD_GROUP_SYSTEM: + return "SYSTEM"; + case Process.THREAD_GROUP_AUDIO_APP: + return "AUDIO_APP"; + case Process.THREAD_GROUP_AUDIO_SYS: + return "AUDIO_SYS"; + case Process.THREAD_GROUP_TOP_APP: + return "TOP_APP"; + case Process.THREAD_GROUP_RT_APP: + return "RT_APP"; + default: + return "UNKNOWN"; + } + } + + private static boolean showSlowLog(long threshold, long measureStart, long measureEnd, + String what, Message msg) { + final long actualTime = measureEnd - measureStart; + if (actualTime < threshold) { + return false; + } + + String name = /* Process.myProcessName() */ "Stub!"; + String threadGroup = threadGroupToString(getThreadGroup()); + boolean isMain = myLooper() == getMainLooper(); + + // For slow delivery, the current message isn't really important, but log it anyway. + Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms " + + Thread.currentThread().getName() + " app=" + name + + " main=" + isMain + " group=" + threadGroup + + " h=" + msg.target.getClass().getName() + " c=" + msg.callback + + " m=" + msg.what); + return true; + } + + /** + * Return the Looper object associated with the current thread. Returns + * null if the calling thread is not associated with a Looper. + */ + public static @Nullable Looper myLooper() { + return sThreadLocal.get(); + } + + /** + * Return the {@link MessageQueue} object associated with the current + * thread. This must be called from a thread running a Looper, or a + * NullPointerException will be thrown. + */ + public static @NonNull MessageQueue myQueue() { + return myLooper().mQueue; + } + + private Looper(boolean quitAllowed) { + mQueue = new MessageQueue(quitAllowed); + mThread = Thread.currentThread(); + } + + /** + * Returns true if the current thread is this looper's thread. + */ + public boolean isCurrentThread() { + return Thread.currentThread() == mThread; + } + + /** + * Control logging of messages as they are processed by this Looper. If + * enabled, a log message will be written to printer + * at the beginning and ending of each message dispatch, identifying the + * target Handler and message contents. + * + * @param printer A Printer object that will receive log messages, or + * null to disable message logging. + */ + public void setMessageLogging(@Nullable Printer printer) { + mLogging = printer; + } + + /** {@hide} */ + public void setTraceTag(long traceTag) { + mTraceTag = traceTag; + } + + /** + * Set a thresholds for slow dispatch/delivery log. + * {@hide} + */ + public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) { + mSlowDispatchThresholdMs = slowDispatchThresholdMs; + mSlowDeliveryThresholdMs = slowDeliveryThresholdMs; + } + + /** + * Quits the looper. + *

+ * Causes the {@link #loop} method to terminate without processing any + * more messages in the message queue. + *

+ * Any attempt to post messages to the queue after the looper is asked to quit will fail. + * For example, the {@link Handler#sendMessage(Message)} method will return false. + *

+ * Using this method may be unsafe because some messages may not be delivered + * before the looper terminates. Consider using {@link #quitSafely} instead to ensure + * that all pending work is completed in an orderly manner. + *

+ * + * @see #quitSafely + */ + public void quit() { + mQueue.quit(false); + } + + /** + * Quits the looper safely. + *

+ * Causes the {@link #loop} method to terminate as soon as all remaining messages + * in the message queue that are already due to be delivered have been handled. + * However pending delayed messages with due times in the future will not be + * delivered before the loop terminates. + *

+ * Any attempt to post messages to the queue after the looper is asked to quit will fail. + * For example, the {@link Handler#sendMessage(Message)} method will return false. + *

+ */ + public void quitSafely() { + mQueue.quit(true); + } + + /** + * Gets the Thread associated with this Looper. + * + * @return The looper's thread. + */ + public @NonNull Thread getThread() { + return mThread; + } + + /** + * Gets this looper's message queue. + * + * @return The looper's message queue. + */ + public @NonNull MessageQueue getQueue() { + return mQueue; + } + + /** + * Dumps the state of the looper for debugging purposes. + * + * @param pw A printer to receive the contents of the dump. + * @param prefix A prefix to prepend to each line which is printed. + */ + public void dump(@NonNull Printer pw, @NonNull String prefix) { + throw new RuntimeException("Stub!"); + } + + /** + * Dumps the state of the looper for debugging purposes. + * + * @param pw A printer to receive the contents of the dump. + * @param prefix A prefix to prepend to each line which is printed. + * @param handler Only dump messages for this Handler. + * @hide + */ + public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + public void dumpDebug(ProtoOutputStream proto, long fieldId) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + return "Looper (" + mThread.getName() + ", tid " + mThread.getId() + + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}"; + } + + /** {@hide} */ + public interface Observer { + /** + * Called right before a message is dispatched. + * + *

The token type is not specified to allow the implementation to specify its own type. + * + * @return a token used for collecting telemetry when dispatching a single message. + * The token token must be passed back exactly once to either + * {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException} + * and must not be reused again. + * + */ + Object messageDispatchStarting(); + + /** + * Called when a message was processed by a Handler. + * + * @param token Token obtained by previously calling + * {@link Observer#messageDispatchStarting} on the same Observer instance. + * @param msg The message that was dispatched. + */ + void messageDispatched(Object token, Message msg); + + /** + * Called when an exception was thrown while processing a message. + * + * @param token Token obtained by previously calling + * {@link Observer#messageDispatchStarting} on the same Observer instance. + * @param msg The message that was dispatched and caused an exception. + * @param exception The exception that was thrown. + */ + void dispatchingThrewException(Object token, Message msg, Exception exception); + } +} diff --git a/AndroidCompat/src/main/java/android/os/Message.java b/AndroidCompat/src/main/java/android/os/Message.java new file mode 100644 index 00000000..89360908 --- /dev/null +++ b/AndroidCompat/src/main/java/android/os/Message.java @@ -0,0 +1,630 @@ +/* + * 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.os; + +import android.annotation.Nullable; +import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; + +/** + * + * Defines a message containing a description and arbitrary data object that can be + * sent to a {@link Handler}. This object contains two extra int fields and an + * extra object field that allow you to not do allocations in many cases. + * + *

While the constructor of Message is public, the best way to get + * one of these is to call {@link #obtain Message.obtain()} or one of the + * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull + * them from a pool of recycled objects.

+ */ +public final class Message implements Parcelable { + /** + * User-defined message code so that the recipient can identify + * what this message is about. Each {@link Handler} has its own name-space + * for message codes, so you do not need to worry about yours conflicting + * with other handlers. + * + * If not specified, this value is 0. + * Use values other than 0 to indicate custom message codes. + */ + public int what; + + /** + * arg1 and arg2 are lower-cost alternatives to using + * {@link #setData(Bundle) setData()} if you only need to store a + * few integer values. + */ + public int arg1; + + /** + * arg1 and arg2 are lower-cost alternatives to using + * {@link #setData(Bundle) setData()} if you only need to store a + * few integer values. + */ + public int arg2; + + /** + * An arbitrary object to send to the recipient. When using + * {@link Messenger} to send the message across processes this can only + * be non-null if it contains a Parcelable of a framework class (not one + * implemented by the application). For other data transfer use + * {@link #setData}. + * + *

Note that Parcelable objects here are not supported prior to + * the {@link android.os.Build.VERSION_CODES#FROYO} release. + */ + public Object obj; + + /** + * Optional Messenger where replies to this message can be sent. The + * semantics of exactly how this is used are up to the sender and + * receiver. + */ + public Messenger replyTo; + + /** + * Indicates that the uid is not set; + * + * @hide Only for use within the system server. + */ + public static final int UID_NONE = -1; + + /** + * Optional field indicating the uid that sent the message. This is + * only valid for messages posted by a {@link Messenger}; otherwise, + * it will be -1. + */ + public int sendingUid = UID_NONE; + + /** + * Optional field indicating the uid that caused this message to be enqueued. + * + * @hide Only for use within the system server. + */ + public int workSourceUid = UID_NONE; + + /** If set message is in use. + * This flag is set when the message is enqueued and remains set while it + * is delivered and afterwards when it is recycled. The flag is only cleared + * when a new message is created or obtained since that is the only time that + * applications are allowed to modify the contents of the message. + * + * It is an error to attempt to enqueue or recycle a message that is already in use. + */ + /*package*/ static final int FLAG_IN_USE = 1 << 0; + + /** If set message is asynchronous */ + /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1; + + /** Flags to clear in the copyFrom method */ + /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; + + /*package*/ int flags; + + /** + * The targeted delivery time of this message. The time-base is + * {@link SystemClock#uptimeMillis}. + * @hide Only for use within the tests. + */ + public long when; + + /** @hide */ + @SuppressWarnings("unused") + public long mInsertSeq; + + /*package*/ Bundle data; + + /*package*/ Handler target; + + /*package*/ Runnable callback; + + // sometimes we store linked lists of these things + /*package*/ Message next; + + + /** @hide */ + public static final Object sPoolSync = new Object(); + private static Message sPool; + private static int sPoolSize = 0; + + private static final int MAX_POOL_SIZE = 50; + + private static boolean gCheckRecycle = true; + + /** + * Return a new Message instance from the global pool. Allows us to + * avoid allocating new objects in many cases. + */ + public static Message obtain() { + synchronized (sPoolSync) { + if (sPool != null) { + Message m = sPool; + sPool = m.next; + m.next = null; + m.flags = 0; // clear in-use flag + sPoolSize--; + return m; + } + } + return new Message(); + } + + /** + * Same as {@link #obtain()}, but copies the values of an existing + * message (including its target) into the new one. + * @param orig Original message to copy. + * @return A Message object from the global pool. + */ + public static Message obtain(Message orig) { + Message m = obtain(); + m.what = orig.what; + m.arg1 = orig.arg1; + m.arg2 = orig.arg2; + m.obj = orig.obj; + m.replyTo = orig.replyTo; + m.sendingUid = orig.sendingUid; + m.workSourceUid = orig.workSourceUid; + if (orig.data != null) { + m.data = new Bundle(orig.data); + } + m.target = orig.target; + m.callback = orig.callback; + + return m; + } + + /** + * Same as {@link #obtain()}, but sets the value for the target member on the Message returned. + * @param h Handler to assign to the returned Message object's target member. + * @return A Message object from the global pool. + */ + public static Message obtain(Handler h) { + Message m = obtain(); + m.target = h; + + return m; + } + + /** + * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on + * the Message that is returned. + * @param h Handler to assign to the returned Message object's target member. + * @param callback Runnable that will execute when the message is handled. + * @return A Message object from the global pool. + */ + public static Message obtain(Handler h, Runnable callback) { + Message m = obtain(); + m.target = h; + m.callback = callback; + + return m; + } + + /** + * Same as {@link #obtain()}, but sets the values for both target and + * what members on the Message. + * @param h Value to assign to the target member. + * @param what Value to assign to the what member. + * @return A Message object from the global pool. + */ + public static Message obtain(Handler h, int what) { + Message m = obtain(); + m.target = h; + m.what = what; + + return m; + } + + /** + * Same as {@link #obtain()}, but sets the values of the target, what, and obj + * members. + * @param h The target value to set. + * @param what The what value to set. + * @param obj The object method to set. + * @return A Message object from the global pool. + */ + public static Message obtain(Handler h, int what, Object obj) { + Message m = obtain(); + m.target = h; + m.what = what; + m.obj = obj; + + return m; + } + + /** + * Same as {@link #obtain()}, but sets the values of the target, what, + * arg1, and arg2 members. + * + * @param h The target value to set. + * @param what The what value to set. + * @param arg1 The arg1 value to set. + * @param arg2 The arg2 value to set. + * @return A Message object from the global pool. + */ + public static Message obtain(Handler h, int what, int arg1, int arg2) { + Message m = obtain(); + m.target = h; + m.what = what; + m.arg1 = arg1; + m.arg2 = arg2; + + return m; + } + + /** + * Same as {@link #obtain()}, but sets the values of the target, what, + * arg1, arg2, and obj members. + * + * @param h The target value to set. + * @param what The what value to set. + * @param arg1 The arg1 value to set. + * @param arg2 The arg2 value to set. + * @param obj The obj value to set. + * @return A Message object from the global pool. + */ + public static Message obtain(Handler h, int what, + int arg1, int arg2, Object obj) { + Message m = obtain(); + m.target = h; + m.what = what; + m.arg1 = arg1; + m.arg2 = arg2; + m.obj = obj; + + return m; + } + + /** @hide */ + public static void updateCheckRecycle(int targetSdkVersion) { + if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) { + gCheckRecycle = false; + } + } + + /** + * Return a Message instance to the global pool. + *

+ * You MUST NOT touch the Message after calling this function because it has + * effectively been freed. It is an error to recycle a message that is currently + * enqueued or that is in the process of being delivered to a Handler. + *

+ */ + public void recycle() { + if (isInUse()) { + if (gCheckRecycle) { + throw new IllegalStateException("This message cannot be recycled because it " + + "is still in use."); + } + return; + } + recycleUnchecked(); + } + + /** + * Recycles a Message that may be in-use. + * Used internally by the MessageQueue and Looper when disposing of queued Messages. + */ + void recycleUnchecked() { + // Mark the message as in use while it remains in the recycled object pool. + // Clear out all other details. + flags = FLAG_IN_USE; + what = 0; + arg1 = 0; + arg2 = 0; + obj = null; + replyTo = null; + sendingUid = UID_NONE; + workSourceUid = UID_NONE; + when = 0; + target = null; + callback = null; + data = null; + + synchronized (sPoolSync) { + if (sPoolSize < MAX_POOL_SIZE) { + next = sPool; + sPool = this; + sPoolSize++; + } + } + } + + /** + * Make this message like o. Performs a shallow copy of the data field. + * Does not copy the linked list fields, nor the timestamp or + * target/callback of the original message. + */ + public void copyFrom(Message o) { + this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM; + this.what = o.what; + this.arg1 = o.arg1; + this.arg2 = o.arg2; + this.obj = o.obj; + this.replyTo = o.replyTo; + this.sendingUid = o.sendingUid; + this.workSourceUid = o.workSourceUid; + + if (o.data != null) { + this.data = (Bundle) o.data.clone(); + } else { + this.data = null; + } + } + + /** + * Return the targeted delivery time of this message, in milliseconds. + */ + public long getWhen() { + return when; + } + + public void setTarget(Handler target) { + this.target = target; + } + + /** + * Retrieve the {@link android.os.Handler Handler} implementation that + * will receive this message. The object must implement + * {@link android.os.Handler#handleMessage(android.os.Message) + * Handler.handleMessage()}. Each Handler has its own name-space for + * message codes, so you do not need to + * worry about yours conflicting with other handlers. + */ + public Handler getTarget() { + return target; + } + + /** + * Retrieve callback object that will execute when this message is handled. + * This object must implement Runnable. This is called by + * the target {@link Handler} that is receiving this Message to + * dispatch it. If + * not set, the message will be dispatched to the receiving Handler's + * {@link Handler#handleMessage(Message)}. + */ + public Runnable getCallback() { + return callback; + } + + /** @hide */ + public Message setCallback(Runnable r) { + callback = r; + return this; + } + + /** + * Obtains a Bundle of arbitrary data associated with this + * event, lazily creating it if necessary. Set this value by calling + * {@link #setData(Bundle)}. Note that when transferring data across + * processes via {@link Messenger}, you will need to set your ClassLoader + * on the Bundle via {@link Bundle#setClassLoader(ClassLoader) + * Bundle.setClassLoader()} so that it can instantiate your objects when + * you retrieve them. + * @see #peekData() + * @see #setData(Bundle) + */ + public Bundle getData() { + if (data == null) { + data = new Bundle(); + } + + return data; + } + + /** + * Like getData(), but does not lazily create the Bundle. A null + * is returned if the Bundle does not already exist. See + * {@link #getData} for further information on this. + * @see #getData() + * @see #setData(Bundle) + */ + @Nullable + public Bundle peekData() { + return data; + } + + /** + * Sets a Bundle of arbitrary data values. Use arg1 and arg2 members + * as a lower cost way to send a few simple integer values, if you can. + * @see #getData() + * @see #peekData() + */ + public void setData(Bundle data) { + this.data = data; + } + + /** + * Chainable setter for {@link #what} + * + * @hide + */ + public Message setWhat(int what) { + this.what = what; + return this; + } + + /** + * Sends this Message to the Handler specified by {@link #getTarget}. + * Throws a null pointer exception if this field has not been set. + */ + public void sendToTarget() { + target.sendMessage(this); + } + + /** + * Returns true if the message is asynchronous, meaning that it is not + * subject to {@link Looper} synchronization barriers. + * + * @return True if the message is asynchronous. + * + * @see #setAsynchronous(boolean) + */ + public boolean isAsynchronous() { + return (flags & FLAG_ASYNCHRONOUS) != 0; + } + + /** + * Sets whether the message is asynchronous, meaning that it is not + * subject to {@link Looper} synchronization barriers. + *

+ * Certain operations, such as view invalidation, may introduce synchronization + * barriers into the {@link Looper}'s message queue to prevent subsequent messages + * from being delivered until some condition is met. In the case of view invalidation, + * messages which are posted after a call to {@link android.view.View#invalidate} + * are suspended by means of a synchronization barrier until the next frame is + * ready to be drawn. The synchronization barrier ensures that the invalidation + * request is completely handled before resuming. + *

+ * Asynchronous messages are exempt from synchronization barriers. They typically + * represent interrupts, input events, and other signals that must be handled independently + * even while other work has been suspended. + *

+ * Note that asynchronous messages may be delivered out of order with respect to + * synchronous messages although they are always delivered in order among themselves. + * If the relative order of these messages matters then they probably should not be + * asynchronous in the first place. Use with caution. + *

+ * + * @param async True if the message is asynchronous. + * + * @see #isAsynchronous() + */ + public void setAsynchronous(boolean async) { + if (async) { + flags |= FLAG_ASYNCHRONOUS; + } else { + flags &= ~FLAG_ASYNCHRONOUS; + } + } + + /*package*/ boolean isInUse() { + return ((flags & FLAG_IN_USE) == FLAG_IN_USE); + } + + /*package*/ void markInUse() { + flags |= FLAG_IN_USE; + } + + /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). + */ + public Message() { + } + + @Override + public String toString() { + return toString(SystemClock.uptimeMillis()); + } + + String toString(long now) { + StringBuilder b = new StringBuilder(); + b.append("{ when="); + TimeUtils.formatDuration(when - now, b); + + if (target != null) { + if (callback != null) { + b.append(" callback="); + b.append(callback.getClass().getName()); + } else { + b.append(" what="); + b.append(what); + } + + if (arg1 != 0) { + b.append(" arg1="); + b.append(arg1); + } + + if (arg2 != 0) { + b.append(" arg2="); + b.append(arg2); + } + + if (obj != null) { + b.append(" obj="); + b.append(obj); + } + + b.append(" target="); + b.append(target.getClass().getName()); + } else { + b.append(" barrier="); + b.append(arg1); + } + + b.append(" }"); + return b.toString(); + } + + public static final @android.annotation.NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public Message createFromParcel(Parcel source) { + Message msg = Message.obtain(); + msg.readFromParcel(source); + return msg; + } + + public Message[] newArray(int size) { + return new Message[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + if (callback != null) { + throw new RuntimeException( + "Can't marshal callbacks across processes."); + } + dest.writeInt(what); + dest.writeInt(arg1); + dest.writeInt(arg2); + if (obj != null) { + try { + Parcelable p = (Parcelable)obj; + dest.writeInt(1); + dest.writeParcelable(p, flags); + } catch (ClassCastException e) { + throw new RuntimeException( + "Can't marshal non-Parcelable objects across processes."); + } + } else { + dest.writeInt(0); + } + dest.writeLong(when); + dest.writeBundle(data); + Messenger.writeMessengerOrNullToParcel(replyTo, dest); + dest.writeInt(sendingUid); + dest.writeInt(workSourceUid); + } + + private void readFromParcel(Parcel source) { + what = source.readInt(); + arg1 = source.readInt(); + arg2 = source.readInt(); + if (source.readInt() != 0) { + obj = source.readParcelable(getClass().getClassLoader()); + } + when = source.readLong(); + data = source.readBundle(); + replyTo = Messenger.readMessengerOrNullFromParcel(source); + sendingUid = source.readInt(); + workSourceUid = source.readInt(); + } +} diff --git a/AndroidCompat/src/main/java/android/os/MessageQueue.java b/AndroidCompat/src/main/java/android/os/MessageQueue.java new file mode 100644 index 00000000..462ff31d --- /dev/null +++ b/AndroidCompat/src/main/java/android/os/MessageQueue.java @@ -0,0 +1,1700 @@ +/* + * Copyright (C) 2024 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.os; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.app.Instrumentation; +import android.os.shadows.ShadowPausedMessageQueue; +import android.util.Log; +import android.util.Printer; +import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; + +import java.io.FileDescriptor; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Low-level class holding the list of messages to be dispatched by a + * {@link Looper}. Messages are not added directly to a MessageQueue, + * but rather through {@link Handler} objects associated with the Looper. + * + *

You can retrieve the MessageQueue for the current thread with + * {@link Looper#myQueue() Looper.myQueue()}. + */ +public final class MessageQueue { + private static final String TAG = "ConcurrentMessageQueue"; + private static final boolean DEBUG = false; + private static final boolean TRACE = false; + + // True if the message queue can be quit. + private final boolean mQuitAllowed; + + @SuppressWarnings("unused") + private long mPtr; // used by native code + + @IntDef(value = { + STACK_NODE_MESSAGE, + STACK_NODE_ACTIVE, + STACK_NODE_PARKED, + STACK_NODE_TIMEDPARK}) + @Retention(RetentionPolicy.SOURCE) + private @interface StackNodeType {} + + /* + * Stack node types. STACK_NODE_MESSAGE indicates a node containing a message. + * The other types indicate what state our Looper thread is in. The bottom of + * the stack is always a single state node. Message nodes are added on top. + */ + private static final int STACK_NODE_MESSAGE = 0; + /* + * Active state indicates that next() is processing messages + */ + private static final int STACK_NODE_ACTIVE = 1; + /* + * Parked state indicates that the Looper thread is sleeping indefinitely (nothing to deliver) + */ + private static final int STACK_NODE_PARKED = 2; + /* + * Timed Park state indicates that the Looper thread is sleeping, waiting for a message + * deadline + */ + private static final int STACK_NODE_TIMEDPARK = 3; + + /* Describes a node in the Treiber stack */ + static class StackNode { + @StackNodeType + private final int mType; + + StackNode(@StackNodeType int type) { + mType = type; + } + + @StackNodeType + final int getNodeType() { + return mType; + } + + final boolean isMessageNode() { + return mType == STACK_NODE_MESSAGE; + } + } + + static final class MessageNode extends StackNode implements Comparable { + private final Message mMessage; + volatile StackNode mNext; + StateNode mBottomOfStack; + boolean mWokeUp; + final long mInsertSeq; + private static final VarHandle sRemovedFromStack; + private volatile boolean mRemovedFromStackValue; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sRemovedFromStack = l.findVarHandle(MessageQueue.MessageNode.class, + "mRemovedFromStackValue", boolean.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + MessageNode(@NonNull Message message, long insertSeq) { + super(STACK_NODE_MESSAGE); + mMessage = message; + mInsertSeq = insertSeq; + } + + long getWhen() { + return mMessage.when; + } + + boolean isRemovedFromStack() { + return mRemovedFromStackValue; + } + + boolean removeFromStack() { + return sRemovedFromStack.compareAndSet(this, false, true); + } + + boolean isAsync() { + return mMessage.isAsynchronous(); + } + + boolean isBarrier() { + return mMessage.target == null; + } + + @Override + public int compareTo(@NonNull MessageNode messageNode) { + Message other = messageNode.mMessage; + + int compared = Long.compare(mMessage.when, other.when); + if (compared == 0) { + compared = Long.compare(mInsertSeq, messageNode.mInsertSeq); + } + return compared; + } + } + + static class StateNode extends StackNode { + StateNode(int type) { + super(type); + } + } + + static final class TimedParkStateNode extends StateNode { + long mWhenToWake; + + TimedParkStateNode() { + super(STACK_NODE_TIMEDPARK); + } + } + + private static final StateNode sStackStateActive = new StateNode(STACK_NODE_ACTIVE); + private static final StateNode sStackStateParked = new StateNode(STACK_NODE_PARKED); + private final TimedParkStateNode mStackStateTimedPark = new TimedParkStateNode(); + + /* This is the top of our treiber stack. */ + private static final VarHandle sState; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sState = l.findVarHandle(MessageQueue.class, "mStateValue", + MessageQueue.StackNode.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + private volatile StackNode mStateValue = sStackStateParked; + private final ConcurrentSkipListSet mPriorityQueue = + new ConcurrentSkipListSet(); + private final ConcurrentSkipListSet mAsyncPriorityQueue = + new ConcurrentSkipListSet(); + + /* + * This helps us ensure that messages with the same timestamp are inserted in FIFO order. + * Increments on each insert, starting at 0. MessageNode.compareTo() will compare sequences + * when delivery timestamps are identical. + */ + private static final VarHandle sNextInsertSeq; + private volatile long mNextInsertSeqValue = 0; + /* + * The exception to the FIFO order rule is sendMessageAtFrontOfQueue(). + * Those messages must be in LIFO order. + * Decrements on each front of queue insert. + */ + private static final VarHandle sNextFrontInsertSeq; + private volatile long mNextFrontInsertSeqValue = -1; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sNextInsertSeq = l.findVarHandle(MessageQueue.class, "mNextInsertSeqValue", + long.class); + sNextFrontInsertSeq = l.findVarHandle(MessageQueue.class, "mNextFrontInsertSeqValue", + long.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + + } + + /* + * Tracks the number of queued and cancelled messages in our stack. + * + * On item cancellation, determine whether to wake next() to flush tombstoned messages. + * We track queued and cancelled counts as two ints packed into a single long. + */ + private static final class MessageCounts { + private static VarHandle sCounts; + private volatile long mCountsValue = 0; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sCounts = l.findVarHandle(MessageQueue.MessageCounts.class, "mCountsValue", + long.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + /* We use a special value to indicate when next() has been woken for flush. */ + private static final long AWAKE = Long.MAX_VALUE; + /* + * Minimum number of messages in the stack which we need before we consider flushing + * tombstoned items. + */ + private static final int MESSAGE_FLUSH_THRESHOLD = 10; + + private static int numQueued(long val) { + return (int) (val >>> Integer.SIZE); + } + + private static int numCancelled(long val) { + return (int) val; + } + + private static long combineCounts(int queued, int cancelled) { + return ((long) queued << Integer.SIZE) | (long) cancelled; + } + + public void incrementQueued() { + while (true) { + long oldVal = mCountsValue; + int queued = numQueued(oldVal); + int cancelled = numCancelled(oldVal); + /* Use Math.max() to avoid overflow of queued count */ + long newVal = combineCounts(Math.max(queued + 1, queued), cancelled); + + /* Don't overwrite 'AWAKE' state */ + if (oldVal == AWAKE || sCounts.compareAndSet(this, oldVal, newVal)) { + break; + } + } + } + + public boolean incrementCancelled() { + while (true) { + long oldVal = mCountsValue; + if (oldVal == AWAKE) { + return false; + } + int queued = numQueued(oldVal); + int cancelled = numCancelled(oldVal); + boolean needsPurge = queued > MESSAGE_FLUSH_THRESHOLD + && (queued >> 1) < cancelled; + long newVal; + if (needsPurge) { + newVal = AWAKE; + } else { + newVal = combineCounts(queued, + Math.max(cancelled + 1, cancelled)); + } + + if (sCounts.compareAndSet(this, oldVal, newVal)) { + return needsPurge; + } + } + } + + public void clearCounts() { + mCountsValue = 0; + } + } + + private final MessageCounts mMessageCounts = new MessageCounts(); + + private final Object mIdleHandlersLock = new Object(); + private final ArrayList mIdleHandlers = new ArrayList(); + private IdleHandler[] mPendingIdleHandlers; + + private final Object mFileDescriptorRecordsLock = new Object(); + private SparseArray mFileDescriptorRecords; + + private static final VarHandle sQuitting; + private boolean mQuittingValue = false; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + sQuitting = l.findVarHandle(MessageQueue.class, "mQuittingValue", boolean.class); + } catch (Exception e) { + Log.wtf(TAG, "VarHandle lookup failed with exception: " + e); + throw new ExceptionInInitializerError(e); + } + } + + // The next barrier token. + // Barriers are indicated by messages with a null target whose arg1 field carries the token. + private final AtomicInteger mNextBarrierToken = new AtomicInteger(1); + + MessageQueue(boolean quitAllowed) { + mQuitAllowed = quitAllowed; + mPtr = ShadowPausedMessageQueue.nativeInit(); + } + + private static void throwIfNotTest() { + throw new IllegalStateException("Test-only API called not from a test!"); + } + + private static void throwIfNotTest$ravenwood() { + return; + } + + @Override + protected void finalize() throws Throwable { + try { + dispose(); + } finally { + super.finalize(); + } + } + + // Disposes of the underlying message queue. + // Must only be called on the looper thread or the finalizer. + private void dispose() { + if (mPtr != 0) { + ShadowPausedMessageQueue.nativeDestroy(mPtr); + mPtr = 0; + } + } + + private static final class MatchDeliverableMessages extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (m.when <= when) { + return true; + } + return false; + } + } + private final MatchDeliverableMessages mMatchDeliverableMessages = + new MatchDeliverableMessages(); + /** + * Returns true if the looper has no pending messages which are due to be processed. + * + *

This method is safe to call from any thread. + * + * @return True if the looper is idle. + */ + public boolean isIdle() { + final long now = SystemClock.uptimeMillis(); + + if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) { + return false; + } + + MessageNode msgNode = null; + MessageNode asyncMsgNode = null; + + if (!mPriorityQueue.isEmpty()) { + try { + msgNode = mPriorityQueue.first(); + } catch (NoSuchElementException e) { } + } + + if (!mAsyncPriorityQueue.isEmpty()) { + try { + asyncMsgNode = mAsyncPriorityQueue.first(); + } catch (NoSuchElementException e) { } + } + + if ((msgNode != null && msgNode.getWhen() <= now) + || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) { + return false; + } + + return true; + } + + /* Protects mNextIsDrainingStack */ + private final ReentrantLock mDrainingLock = new ReentrantLock(); + private boolean mNextIsDrainingStack = false; + private final Condition mDrainCompleted = mDrainingLock.newCondition(); + + /** + * Add a new {@link IdleHandler} to this message queue. This may be + * removed automatically for you by returning false from + * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is + * invoked, or explicitly removing it with {@link #removeIdleHandler}. + * + *

This method is safe to call from any thread. + * + * @param handler The IdleHandler to be added. + */ + public void addIdleHandler(@NonNull IdleHandler handler) { + if (handler == null) { + throw new NullPointerException("Can't add a null IdleHandler"); + } + synchronized (mIdleHandlersLock) { + mIdleHandlers.add(handler); + } + } + + /** + * Remove an {@link IdleHandler} from the queue that was previously added + * with {@link #addIdleHandler}. If the given object is not currently + * in the idle list, nothing is done. + * + *

This method is safe to call from any thread. + * + * @param handler The IdleHandler to be removed. + */ + public void removeIdleHandler(@NonNull IdleHandler handler) { + synchronized (mIdleHandlersLock) { + mIdleHandlers.remove(handler); + } + } + + /** + * Returns whether this looper's thread is currently polling for more work to do. + * This is a good signal that the loop is still alive rather than being stuck + * handling a callback. Note that this method is intrinsically racy, since the + * state of the loop can change before you get the result back. + * + *

This method is safe to call from any thread. + * + * @return True if the looper is currently polling for events. + * @hide + */ + public boolean isPolling() { + // If the loop is quitting then it must not be idling. + // We can assume mPtr != 0 when sQuitting is false. + return !((boolean) sQuitting.getVolatile(this)) && ShadowPausedMessageQueue.nativeIsPolling(mPtr); + } + + /* Helper to choose the correct queue to insert into. */ + private void insertIntoPriorityQueue(MessageNode msgNode) { + if (msgNode.isAsync()) { + mAsyncPriorityQueue.add(msgNode); + } else { + mPriorityQueue.add(msgNode); + } + } + + private boolean removeFromPriorityQueue(MessageNode msgNode) { + if (msgNode.isAsync()) { + return mAsyncPriorityQueue.remove(msgNode); + } else { + return mPriorityQueue.remove(msgNode); + } + } + + private MessageNode pickEarliestNode(MessageNode nodeA, MessageNode nodeB) { + if (nodeA != null && nodeB != null) { + if (nodeA.compareTo(nodeB) < 0) { + return nodeA; + } + return nodeB; + } + + return nodeA != null ? nodeA : nodeB; + } + + private MessageNode iterateNext(Iterator iter) { + if (iter.hasNext()) { + try { + return iter.next(); + } catch (NoSuchElementException e) { + /* The queue is empty - this can happen if we race with remove */ + } + } + return null; + } + + /* Move any non-cancelled messages into the priority queue */ + private void drainStack(StackNode oldTop) { + while (oldTop.isMessageNode()) { + MessageNode oldTopMessageNode = (MessageNode) oldTop; + if (oldTopMessageNode.removeFromStack()) { + insertIntoPriorityQueue(oldTopMessageNode); + } + MessageNode inserted = oldTopMessageNode; + oldTop = oldTopMessageNode.mNext; + /* + * removeMessages can walk this list while we are consuming it. + * Set our next pointer to null *after* we add the message to our + * priority queue. This way removeMessages() will always find the + * message, either in our list or in the priority queue. + */ + inserted.mNext = null; + } + } + + /* Set the stack state to Active, return a list of nodes to walk. */ + private StackNode swapAndSetStackStateActive() { + while (true) { + /* Set stack state to Active, get node list to walk later */ + StackNode current = (StackNode) sState.getVolatile(this); + if (current == sStackStateActive + || sState.compareAndSet(this, current, sStackStateActive)) { + return current; + } + } + } + + /* This is only read/written from the Looper thread */ + private int mNextPollTimeoutMillis; + private static final AtomicLong mMessagesDelivered = new AtomicLong(); + private boolean mMessageDirectlyQueued; + + private Message nextMessage() { + return nextMessage(false); // TODO: Suwayomi this was not given, no idea how that works + } + + private Message nextMessage(boolean peek) { + int i = 0; + + while (true) { + if (DEBUG) { + Log.d(TAG, "nextMessage loop #" + i); + i++; + } + + mDrainingLock.lock(); + mNextIsDrainingStack = true; + mDrainingLock.unlock(); + + /* + * Set our state to active, drain any items from the stack into our priority queues + */ + StackNode oldTop; + oldTop = swapAndSetStackStateActive(); + drainStack(oldTop); + + mDrainingLock.lock(); + mNextIsDrainingStack = false; + mDrainCompleted.signalAll(); + mDrainingLock.unlock(); + + /* + * The objective of this next block of code is to: + * - find a message to return (if any is ready) + * - find a next message we would like to return, after scheduling. + * - we make our scheduling decision based on this next message (if it exists). + * + * We have two queues to juggle and the presence of barriers throws an additional + * wrench into our plans. + * + * The last wrinkle is that remove() may delete items from underneath us. If we hit + * that case, we simply restart the loop. + */ + + /* Get the first node from each queue */ + Iterator queueIter = mPriorityQueue.iterator(); + MessageNode msgNode = iterateNext(queueIter); + Iterator asyncQueueIter = mAsyncPriorityQueue.iterator(); + MessageNode asyncMsgNode = iterateNext(asyncQueueIter); + + if (DEBUG) { + if (msgNode != null) { + Message msg = msgNode.mMessage; + Log.d(TAG, "Next found node what: " + msg.what + " when: " + msg.when + + " seq: " + msgNode.mInsertSeq + "barrier: " + + msgNode.isBarrier() + " now: " + SystemClock.uptimeMillis()); + } + if (asyncMsgNode != null) { + Message msg = asyncMsgNode.mMessage; + Log.d(TAG, "Next found async node what: " + msg.what + " when: " + msg.when + + " seq: " + asyncMsgNode.mInsertSeq + "barrier: " + + asyncMsgNode.isBarrier() + " now: " + + SystemClock.uptimeMillis()); + } + } + + /* + * the node which we will return, null if none are ready + */ + MessageNode found = null; + /* + * The node from which we will determine our next wakeup time. + * Null indicates there is no next message ready. If we found a node, + * we can leave this null as Looper will call us again after delivering + * the message. + */ + MessageNode next = null; + + long now = SystemClock.uptimeMillis(); + /* + * If we have a barrier we should return the async node (if it exists and is ready) + */ + if (msgNode != null && msgNode.isBarrier()) { + if (asyncMsgNode != null && now >= asyncMsgNode.getWhen()) { + found = asyncMsgNode; + } else { + next = asyncMsgNode; + } + } else { /* No barrier. */ + MessageNode earliest; + /* + * If we have two messages, pick the earliest option from either queue. + * Otherwise grab whichever node is non-null. If both are null we'll fall through. + */ + earliest = pickEarliestNode(msgNode, asyncMsgNode); + + if (earliest != null) { + if (now >= earliest.getWhen()) { + found = earliest; + } else { + next = earliest; + } + } + } + + if (DEBUG) { + if (found != null) { + Message msg = found.mMessage; + Log.d(TAG, "Will deliver node what: " + msg.what + " when: " + msg.when + + " seq: " + found.mInsertSeq + " barrier: " + found.isBarrier() + + " async: " + found.isAsync() + " now: " + + SystemClock.uptimeMillis()); + } else { + Log.d(TAG, "No node to deliver"); + } + if (next != null) { + Message msg = next.mMessage; + Log.d(TAG, "Next node what: " + msg.what + " when: " + msg.when + " seq: " + + next.mInsertSeq + " barrier: " + next.isBarrier() + " async: " + + next.isAsync() + + " now: " + SystemClock.uptimeMillis()); + } else { + Log.d(TAG, "No next node"); + } + } + + /* + * If we have a found message, we will get called again so there's no need to set state. + * In that case we can leave our state as ACTIVE. + * + * Otherwise we should determine how to park the thread. + */ + StateNode nextOp = sStackStateActive; + if (found == null) { + if (next == null) { + /* No message to deliver, sleep indefinitely */ + mNextPollTimeoutMillis = -1; + nextOp = sStackStateParked; + if (DEBUG) { + Log.d(TAG, "nextMessage next state is StackStateParked"); + } + } else { + /* Message not ready, or we found one to deliver already, set a timeout */ + long nextMessageWhen = next.getWhen(); + if (nextMessageWhen > now) { + mNextPollTimeoutMillis = (int) Math.min(nextMessageWhen - now, + Integer.MAX_VALUE); + } else { + mNextPollTimeoutMillis = 0; + } + + mStackStateTimedPark.mWhenToWake = now + mNextPollTimeoutMillis; + nextOp = mStackStateTimedPark; + if (DEBUG) { + Log.d(TAG, "nextMessage next state is StackStateTimedParked timeout ms " + + mNextPollTimeoutMillis + " mWhenToWake: " + + mStackStateTimedPark.mWhenToWake + " now " + now); + } + } + } + + /* + * Try to swap our state from Active back to Park or TimedPark. If we raced with + * enqueue, loop back around to pick up any new items. + */ + if (sState.compareAndSet(this, sStackStateActive, nextOp)) { + mMessageCounts.clearCounts(); + if (found != null) { + if (!peek && !removeFromPriorityQueue(found)) { + /* + * RemoveMessages() might be able to pull messages out from under us + * However we can detect that here and just loop around if it happens. + */ + continue; + } + + if (TRACE) { + Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet()); + } + return found.mMessage; + } + return null; + } + } + } + + Message next() { + final long ptr = mPtr; + if (ptr == 0) { + return null; + } + + mNextPollTimeoutMillis = 0; + int pendingIdleHandlerCount = -1; // -1 only during first iteration + while (true) { + if (mNextPollTimeoutMillis != 0) { + // Binder.flushPendingCommands(); + } + + mMessageDirectlyQueued = false; + ShadowPausedMessageQueue.nativePollOnce(ptr, mNextPollTimeoutMillis); + + Message msg = nextMessage(); + if (msg != null) { + msg.markInUse(); + return msg; + } + + if ((boolean) sQuitting.getVolatile(this)) { + return null; + } + + synchronized (mIdleHandlersLock) { + // If first time idle, then get the number of idlers to run. + // Idle handles only run if the queue is empty or if the first message + // in the queue (possibly a barrier) is due to be handled in the future. + if (pendingIdleHandlerCount < 0 + && isIdle()) { + pendingIdleHandlerCount = mIdleHandlers.size(); + } + if (pendingIdleHandlerCount <= 0) { + // No idle handlers to run. Loop and wait some more. + continue; + } + + if (mPendingIdleHandlers == null) { + mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; + } + mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); + } + + // Run the idle handlers. + // We only ever reach this code block during the first iteration. + for (int i = 0; i < pendingIdleHandlerCount; i++) { + final IdleHandler idler = mPendingIdleHandlers[i]; + mPendingIdleHandlers[i] = null; // release the reference to the handler + + boolean keep = false; + try { + keep = idler.queueIdle(); + } catch (Throwable t) { + Log.wtf(TAG, "IdleHandler threw exception", t); + } + + if (!keep) { + synchronized (mIdleHandlersLock) { + mIdleHandlers.remove(idler); + } + } + } + + // Reset the idle handler count to 0 so we do not run them again. + pendingIdleHandlerCount = 0; + + // While calling an idle handler, a new message could have been delivered + // so go back and look again for a pending message without waiting. + mNextPollTimeoutMillis = 0; + } + } + + void quit(boolean safe) { + if (!mQuitAllowed) { + throw new IllegalStateException("Main thread not allowed to quit."); + } + synchronized (mIdleHandlersLock) { + if (sQuitting.compareAndSet(this, false, true)) { + if (safe) { + removeAllFutureMessages(); + } else { + removeAllMessages(); + } + + // We can assume mPtr != 0 because sQuitting was previously false. + ShadowPausedMessageQueue.nativeWake(mPtr); + } + } + } + + boolean enqueueMessage(@NonNull Message msg, long when) { + if (msg.target == null) { + throw new IllegalArgumentException("Message must have a target."); + } + + if (msg.isInUse()) { + throw new IllegalStateException(msg + " This message is already in use."); + } + + return enqueueMessageUnchecked(msg, when); + } + + private boolean enqueueMessageUnchecked(@NonNull Message msg, long when) { + if ((boolean) sQuitting.getVolatile(this)) { + IllegalStateException e = new IllegalStateException( + msg.target + " sending message to a Handler on a dead thread"); + Log.w(TAG, e.getMessage(), e); + msg.recycleUnchecked(); + return false; + } + + long seq = when != 0 ? ((long)sNextInsertSeq.getAndAdd(this, 1L) + 1L) + : ((long)sNextFrontInsertSeq.getAndAdd(this, -1L) - 1L); + /* TODO: Add a MessageNode member to Message so we can avoid this allocation */ + MessageNode node = new MessageNode(msg, seq); + msg.when = when; + msg.markInUse(); + + if (DEBUG) { + Log.d(TAG, "Insert message what: " + msg.what + " when: " + msg.when + " seq: " + + node.mInsertSeq + " barrier: " + node.isBarrier() + " async: " + + node.isAsync() + " now: " + SystemClock.uptimeMillis()); + } + + final Looper myLooper = Looper.myLooper(); + /* If we are running on the looper thread we can add directly to the priority queue */ + if (myLooper != null && myLooper.getQueue() == this) { + node.removeFromStack(); + insertIntoPriorityQueue(node); + /* + * We still need to do this even though we are the current thread, + * otherwise next() may sleep indefinitely. + */ + if (!mMessageDirectlyQueued) { + mMessageDirectlyQueued = true; + ShadowPausedMessageQueue.nativeWake(mPtr); + } + return true; + } + + while (true) { + StackNode old = (StackNode) sState.getVolatile(this); + boolean wakeNeeded; + boolean inactive; + + node.mNext = old; + switch (old.getNodeType()) { + case STACK_NODE_ACTIVE: + /* + * The worker thread is currently active and will process any elements added to + * the stack before parking again. + */ + node.mBottomOfStack = (StateNode) old; + inactive = false; + node.mWokeUp = true; + wakeNeeded = false; + break; + + case STACK_NODE_PARKED: + node.mBottomOfStack = (StateNode) old; + inactive = true; + node.mWokeUp = true; + wakeNeeded = true; + break; + + case STACK_NODE_TIMEDPARK: + node.mBottomOfStack = (StateNode) old; + inactive = true; + wakeNeeded = mStackStateTimedPark.mWhenToWake >= node.getWhen(); + node.mWokeUp = wakeNeeded; + break; + + default: + MessageNode oldMessage = (MessageNode) old; + + node.mBottomOfStack = oldMessage.mBottomOfStack; + int bottomType = node.mBottomOfStack.getNodeType(); + inactive = bottomType >= STACK_NODE_PARKED; + wakeNeeded = (bottomType == STACK_NODE_TIMEDPARK + && mStackStateTimedPark.mWhenToWake >= node.getWhen() + && !oldMessage.mWokeUp); + node.mWokeUp = oldMessage.mWokeUp || wakeNeeded; + break; + } + if (sState.compareAndSet(this, old, node)) { + if (inactive) { + if (wakeNeeded) { + ShadowPausedMessageQueue.nativeWake(mPtr); + } else { + mMessageCounts.incrementQueued(); + } + } + return true; + } + } + } + + /** + * Posts a synchronization barrier to the Looper's message queue. + * + * Message processing occurs as usual until the message queue encounters the + * synchronization barrier that has been posted. When the barrier is encountered, + * later synchronous messages in the queue are stalled (prevented from being executed) + * until the barrier is released by calling {@link #removeSyncBarrier} and specifying + * the token that identifies the synchronization barrier. + * + * This method is used to immediately postpone execution of all subsequently posted + * synchronous messages until a condition is met that releases the barrier. + * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier + * and continue to be processed as usual. + * + * This call must be always matched by a call to {@link #removeSyncBarrier} with + * the same token to ensure that the message queue resumes normal operation. + * Otherwise the application will probably hang! + * + * @return A token that uniquely identifies the barrier. This token must be + * passed to {@link #removeSyncBarrier} to release the barrier. + * + * @hide + */ + @TestApi + public int postSyncBarrier() { + return postSyncBarrier(SystemClock.uptimeMillis()); + } + + private int postSyncBarrier(long when) { + final int token = mNextBarrierToken.getAndIncrement(); + final Message msg = Message.obtain(); + + msg.markInUse(); + msg.arg1 = token; + + if (!enqueueMessageUnchecked(msg, when)) { + Log.wtf(TAG, "Unexpected error while adding sync barrier!"); + return -1; + } + + return token; + } + + private static final class MatchBarrierToken extends MessageCompare { + int mBarrierToken; + + MatchBarrierToken(int token) { + super(); + mBarrierToken = token; + } + + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (m.target == null && m.arg1 == mBarrierToken) { + return true; + } + return false; + } + } + + /** + * Removes a synchronization barrier. + * + * @param token The synchronization barrier token that was returned by + * {@link #postSyncBarrier}. + * + * @throws IllegalStateException if the barrier was not found. + * + * @hide + */ + @TestApi + public void removeSyncBarrier(int token) { + boolean removed; + MessageNode first; + final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token); + + try { + /* Retain the first element to see if we are currently stuck on a barrier. */ + first = mPriorityQueue.first(); + } catch (NoSuchElementException e) { + /* The queue is empty */ + first = null; + } + + removed = findOrRemoveMessages(null, 0, null, null, 0, matchBarrierToken, true); + if (removed && first != null) { + Message m = first.mMessage; + if (m.target == null && m.arg1 == token) { + /* Wake up next() in case it was sleeping on this barrier. */ + ShadowPausedMessageQueue.nativeWake(mPtr); + } + } else if (!removed) { + throw new IllegalStateException("The specified message queue synchronization " + + " barrier token has not been posted or has already been removed."); + } + } + + private static final class MatchEarliestMessage extends MessageCompare { + MessageNode mEarliest = null; + + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (mEarliest == null || mEarliest.mMessage.when > m.when) { + mEarliest = n; + } + + return false; + } + } + + /** + * Get the timestamp of the next executable message in our priority queue. + * Returns null if there are no messages ready for delivery. + * + * Caller must ensure that this doesn't race 'next' from the Looper thread. + */ + Long peekWhenForTest() { + throwIfNotTest(); + Message ret = nextMessage(true); + return ret != null ? ret.when : null; + } + + /** + * Return the next executable message in our priority queue. + * Returns null if there are no messages ready for delivery + * + * Caller must ensure that this doesn't race 'next' from the Looper thread. + */ + Message pollForTest() { + throwIfNotTest(); + return nextMessage(false); + } + + /** + * @return true if we are blocked on a sync barrier + */ + boolean isBlockedOnSyncBarrier() { + throwIfNotTest(); + Iterator queueIter = mPriorityQueue.iterator(); + MessageNode queueNode = iterateNext(queueIter); + + if (queueNode.isBarrier()) { + long now = SystemClock.uptimeMillis(); + + /* Look for a deliverable async node. If one exists we are not blocked. */ + Iterator asyncQueueIter = mAsyncPriorityQueue.iterator(); + MessageNode asyncNode = iterateNext(asyncQueueIter); + if (asyncNode != null && now >= asyncNode.getWhen()) { + return false; + } + /* + * Look for a deliverable sync node. In this case, if one exists we are blocked + * since the barrier prevents delivery of the Message. + */ + while (queueNode.isBarrier()) { + queueNode = iterateNext(queueIter); + } + if (queueNode != null && now >= queueNode.getWhen()) { + return true; + } + } + + // TODO: Suwayomi moved this outside if, there was no return? + return false; + } + + private StateNode getStateNode(StackNode node) { + if (node.isMessageNode()) { + return ((MessageNode) node).mBottomOfStack; + } + return (StateNode) node; + } + + private void waitForDrainCompleted() { + mDrainingLock.lock(); + while (mNextIsDrainingStack) { + mDrainCompleted.awaitUninterruptibly(); + } + mDrainingLock.unlock(); + } + + /* + * This class is used to find matches for hasMessages() and removeMessages() + */ + private abstract static class MessageCompare { + public abstract boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when); + } + + private boolean stackHasMessages(Handler h, int what, Object object, Runnable r, long when, + MessageCompare compare, boolean removeMatches) { + boolean found = false; + StackNode top = (StackNode) sState.getVolatile(this); + StateNode bottom = getStateNode(top); + + /* + * If the top node is a state node, there are no reachable messages. + * If it's anything other than Active, we can quit as we know that next() is not + * consuming items. + * If the top node is Active then we know that next() is currently consuming items. + * In that case we should wait next() has drained the stack. + */ + if (top == bottom) { + if (bottom != sStackStateActive) { + return false; + } + waitForDrainCompleted(); + return false; + } + + /* + * We have messages that we may tombstone. Walk the stack until we hit the bottom or we + * hit a null pointer. + * If we hit the bottom, we are done. + * If we hit a null pointer, then the stack is being consumed by next() and we must cycle + * until the stack has been drained. + */ + MessageNode p = (MessageNode) top; + + while (true) { + if (compare.compareMessage(p, h, what, object, r, when)) { + found = true; + if (DEBUG) { + Log.w(TAG, "stackHasMessages node matches"); + } + if (removeMatches) { + if (p.removeFromStack()) { + p.mMessage.recycleUnchecked(); + if (mMessageCounts.incrementCancelled()) { + ShadowPausedMessageQueue.nativeWake(mPtr); + } + } + } else { + return true; + } + } + + StackNode n = p.mNext; + if (n == null) { + /* Next() is walking the stack, we must re-sample */ + if (DEBUG) { + Log.d(TAG, "stackHasMessages next() is walking the stack, we must re-sample"); + } + waitForDrainCompleted(); + break; + } + if (!n.isMessageNode()) { + /* We reached the end of the stack */ + return found; + } + p = (MessageNode) n; + } + + return found; + } + + private boolean priorityQueueHasMessage(ConcurrentSkipListSet queue, Handler h, + int what, Object object, Runnable r, long when, MessageCompare compare, + boolean removeMatches) { + Iterator iterator = queue.iterator(); + boolean found = false; + + while (iterator.hasNext()) { + MessageNode msg = iterator.next(); + + if (compare.compareMessage(msg, h, what, object, r, when)) { + if (removeMatches) { + found = true; + if (queue.remove(msg)) { + msg.mMessage.recycleUnchecked(); + } + } else { + return true; + } + } + } + return found; + } + + private boolean findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when, + MessageCompare compare, boolean removeMatches) { + boolean foundInStack, foundInQueue; + + foundInStack = stackHasMessages(h, what, object, r, when, compare, removeMatches); + foundInQueue = priorityQueueHasMessage(mPriorityQueue, h, what, object, r, when, compare, + removeMatches); + foundInQueue |= priorityQueueHasMessage(mAsyncPriorityQueue, h, what, object, r, when, + compare, removeMatches); + + return foundInStack || foundInQueue; + } + + private static final class MatchHandlerWhatAndObject extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (m.target == h && m.what == what && (object == null || m.obj == object)) { + return true; + } + return false; + } + } + private final MatchHandlerWhatAndObject mMatchHandlerWhatAndObject = + new MatchHandlerWhatAndObject(); + boolean hasMessages(Handler h, int what, Object object) { + if (h == null) { + return false; + } + + return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, false); + } + + private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, + long when) { + final Message m = n.mMessage; + if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) { + return true; + } + return false; + } + } + private final MatchHandlerWhatAndObjectEquals mMatchHandlerWhatAndObjectEquals = + new MatchHandlerWhatAndObjectEquals(); + boolean hasEqualMessages(Handler h, int what, Object object) { + if (h == null) { + return false; + } + + return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, + false); + } + + private static final class MatchHandlerRunnableAndObject extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (m.target == h && m.callback == r && (object == null || m.obj == object)) { + return true; + } + return false; + } + } + private final MatchHandlerRunnableAndObject mMatchHandlerRunnableAndObject = + new MatchHandlerRunnableAndObject(); + + boolean hasMessages(Handler h, Runnable r, Object object) { + if (h == null) { + return false; + } + + return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, false); + } + + private static final class MatchHandler extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (m.target == h) { + return true; + } + return false; + } + } + private final MatchHandler mMatchHandler = new MatchHandler(); + boolean hasMessages(Handler h) { + if (h == null) { + return false; + } + return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false); + } + + void removeMessages(Handler h, int what, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true); + } + + void removeEqualMessages(Handler h, int what, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true); + } + + void removeMessages(Handler h, Runnable r, Object object) { + if (h == null || r == null) { + return; + } + findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true); + } + + private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) { + return true; + } + return false; + } + } + private final MatchHandlerRunnableAndObjectEquals mMatchHandlerRunnableAndObjectEquals = + new MatchHandlerRunnableAndObjectEquals(); + void removeEqualMessages(Handler h, Runnable r, Object object) { + if (h == null || r == null) { + return; + } + findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true); + } + + private static final class MatchHandlerAndObject extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (m.target == h && (object == null || m.obj == object)) { + return true; + } + return false; + } + } + private final MatchHandlerAndObject mMatchHandlerAndObject = new MatchHandlerAndObject(); + void removeCallbacksAndMessages(Handler h, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true); + } + + private static final class MatchHandlerAndObjectEquals extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (m.target == h && (object == null || object.equals(m.obj))) { + return true; + } + return false; + } + } + private final MatchHandlerAndObjectEquals mMatchHandlerAndObjectEquals = + new MatchHandlerAndObjectEquals(); + void removeCallbacksAndEqualMessages(Handler h, Object object) { + if (h == null) { + return; + } + findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true); + } + + private static final class MatchAllMessages extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + return true; + } + } + private final MatchAllMessages mMatchAllMessages = new MatchAllMessages(); + private void removeAllMessages() { + findOrRemoveMessages(null, -1, null, null, 0, mMatchAllMessages, true); + } + + private static final class MatchAllFutureMessages extends MessageCompare { + @Override + public boolean compareMessage(MessageNode n, Handler h, int what, Object object, + Runnable r, long when) { + final Message m = n.mMessage; + if (m.when > when) { + return true; + } + return false; + } + } + private final MatchAllFutureMessages mMatchAllFutureMessages = new MatchAllFutureMessages(); + private void removeAllFutureMessages() { + findOrRemoveMessages(null, -1, null, null, SystemClock.uptimeMillis(), + mMatchAllFutureMessages, true); + } + + private void printPriorityQueueNodes() { + Iterator iterator = mPriorityQueue.iterator(); + + Log.d(TAG, "* Dump priority queue"); + while (iterator.hasNext()) { + MessageNode msgNode = iterator.next(); + Log.d(TAG, "** MessageNode what: " + msgNode.mMessage.what + " when " + + msgNode.mMessage.when + " seq: " + msgNode.mInsertSeq); + } + } + + private int dumpPriorityQueue(ConcurrentSkipListSet queue, Printer pw, + String prefix, Handler h, int n) { + int count = 0; + long now = SystemClock.uptimeMillis(); + + for (MessageNode msgNode : queue) { + Message msg = msgNode.mMessage; + if (h == null || h == msg.target) { + pw.println(prefix + "Message " + (n + count) + ": " + msg.toString(now)); + } + count++; + } + return count; + } + + /** + * Adds a file descriptor listener to receive notification when file descriptor + * related events occur. + *

+ * If the file descriptor has already been registered, the specified events + * and listener will replace any that were previously associated with it. + * It is not possible to set more than one listener per file descriptor. + *

+ * It is important to always unregister the listener when the file descriptor + * is no longer of use. + *

+ * + * @param fd The file descriptor for which a listener will be registered. + * @param events The set of events to receive: a combination of the + * {@link OnFileDescriptorEventListener#EVENT_INPUT}, + * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and + * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks. If the requested + * set of events is zero, then the listener is unregistered. + * @param listener The listener to invoke when file descriptor events occur. + * + * @see OnFileDescriptorEventListener + * @see #removeOnFileDescriptorEventListener + */ + public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd, + @OnFileDescriptorEventListener.Events int events, + @NonNull OnFileDescriptorEventListener listener) { + if (fd == null) { + throw new IllegalArgumentException("fd must not be null"); + } + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mFileDescriptorRecordsLock) { + updateOnFileDescriptorEventListenerLocked(fd, events, listener); + } + } + + /** + * Removes a file descriptor listener. + *

+ * This method does nothing if no listener has been registered for the + * specified file descriptor. + *

+ * + * @param fd The file descriptor whose listener will be unregistered. + * + * @see OnFileDescriptorEventListener + * @see #addOnFileDescriptorEventListener + */ + public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) { + if (fd == null) { + throw new IllegalArgumentException("fd must not be null"); + } + + synchronized (mFileDescriptorRecordsLock) { + updateOnFileDescriptorEventListenerLocked(fd, 0, null); + } + } + + private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, + OnFileDescriptorEventListener listener) { + throw new RuntimeException("Stub!"); + // final int fdNum = fd.getInt$(); + + // int index = -1; + // FileDescriptorRecord record = null; + // if (mFileDescriptorRecords != null) { + // index = mFileDescriptorRecords.indexOfKey(fdNum); + // if (index >= 0) { + // record = mFileDescriptorRecords.valueAt(index); + // if (record != null && record.mEvents == events) { + // return; + // } + // } + // } + + // if (events != 0) { + // events |= OnFileDescriptorEventListener.EVENT_ERROR; + // if (record == null) { + // if (mFileDescriptorRecords == null) { + // mFileDescriptorRecords = new SparseArray(); + // } + // record = new FileDescriptorRecord(fd, events, listener); + // mFileDescriptorRecords.put(fdNum, record); + // } else { + // record.mListener = listener; + // record.mEvents = events; + // record.mSeq += 1; + // } + // nativeSetFileDescriptorEvents(mPtr, fdNum, events); + // } else if (record != null) { + // record.mEvents = 0; + // mFileDescriptorRecords.removeAt(index); + // nativeSetFileDescriptorEvents(mPtr, fdNum, 0); + // } + } + + // Called from native code. + private int dispatchEvents(int fd, int events) { + // Get the file descriptor record and any state that might change. + final FileDescriptorRecord record; + final int oldWatchedEvents; + final OnFileDescriptorEventListener listener; + final int seq; + synchronized (mFileDescriptorRecordsLock) { + record = mFileDescriptorRecords.get(fd); + if (record == null) { + return 0; // spurious, no listener registered + } + + oldWatchedEvents = record.mEvents; + events &= oldWatchedEvents; // filter events based on current watched set + if (events == 0) { + return oldWatchedEvents; // spurious, watched events changed + } + + listener = record.mListener; + seq = record.mSeq; + } + + // Invoke the listener outside of the lock. + int newWatchedEvents = listener.onFileDescriptorEvents( + record.mDescriptor, events); + if (newWatchedEvents != 0) { + newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR; + } + + // Update the file descriptor record if the listener changed the set of + // events to watch and the listener itself hasn't been updated since. + if (newWatchedEvents != oldWatchedEvents) { + synchronized (mFileDescriptorRecordsLock) { + int index = mFileDescriptorRecords.indexOfKey(fd); + if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record + && record.mSeq == seq) { + record.mEvents = newWatchedEvents; + if (newWatchedEvents == 0) { + mFileDescriptorRecords.removeAt(index); + } + } + } + } + + // Return the new set of events to watch for native code to take care of. + return newWatchedEvents; + } + + /** + * Callback interface for discovering when a thread is going to block + * waiting for more messages. + */ + public static interface IdleHandler { + /** + * Called when the message queue has run out of messages and will now + * wait for more. Return true to keep your idle handler active, false + * to have it removed. This may be called if there are still messages + * pending in the queue, but they are all scheduled to be dispatched + * after the current time. + */ + boolean queueIdle(); + } + + /** + * A listener which is invoked when file descriptor related events occur. + */ + public interface OnFileDescriptorEventListener { + /** + * File descriptor event: Indicates that the file descriptor is ready for input + * operations, such as reading. + *

+ * The listener should read all available data from the file descriptor + * then return true to keep the listener active or false + * to remove the listener. + *

+ * In the case of a socket, this event may be generated to indicate + * that there is at least one incoming connection that the listener + * should accept. + *

+ * This event will only be generated if the {@link #EVENT_INPUT} event mask was + * specified when the listener was added. + *

+ */ + public static final int EVENT_INPUT = 1 << 0; + + /** + * File descriptor event: Indicates that the file descriptor is ready for output + * operations, such as writing. + *

+ * The listener should write as much data as it needs. If it could not + * write everything at once, then it should return true to + * keep the listener active. Otherwise, it should return false + * to remove the listener then re-register it later when it needs to write + * something else. + *

+ * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was + * specified when the listener was added. + *

+ */ + public static final int EVENT_OUTPUT = 1 << 1; + + /** + * File descriptor event: Indicates that the file descriptor encountered a + * fatal error. + *

+ * File descriptor errors can occur for various reasons. One common error + * is when the remote peer of a socket or pipe closes its end of the connection. + *

+ * This event may be generated at any time regardless of whether the + * {@link #EVENT_ERROR} event mask was specified when the listener was added. + *

+ */ + public static final int EVENT_ERROR = 1 << 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "EVENT_" }, value = { + EVENT_INPUT, + EVENT_OUTPUT, + EVENT_ERROR + }) + public @interface Events {} + + /** + * Called when a file descriptor receives events. + * + * @param fd The file descriptor. + * @param events The set of events that occurred: a combination of the + * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks. + * @return The new set of events to watch, or 0 to unregister the listener. + * + * @see #EVENT_INPUT + * @see #EVENT_OUTPUT + * @see #EVENT_ERROR + */ + @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events); + } + + static final class FileDescriptorRecord { + public final FileDescriptor mDescriptor; + public int mEvents; + public OnFileDescriptorEventListener mListener; + public int mSeq; + + public FileDescriptorRecord(FileDescriptor descriptor, + int events, OnFileDescriptorEventListener listener) { + mDescriptor = descriptor; + mEvents = events; + mListener = listener; + } + } +} diff --git a/AndroidCompat/src/main/java/android/os/ThreadLocalWorkSource.java b/AndroidCompat/src/main/java/android/os/ThreadLocalWorkSource.java new file mode 100644 index 00000000..e9adb209 --- /dev/null +++ b/AndroidCompat/src/main/java/android/os/ThreadLocalWorkSource.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2018 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.os; + +/** + * Tracks who triggered the work currently executed on this thread. + * + *

ThreadLocalWorkSource is automatically updated inside system server for incoming/outgoing + * binder calls and messages posted to handler threads. + * + *

ThreadLocalWorkSource can also be set manually if needed to refine the WorkSource. + * + *

Example: + *

    + *
  • Bluetooth process calls {@link PowerManager#isInteractive()} API on behalf of app foo. + *
  • ThreadLocalWorkSource will be automatically set to the UID of foo. + *
  • Any code on the thread handling {@link PowerManagerService#isInteractive()} can call + * {@link ThreadLocalWorkSource#getUid()} to blame any resource used to handle this call. + *
  • If a message is posted from the binder thread, the code handling the message can also call + * {@link ThreadLocalWorkSource#getUid()} and it will return the UID of foo since the work source is + * automatically propagated. + *
+ * + * @hide Only for use within system server. + */ +public final class ThreadLocalWorkSource { + public static final int UID_NONE = Message.UID_NONE; + private static final ThreadLocal sWorkSourceUid = + ThreadLocal.withInitial(() -> new int[] {UID_NONE}); + + /** + * Returns the UID to blame for the code currently executed on this thread. + * + *

This UID is set automatically by common frameworks (e.g. Binder and Handler frameworks) + * and automatically propagated inside system server. + *

It can also be set manually using {@link #setUid(int)}. + */ + public static int getUid() { + return sWorkSourceUid.get()[0]; + } + + /** + * Sets the UID to blame for the code currently executed on this thread. + * + *

Inside system server, this UID will be automatically propagated. + *

It will be used to attribute future resources used on this thread (e.g. binder + * transactions or processing handler messages) and on any other threads the UID is propagated + * to. + * + * @return a token that can be used to restore the state. + */ + public static long setUid(int uid) { + final long token = getToken(); + sWorkSourceUid.get()[0] = uid; + return token; + } + + /** + * Restores the state using the provided token. + */ + public static void restore(long token) { + sWorkSourceUid.get()[0] = parseUidFromToken(token); + } + + /** + * Clears the stored work source uid. + * + *

This method should be used when we do not know who to blame. If the UID to blame is the + * UID of the current process, it is better to attribute the work to the current process + * explicitly instead of clearing the work source: + * + *

+     * ThreadLocalWorkSource.setUid(Process.myUid());
+     * 
+ * + * @return a token that can be used to restore the state. + */ + public static long clear() { + return setUid(UID_NONE); + } + + private static int parseUidFromToken(long token) { + return (int) token; + } + + private static long getToken() { + return sWorkSourceUid.get()[0]; + } + + private ThreadLocalWorkSource() { + } +} diff --git a/AndroidCompat/src/main/java/android/os/shadows/NativeObjRegistry.java b/AndroidCompat/src/main/java/android/os/shadows/NativeObjRegistry.java new file mode 100644 index 00000000..343805dc --- /dev/null +++ b/AndroidCompat/src/main/java/android/os/shadows/NativeObjRegistry.java @@ -0,0 +1,168 @@ +package android.os.shadows; +// package org.robolectric.res.android; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A unique id per object registry. Used to emulate android platform behavior of storing a long + * which represents a pointer to an object. + */ +public class NativeObjRegistry { + + private static final int INITIAL_ID = 1; + + private final String name; + private final boolean debug; + private final HashMap nativeObjToIdMap = new HashMap(); + private final Map idToDebugInfoMap; + + private long nextId = INITIAL_ID; + + public NativeObjRegistry(Class theClass) { + this(theClass, false); + } + + public NativeObjRegistry(Class theClass, boolean debug) { + this(theClass.getSimpleName(), debug); + } + + public NativeObjRegistry(String name) { + this(name, false); + } + + public NativeObjRegistry(String name, boolean debug) { + this.name = name; + this.debug = debug; + this.idToDebugInfoMap = debug ? new HashMap<>() : null; + } + + private Long getNativeObjectId(T o) { + for(Map.Entry entry : nativeObjToIdMap.entrySet()) { + if (o == entry.getValue()) + return entry.getKey(); + } + return null; + } + + /** + * Register and assign a new unique native id for given object (representing a C memory pointer). + * + * @throws IllegalStateException if the object was previously registered + */ + public synchronized long register(T o) { + if (o == null) + throw new IllegalStateException("Object must not be null"); + Long nativeId = getNativeObjectId(o); + if (nativeId != null) { + if (debug) { + DebugInfo debugInfo = idToDebugInfoMap.get(nativeId); + if (debugInfo != null) { + System.out.printf( + "NativeObjRegistry %s: register %d -> %s already registered:%n", name, nativeId, o); + debugInfo.registrationTrace.printStackTrace(System.out); + } + } + throw new IllegalStateException("Object was previously registered with id " + nativeId); + } + + nativeId = nextId; + if (debug) { + System.out.printf("NativeObjRegistry %s: register %d -> %s%n", name, nativeId, o); + idToDebugInfoMap.put(nativeId, new DebugInfo(new Trace())); + } + nativeObjToIdMap.put(nativeId, o); + nextId++; + return nativeId; + } + + /** + * Unregister an object previously registered with {@link #register(Object)}. + * + * @param nativeId the unique id (representing a C memory pointer) of the object to unregister. + * @throws IllegalStateException if the object was never registered, or was previously + * unregistered. + */ + public synchronized T unregister(long nativeId) { + T o = nativeObjToIdMap.remove(nativeId); + if (debug) { + System.out.printf("NativeObjRegistry %s: unregister %d -> %s%n", name, nativeId, o); + new RuntimeException("unregister debug").printStackTrace(System.out); + } + if (o == null) { + if (debug) { + DebugInfo debugInfo = idToDebugInfoMap.get(nativeId); + debugInfo.unregistrationTraces.add(new Trace()); + if (debugInfo.unregistrationTraces.size() > 1) { + System.out.format("NativeObjRegistry %s: Too many unregistrations:%n", name); + for (Trace unregistration : debugInfo.unregistrationTraces) { + unregistration.printStackTrace(System.out); + } + } + } + throw new IllegalStateException( + nativeId + " has already been removed (or was never registered)"); + } + return o; + } + + /** Retrieve the native object for given id. Throws if object with that id cannot be found */ + public synchronized T getNativeObject(long nativeId) { + T object = nativeObjToIdMap.get(nativeId); + if (object != null) { + return object; + } else { + throw new NullPointerException( + String.format( + "Could not find object with nativeId: %d. Currently registered ids: %s", + nativeId, nativeObjToIdMap.keySet())); + } + } + + /** + * Updates the native object for the given id. + * + * @throws IllegalStateException if no object was registered with the given id before + */ + public synchronized void update(long nativeId, T o) { + T previous = nativeObjToIdMap.get(nativeId); + if (previous == null) { + throw new IllegalStateException("Native id " + nativeId + " was never registered"); + } + if (debug) { + System.out.printf("NativeObjRegistry %s: update %d -> %s%n", name, nativeId, o); + idToDebugInfoMap.put(nativeId, new DebugInfo(new Trace())); + } + nativeObjToIdMap.put(nativeId, o); + } + + /** + * Similar to {@link #getNativeObject(long)} but returns null if object with given id cannot be + * found. + */ + public synchronized T peekNativeObject(long nativeId) { + return nativeObjToIdMap.get(nativeId); + } + + /** WARNING -- dangerous! Call {@link #unregister(long)} instead! */ + public synchronized void clear() { + nextId = INITIAL_ID; + nativeObjToIdMap.clear(); + } + + private static class DebugInfo { + final Trace registrationTrace; + final List unregistrationTraces = new ArrayList<>(); + + public DebugInfo(Trace trace) { + registrationTrace = trace; + } + } + + private static class Trace extends Throwable { + + private Trace() {} + } +} diff --git a/AndroidCompat/src/main/java/android/os/shadows/ShadowPausedMessageQueue.java b/AndroidCompat/src/main/java/android/os/shadows/ShadowPausedMessageQueue.java new file mode 100644 index 00000000..39f1acc5 --- /dev/null +++ b/AndroidCompat/src/main/java/android/os/shadows/ShadowPausedMessageQueue.java @@ -0,0 +1,73 @@ +package android.os.shadows; +// package org.robolectric.shadows; +// and badly gutted + +import android.os.Looper; +import android.os.Message; +import android.os.MessageQueue; +import android.os.MessageQueue.IdleHandler; +import android.os.SystemClock; +import android.util.Log; +import java.time.Duration; +import java.util.ArrayList; + +/** + * The shadow {@link} MessageQueue} for {@link LooperMode.Mode.PAUSED} + * + *

This class should not be referenced directly. Use {@link ShadowMessageQueue} instead. + */ +@SuppressWarnings("SynchronizeOnNonFinalField") +public class ShadowPausedMessageQueue { + + // just use this class as the native object + private static NativeObjRegistry nativeQueueRegistry = + new NativeObjRegistry(ShadowPausedMessageQueue.class); + private boolean isPolling = false; + private Exception uncaughtException = null; + + // shadow constructor instead of nativeInit because nativeInit signature has changed across SDK + // versions + public static long nativeInit() { + return nativeQueueRegistry.register(new ShadowPausedMessageQueue()); + } + + public static void nativeDestroy(long ptr) { + nativeQueueRegistry.unregister(ptr); + } + + public static void nativePollOnce(long ptr, int timeoutMillis) { + ShadowPausedMessageQueue obj = nativeQueueRegistry.getNativeObject(ptr); + obj.nativePollOnce(timeoutMillis); + } + + public void nativePollOnce(int timeoutMillis) { + if (timeoutMillis == 0) { + return; + } + synchronized (this) { + isPolling = true; + try { + if (timeoutMillis < 0) { + this.wait(); + } else { + this.wait(timeoutMillis); + } + } catch (InterruptedException e) { + // ignore + } + isPolling = false; + } + } + + public static void nativeWake(long ptr) { + ShadowPausedMessageQueue obj = nativeQueueRegistry.getNativeObject(ptr); + synchronized (obj) { + obj.notifyAll(); + } + } + + public static boolean nativeIsPolling(long ptr) { + return nativeQueueRegistry.getNativeObject(ptr).isPolling; + } +} + diff --git a/AndroidCompat/src/main/java/android/util/Log.java b/AndroidCompat/src/main/java/android/util/Log.java index 03314a9c..1cbc90a7 100644 --- a/AndroidCompat/src/main/java/android/util/Log.java +++ b/AndroidCompat/src/main/java/android/util/Log.java @@ -136,4 +136,4 @@ public final class Log { first.append(msg); return first.toString(); } -} \ No newline at end of file +} diff --git a/AndroidCompat/src/main/java/android/util/Slog.java b/AndroidCompat/src/main/java/android/util/Slog.java new file mode 100644 index 00000000..78e6cae9 --- /dev/null +++ b/AndroidCompat/src/main/java/android/util/Slog.java @@ -0,0 +1,270 @@ +/* + * 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.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Build; + +/** + * API for sending log output to the {@link Log#LOG_ID_SYSTEM} buffer. + * + *

Should be used by system components. Use {@code adb logcat --buffer=system} to fetch the logs. + * + * @see Log + * @hide + */ +public final class Slog { + + private Slog() { + } + + /** + * Logs {@code msg} at {@link Log#VERBOSE} level. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * + * @see Log#v(String, String) + */ + public static int v(@Nullable String tag, @NonNull String msg) { + return Log.println(Log.VERBOSE, tag, msg); + } + + /** + * Logs {@code msg} at {@link Log#VERBOSE} level, attaching stack trace of the {@code tr} to + * the end of the log statement. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * @param tr an exception to log. + * + * @see Log#v(String, String, Throwable) + */ + public static int v(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) { + return Log.println(Log.VERBOSE, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + /** + * Logs {@code msg} at {@link Log#DEBUG} level. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * + * @see Log#d(String, String) + */ + public static int d(@Nullable String tag, @NonNull String msg) { + return Log.println(Log.DEBUG, tag, msg); + } + + /** + * Logs {@code msg} at {@link Log#DEBUG} level, attaching stack trace of the {@code tr} to + * the end of the log statement. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * @param tr an exception to log. + * + * @see Log#d(String, String, Throwable) + */ + public static int d(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) { + return Log.println(Log.DEBUG, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + /** + * Logs {@code msg} at {@link Log#INFO} level. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * + * @see Log#i(String, String) + */ + public static int i(@Nullable String tag, @NonNull String msg) { + return Log.println(Log.INFO, tag, msg); + } + + /** + * Logs {@code msg} at {@link Log#INFO} level, attaching stack trace of the {@code tr} to + * the end of the log statement. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * @param tr an exception to log. + * + * @see Log#i(String, String, Throwable) + */ + public static int i(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) { + return Log.println(Log.INFO, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + /** + * Logs {@code msg} at {@link Log#WARN} level. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * + * @see Log#w(String, String) + */ + public static int w(@Nullable String tag, @NonNull String msg) { + return Log.println(Log.WARN, tag, msg); + } + + /** + * Logs {@code msg} at {@link Log#WARN} level, attaching stack trace of the {@code tr} to + * the end of the log statement. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * @param tr an exception to log. + * + * @see Log#w(String, String, Throwable) + */ + public static int w(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) { + return Log.println(Log.WARN, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + /** + * Logs stack trace of {@code tr} at {@link Log#WARN} level. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param tr an exception to log. + * + * @see Log#w(String, Throwable) + */ + public static int w(@Nullable String tag, @Nullable Throwable tr) { + return Log.println(Log.WARN, tag, Log.getStackTraceString(tr)); + } + + /** + * Logs {@code msg} at {@link Log#ERROR} level. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * + * @see Log#e(String, String) + */ + public static int e(@Nullable String tag, @NonNull String msg) { + return Log.println(Log.ERROR, tag, msg); + } + + /** + * Logs {@code msg} at {@link Log#ERROR} level, attaching stack trace of the {@code tr} to + * the end of the log statement. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * @param tr an exception to log. + * + * @see Log#e(String, String, Throwable) + */ + public static int e(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) { + return Log.println(Log.ERROR, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + /** + * Logs a condition that should never happen. + * + *

+ * Similar to {@link Log#wtf(String, String)}, but will never cause the caller to crash, and + * will always be handled asynchronously. Primarily to be used by the system server. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * + * @see Log#wtf(String, String) + */ + public static int wtf(@Nullable String tag, @NonNull String msg) { + return Log.wtf(tag, msg, null); + } + + /** + * Logs a condition that should never happen, attaching the full call stack to the log. + * + *

+ * Similar to {@link Log#wtfStack(String, String)}, but will never cause the caller to crash, + * and will always be handled asynchronously. Primarily to be used by the system server. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * + * @see Log#wtfStack(String, String) + */ + public static int wtfStack(@Nullable String tag, @NonNull String msg) { + return Log.wtf(tag, msg, null); + } + + /** + * Logs a condition that should never happen, attaching stack trace of the {@code tr} to the + * end of the log statement. + * + *

+ * Similar to {@link Log#wtf(String, Throwable)}, but will never cause the caller to crash, + * and will always be handled asynchronously. Primarily to be used by the system server. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param tr an exception to log. + * + * @see Log#wtf(String, Throwable) + */ + public static int wtf(@Nullable String tag, @Nullable Throwable tr) { + return Log.wtf(tag, tr.getMessage(), tr); + } + + /** + * Logs a condition that should never happen, attaching stack trace of the {@code tr} to the + * end of the log statement. + * + *

+ * Similar to {@link Log#wtf(String, String, Throwable)}, but will never cause the caller to + * crash, and will always be handled asynchronously. Primarily to be used by the system server. + * + * @param tag identifies the source of a log message. It usually represents system service, + * e.g. {@code PackageManager}. + * @param msg the message to log. + * @param tr an exception to log. + * + * @see Log#wtf(String, String, Throwable) + */ + public static int wtf(@Nullable String tag, @NonNull String msg, @Nullable Throwable tr) { + return Log.wtf(tag, msg, tr); + } + + /** @hide */ + public static int println(int priority, @Nullable String tag, @NonNull String msg) { + return Log.println(priority, tag, msg); + } +} diff --git a/AndroidCompat/src/main/java/android/util/TimeUtils.java b/AndroidCompat/src/main/java/android/util/TimeUtils.java new file mode 100644 index 00000000..42521606 --- /dev/null +++ b/AndroidCompat/src/main/java/android/util/TimeUtils.java @@ -0,0 +1,371 @@ +/* + * 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.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.TestApi; +import android.os.Build; +import android.os.SystemClock; + +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * A class containing utility methods related to time zones. + */ +public class TimeUtils { + /** @hide */ public TimeUtils() {} + /** {@hide} */ + private static final SimpleDateFormat sLoggingFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + /** @hide */ + public static final SimpleDateFormat sDumpDateFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + /** + * This timestamp is used in TimeUtils methods and by the SettingsUI to filter time zones + * to only "effective" ones in a country. It is compared against the notUsedAfter metadata that + * Android records for some time zones. + * + *

What is notUsedAfter?

+ * Android chooses to avoid making users choose between functionally identical time zones at the + * expense of not being able to represent local times in the past. + * + * notUsedAfter exists because some time zones can "merge" with other time zones after a given + * point in time (i.e. they change to have identical transitions, offsets, display names, etc.). + * From the notUsedAfter time, the zone will express the same local time as the one it merged + * with. + * + *

Why hardcoded?

+ * Rather than using System.currentTimeMillis(), a timestamp known to be in the recent past is + * used to ensure consistent behavior across devices and time, and avoid assumptions that the + * system clock on a device is currently set correctly. The fixed value should be updated + * occasionally, but it doesn't have to be very often as effective time zones for a country + * don't change very often. + * + * @hide + */ + public static final Instant MIN_USE_DATE_OF_TIMEZONE = + Instant.ofEpochMilli(1546300800000L); // 1/1/2019 00:00 UTC + + /** @hide Field length that can hold 999 days of time */ + public static final int HUNDRED_DAY_FIELD_LEN = 19; + + private static final int SECONDS_PER_MINUTE = 60; + private static final int SECONDS_PER_HOUR = 60 * 60; + private static final int SECONDS_PER_DAY = 24 * 60 * 60; + + /** @hide */ + public static final long NANOS_PER_MS = 1000000; + + private static final Object sFormatSync = new Object(); + private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; + private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; + + static private int accumField(int amt, int suffix, boolean always, int zeropad) { + if (amt > 999) { + int num = 0; + while (amt != 0) { + num++; + amt /= 10; + } + return num + suffix; + } else { + if (amt > 99 || (always && zeropad >= 3)) { + return 3+suffix; + } + if (amt > 9 || (always && zeropad >= 2)) { + return 2+suffix; + } + if (always || amt > 0) { + return 1+suffix; + } + } + return 0; + } + + static private int printFieldLocked(char[] formatStr, int amt, char suffix, int pos, + boolean always, int zeropad) { + if (always || amt > 0) { + final int startPos = pos; + if (amt > 999) { + int tmp = 0; + while (amt != 0 && tmp < sTmpFormatStr.length) { + int dig = amt % 10; + sTmpFormatStr[tmp] = (char)(dig + '0'); + tmp++; + amt /= 10; + } + tmp--; + while (tmp >= 0) { + formatStr[pos] = sTmpFormatStr[tmp]; + pos++; + tmp--; + } + } else { + if ((always && zeropad >= 3) || amt > 99) { + int dig = amt/100; + formatStr[pos] = (char)(dig + '0'); + pos++; + amt -= (dig*100); + } + if ((always && zeropad >= 2) || amt > 9 || startPos != pos) { + int dig = amt/10; + formatStr[pos] = (char)(dig + '0'); + pos++; + amt -= (dig*10); + } + formatStr[pos] = (char)(amt + '0'); + pos++; + } + formatStr[pos] = suffix; + pos++; + } + return pos; + } + + private static int formatDurationLocked(long duration, int fieldLen) { + if (sFormatStr.length < fieldLen) { + sFormatStr = new char[fieldLen]; + } + + char[] formatStr = sFormatStr; + + if (duration == 0) { + int pos = 0; + fieldLen -= 1; + while (pos < fieldLen) { + formatStr[pos++] = ' '; + } + formatStr[pos] = '0'; + return pos+1; + } + + char prefix; + if (duration > 0) { + prefix = '+'; + } else { + prefix = '-'; + duration = -duration; + } + + int millis = (int)(duration%1000); + int seconds = (int) Math.floor(duration / 1000); + int days = 0, hours = 0, minutes = 0; + + if (seconds >= SECONDS_PER_DAY) { + days = seconds / SECONDS_PER_DAY; + seconds -= days * SECONDS_PER_DAY; + } + if (seconds >= SECONDS_PER_HOUR) { + hours = seconds / SECONDS_PER_HOUR; + seconds -= hours * SECONDS_PER_HOUR; + } + if (seconds >= SECONDS_PER_MINUTE) { + minutes = seconds / SECONDS_PER_MINUTE; + seconds -= minutes * SECONDS_PER_MINUTE; + } + + int pos = 0; + + if (fieldLen != 0) { + int myLen = accumField(days, 1, false, 0); + myLen += accumField(hours, 1, myLen > 0, 2); + myLen += accumField(minutes, 1, myLen > 0, 2); + myLen += accumField(seconds, 1, myLen > 0, 2); + myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1; + while (myLen < fieldLen) { + formatStr[pos] = ' '; + pos++; + myLen++; + } + } + + formatStr[pos] = prefix; + pos++; + + int start = pos; + boolean zeropad = fieldLen != 0; + pos = printFieldLocked(formatStr, days, 'd', pos, false, 0); + pos = printFieldLocked(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); + pos = printFieldLocked(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); + pos = printFieldLocked(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); + pos = printFieldLocked(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); + formatStr[pos] = 's'; + return pos + 1; + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long duration, StringBuilder builder) { + synchronized (sFormatSync) { + int len = formatDurationLocked(duration, 0); + builder.append(sFormatStr, 0, len); + } + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long duration, StringBuilder builder, int fieldLen) { + synchronized (sFormatSync) { + int len = formatDurationLocked(duration, fieldLen); + builder.append(sFormatStr, 0, len); + } + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long duration, PrintWriter pw, int fieldLen) { + synchronized (sFormatSync) { + int len = formatDurationLocked(duration, fieldLen); + pw.print(new String(sFormatStr, 0, len)); + } + } + + /** @hide Just for debugging; not internationalized. */ + @TestApi + public static String formatDuration(long duration) { + synchronized (sFormatSync) { + int len = formatDurationLocked(duration, 0); + return new String(sFormatStr, 0, len); + } + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long duration, PrintWriter pw) { + formatDuration(duration, pw, 0); + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long time, long now, StringBuilder sb) { + if (time == 0) { + sb.append("--"); + return; + } + formatDuration(time-now, sb, 0); + } + + /** @hide Just for debugging; not internationalized. */ + public static void formatDuration(long time, long now, PrintWriter pw) { + if (time == 0) { + pw.print("--"); + return; + } + formatDuration(time-now, pw, 0); + } + + /** @hide Just for debugging; not internationalized. */ + public static String formatUptime(long time) { + return formatTime(time, SystemClock.uptimeMillis()); + } + + /** @hide Just for debugging; not internationalized. */ + public static String formatRealtime(long time) { + return formatTime(time, SystemClock.elapsedRealtime()); + } + + /** @hide Just for debugging; not internationalized. */ + public static String formatTime(long time, long referenceTime) { + long diff = time - referenceTime; + if (diff > 0) { + return time + " (in " + diff + " ms)"; + } + if (diff < 0) { + return time + " (" + -diff + " ms ago)"; + } + return time + " (now)"; + } + + /** + * Convert a System.currentTimeMillis() value to a time of day value like + * that printed in logs. MM-DD HH:MM:SS.MMM + * + * @param millis since the epoch (1/1/1970) + * @return String representation of the time. + * @hide + */ + public static String logTimeOfDay(long millis) { + Calendar c = Calendar.getInstance(); + if (millis >= 0) { + c.setTimeInMillis(millis); + return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c); + } else { + return Long.toString(millis); + } + } + + /** {@hide} */ + public static String formatForLogging(long millis) { + if (millis <= 0) { + return "unknown"; + } else { + return sLoggingFormat.format(new Date(millis)); + } + } + + /** + * Dump a currentTimeMillis style timestamp for dumpsys. + * + * @hide + */ + public static void dumpTime(PrintWriter pw, long time) { + pw.print(sDumpDateFormat.format(new Date(time))); + } + + /** + * This method is used to find if a clock time is inclusively between two other clock times + * @param reference The time of the day we want check if it is between start and end + * @param start The start time reference + * @param end The end time + * @return true if the reference time is between the two clock times, and false otherwise. + */ + public static boolean isTimeBetween(@NonNull LocalTime reference, + @NonNull LocalTime start, + @NonNull LocalTime end) { + // ////////E----+-----S//////// + if ((reference.isBefore(start) && reference.isAfter(end) + // -----+----S//////////E------ + || (reference.isBefore(end) && reference.isBefore(start) && start.isBefore(end)) + // ---------S//////////E---+--- + || (reference.isAfter(end) && reference.isAfter(start)) && start.isBefore(end))) { + return false; + } else { + return true; + } + } + + /** + * Dump a currentTimeMillis style timestamp for dumpsys, with the delta time from now. + * + * @hide + */ + public static void dumpTimeWithDelta(PrintWriter pw, long time, long now) { + pw.print(sDumpDateFormat.format(new Date(time))); + if (time == now) { + pw.print(" (now)"); + } else { + pw.print(" ("); + TimeUtils.formatDuration(time, now, pw); + pw.print(")"); + } + }} diff --git a/AndroidCompat/src/main/java/android/util/TraceNameSupplier.java b/AndroidCompat/src/main/java/android/util/TraceNameSupplier.java new file mode 100644 index 00000000..e4b3a4ed --- /dev/null +++ b/AndroidCompat/src/main/java/android/util/TraceNameSupplier.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 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.os; + +import android.annotation.NonNull; + +/** + * Supplier for custom trace messages. + * + * @hide + */ +public interface TraceNameSupplier { + + /** + * Gets the name used for trace messages. + */ + @NonNull String getTraceName(); +} diff --git a/AndroidCompat/src/main/java/android/view/View.java b/AndroidCompat/src/main/java/android/view/View.java new file mode 100644 index 00000000..06a19f6c --- /dev/null +++ b/AndroidCompat/src/main/java/android/view/View.java @@ -0,0 +1,5748 @@ +/* + * 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 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.FloatRange; +import android.annotation.IdRes; +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.UiThread; +import android.app.PendingIntent; +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.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.RenderNode; +import android.graphics.Shader; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +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.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +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.Pair; +import android.util.Property; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.StateSet; +import android.util.TimeUtils; +import android.util.TypedValue; +import android.view.ContextMenu.ContextMenuInfo; +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.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.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; +import android.widget.Checkable; + +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; + +@UiThread +public class View implements Drawable.Callback, KeyEvent.Callback, + AccessibilityEventSource { + /** @hide */ + public static boolean DEBUG_DRAW = false; + + protected static final String VIEW_LOG_TAG = "View"; + + public static boolean sDebugViewAttributes = false; + + public static String sDebugViewAttributesApplicationPackage; + + public static final int NO_ID = -1; + + public static final int LAST_APP_AUTOFILL_ID = Integer.MAX_VALUE / 2; + + /** @hide */ + // public HapticScrollFeedbackProvider mScrollFeedbackProvider = null; + + static boolean sTextureViewIgnoresDrawableSetters = false; + + protected static boolean sPreserveMarginParamsInLayoutParamConversion; + + static boolean sCascadedDragDrop; + + static boolean sHasFocusableExcludeAutoFocusable; + + static boolean sBrokenInsetsDispatch; + + protected static boolean sBrokenWindowBackground; + + static boolean sForceLayoutWhenInsetsChanged; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface Focusable {} + + public static final int NOT_FOCUSABLE = 0x00000000; + + public static final int FOCUSABLE = 0x00000001; + + public static final int FOCUSABLE_AUTO = 0x00000010; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface Visibility {} + + public static final int VISIBLE = 0x00000000; + + public static final int INVISIBLE = 0x00000004; + + public static final int GONE = 0x00000008; + + static final int VISIBILITY_MASK = 0x0000000C; + + public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress"; + + public static final String AUTOFILL_HINT_NAME = "name"; + + public static final String AUTOFILL_HINT_USERNAME = "username"; + + public static final String AUTOFILL_HINT_PASSWORD = "password"; + + public static final String AUTOFILL_HINT_PHONE = "phone"; + + public static final String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress"; + + public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode"; + + public static final String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber"; + + public static final String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode"; + + public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = + "creditCardExpirationDate"; + + public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = + "creditCardExpirationMonth"; + + public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = + "creditCardExpirationYear"; + + public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay"; + + // TODO(229765029): unhide this for UI toolkit + public static final String AUTOFILL_HINT_PASSWORD_AUTO = "passwordAuto"; + + public static final String AUTOFILL_HINT_CREDENTIAL_MANAGER = "credential"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface AutofillType {} + + public static final int AUTOFILL_TYPE_NONE = 0; + + public static final int AUTOFILL_TYPE_TEXT = 1; + + public static final int AUTOFILL_TYPE_TOGGLE = 2; + + public static final int AUTOFILL_TYPE_LIST = 3; + + public static final int AUTOFILL_TYPE_DATE = 4; + + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface AutofillImportance {} + + public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0; + + public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1; + + public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2; + + public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 0x4; + + public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 0x8; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface AutofillFlags {} + + public static final int AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x1; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface ContentCaptureImportance {} + + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0x0; + + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 0x1; + + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 0x2; + + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 0x4; + + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 0x8; + + /** {@hide} */ + @Retention(RetentionPolicy.SOURCE) + public @interface ScrollCaptureHint {} + + public static final int SCROLL_CAPTURE_HINT_AUTO = 0; + + public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 0x1; + + public static final int SCROLL_CAPTURE_HINT_INCLUDE = 0x2; + + public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 0x4; + + static final int ENABLED = 0x00000000; + + static final int DISABLED = 0x00000020; + + static final int ENABLED_MASK = 0x00000020; + + static final int WILL_NOT_DRAW = 0x00000080; + + static final int DRAW_MASK = 0x00000080; + + static final int SCROLLBARS_NONE = 0x00000000; + + static final int SCROLLBARS_HORIZONTAL = 0x00000100; + + static final int SCROLLBARS_VERTICAL = 0x00000200; + + static final int SCROLLBARS_MASK = 0x00000300; + + static final int FILTER_TOUCHES_WHEN_OBSCURED = 0x00000400; + + static final int OPTIONAL_FITS_SYSTEM_WINDOWS = 0x00000800; + + static final int FADING_EDGE_NONE = 0x00000000; + + static final int FADING_EDGE_HORIZONTAL = 0x00001000; + + static final int FADING_EDGE_VERTICAL = 0x00002000; + + static final int FADING_EDGE_MASK = 0x00003000; + + static final int CLICKABLE = 0x00004000; + + static final int DRAWING_CACHE_ENABLED = 0x00008000; + + static final int SAVE_DISABLED = 0x000010000; + + static final int SAVE_DISABLED_MASK = 0x000010000; + + static final int WILL_NOT_CACHE_DRAWING = 0x000020000; + + static final int FOCUSABLE_IN_TOUCH_MODE = 0x00040000; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface DrawingCacheQuality {} + + @Deprecated + public static final int DRAWING_CACHE_QUALITY_LOW = 0x00080000; + + @Deprecated + public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000; + + @Deprecated + public static final int DRAWING_CACHE_QUALITY_AUTO = 0x00000000; + + static final int DRAWING_CACHE_QUALITY_MASK = 0x00180000; + + static final int LONG_CLICKABLE = 0x00200000; + + static final int DUPLICATE_PARENT_STATE = 0x00400000; + + static final int CONTEXT_CLICKABLE = 0x00800000; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface ScrollBarStyle {} + + public static final int SCROLLBARS_INSIDE_OVERLAY = 0; + + public static final int SCROLLBARS_INSIDE_INSET = 0x01000000; + + public static final int SCROLLBARS_OUTSIDE_OVERLAY = 0x02000000; + + public static final int SCROLLBARS_OUTSIDE_INSET = 0x03000000; + + static final int SCROLLBARS_INSET_MASK = 0x01000000; + + static final int SCROLLBARS_OUTSIDE_MASK = 0x02000000; + + static final int SCROLLBARS_STYLE_MASK = 0x03000000; + + public static final int KEEP_SCREEN_ON = 0x04000000; + + public static final int SOUND_EFFECTS_ENABLED = 0x08000000; + + public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000; + + static final int PARENT_SAVE_DISABLED = 0x20000000; + + static final int PARENT_SAVE_DISABLED_MASK = 0x20000000; + + static final int TOOLTIP = 0x40000000; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface ContentSensitivity {} + + public static final int CONTENT_SENSITIVITY_AUTO = 0x0; + + public static final int CONTENT_SENSITIVITY_SENSITIVE = 0x1; + + public static final int CONTENT_SENSITIVITY_NOT_SENSITIVE = 0x2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface FocusableMode {} + + public static final int FOCUSABLES_ALL = 0x00000000; + + public static final int FOCUSABLES_TOUCH_MODE = 0x00000001; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface FocusDirection {} + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface FocusRealDirection {} // Like @FocusDirection, but without forward/backward + + public static final int FOCUS_BACKWARD = 0x00000001; + + public static final int FOCUS_FORWARD = 0x00000002; + + public static final int FOCUS_LEFT = 0x00000011; + + public static final int FOCUS_UP = 0x00000021; + + public static final int FOCUS_RIGHT = 0x00000042; + + public static final int FOCUS_DOWN = 0x00000082; + + public static final int MEASURED_SIZE_MASK = 0x00ffffff; + + public static final int MEASURED_STATE_MASK = 0xff000000; + + public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; + + public static final int MEASURED_STATE_TOO_SMALL = 0x01000000; + + // Singles + protected static final int[] EMPTY_STATE_SET = new int[0]; + protected static final int[] ENABLED_STATE_SET = new int[0]; + protected static final int[] FOCUSED_STATE_SET = new int[0]; + protected static final int[] SELECTED_STATE_SET = new int[0]; + protected static final int[] PRESSED_STATE_SET = new int[0]; + protected static final int[] WINDOW_FOCUSED_STATE_SET = new int[0]; + // Doubles + protected static final int[] ENABLED_FOCUSED_STATE_SET = new int[0]; + protected static final int[] ENABLED_SELECTED_STATE_SET = new int[0]; + protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] FOCUSED_SELECTED_STATE_SET = new int[0]; + protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET = new int[0]; + // Triples + protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET = new int[0]; + protected static final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_SELECTED_STATE_SET = new int[0]; + protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_FOCUSED_SELECTED_STATE_SET = new int[0]; + protected static final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_ENABLED_STATE_SET = new int[0]; + protected static final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_ENABLED_SELECTED_STATE_SET = new int[0]; + protected static final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_ENABLED_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = new int[0]; + protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET = new int[0]; + protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = new int[0]; + + public static final int FRAME_RATE_CATEGORY_REASON_UNKNOWN = 0x0000_0000; + + public static final int FRAME_RATE_CATEGORY_REASON_SMALL = 0x0100_0000; + + public static final int FRAME_RATE_CATEGORY_REASON_INTERMITTENT = 0x0200_0000; + + public static final int FRAME_RATE_CATEGORY_REASON_LARGE = 0x03000000; + + public static final int FRAME_RATE_CATEGORY_REASON_REQUESTED = 0x0400_0000; + + public static final int FRAME_RATE_CATEGORY_REASON_INVALID = 0x0500_0000; + + public static final int FRAME_RATE_CATEGORY_REASON_VELOCITY = 0x0600_0000; + + public static final int FRAME_RATE_CATEGORY_REASON_BOOST = 0x0800_0000; + + public static final int FRAME_RATE_CATEGORY_REASON_TOUCH = 0x0900_0000; + + public static final int FRAME_RATE_CATEGORY_REASON_CONFLICTED = 0x0A00_0000; + + protected static boolean sToolkitSetFrameRateReadOnlyFlagValue; + + // Used to set frame rate compatibility. + int mFrameRateCompatibility; + + static final int DEBUG_CORNERS_COLOR = 0; + + static final int DEBUG_CORNERS_SIZE_DIP = 8; + + static final ThreadLocal sThreadLocal = ThreadLocal.withInitial(Rect::new); + + protected Animation mCurrentAnimation = null; + + @ViewDebug.ExportedProperty(category = "measurement") + int mMeasuredWidth; + + @ViewDebug.ExportedProperty(category = "measurement") + int mMeasuredHeight; + + boolean mRecreateDisplayList = false; + + @IdRes + @ViewDebug.ExportedProperty(resolveId = true) + int mID = NO_ID; + protected Object mTag = null; + + /* + * Masks for mPrivateFlags, as generated by dumpFlags(): + * + * |-------|-------|-------|-------| + * 1 PFLAG_WANTS_FOCUS + * 1 PFLAG_FOCUSED + * 1 PFLAG_SELECTED + * 1 PFLAG_IS_ROOT_NAMESPACE + * 1 PFLAG_HAS_BOUNDS + * 1 PFLAG_DRAWN + * 1 PFLAG_DRAW_ANIMATION + * 1 PFLAG_SKIP_DRAW + * 1 PFLAG_REQUEST_TRANSPARENT_REGIONS + * 1 PFLAG_DRAWABLE_STATE_DIRTY + * 1 PFLAG_MEASURED_DIMENSION_SET + * 1 PFLAG_FORCE_LAYOUT + * 1 PFLAG_LAYOUT_REQUIRED + * 1 PFLAG_PRESSED + * 1 PFLAG_DRAWING_CACHE_VALID + * 1 PFLAG_ANIMATION_STARTED + * 1 PFLAG_SAVE_STATE_CALLED + * 1 PFLAG_ALPHA_SET + * 1 PFLAG_SCROLL_CONTAINER + * 1 PFLAG_SCROLL_CONTAINER_ADDED + * 1 PFLAG_DIRTY + * 1 PFLAG_DIRTY_MASK + * 1 PFLAG_OPAQUE_BACKGROUND + * 1 PFLAG_OPAQUE_SCROLLBARS + * 11 PFLAG_OPAQUE_MASK + * 1 PFLAG_PREPRESSED + * 1 PFLAG_CANCEL_NEXT_UP_EVENT + * 1 PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH + * 1 PFLAG_HOVERED + * 1 PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK + * 1 PFLAG_ACTIVATED + * 1 PFLAG_INVALIDATED + * |-------|-------|-------|-------| + */ + /** {@hide} */ + static final int PFLAG_WANTS_FOCUS = 0x00000001; + /** {@hide} */ + static final int PFLAG_FOCUSED = 0x00000002; + /** {@hide} */ + static final int PFLAG_SELECTED = 0x00000004; + /** {@hide} */ + static final int PFLAG_IS_ROOT_NAMESPACE = 0x00000008; + /** {@hide} */ + static final int PFLAG_HAS_BOUNDS = 0x00000010; + /** {@hide} */ + static final int PFLAG_DRAWN = 0x00000020; + static final int PFLAG_DRAW_ANIMATION = 0x00000040; + /** {@hide} */ + static final int PFLAG_SKIP_DRAW = 0x00000080; + /** {@hide} */ + static final int PFLAG_REQUEST_TRANSPARENT_REGIONS = 0x00000200; + /** {@hide} */ + static final int PFLAG_DRAWABLE_STATE_DIRTY = 0x00000400; + /** {@hide} */ + static final int PFLAG_MEASURED_DIMENSION_SET = 0x00000800; + /** {@hide} */ + static final int PFLAG_FORCE_LAYOUT = 0x00001000; + /** {@hide} */ + static final int PFLAG_LAYOUT_REQUIRED = 0x00002000; + + /** {@hide} */ + static final int PFLAG_DRAWING_CACHE_VALID = 0x00008000; + static final int PFLAG_ANIMATION_STARTED = 0x00010000; + + static final int PFLAG_ALPHA_SET = 0x00040000; + + static final int PFLAG_SCROLL_CONTAINER = 0x00080000; + + static final int PFLAG_SCROLL_CONTAINER_ADDED = 0x00100000; + + static final int PFLAG_DIRTY = 0x00200000; + + static final int PFLAG_DIRTY_MASK = 0x00200000; + + static final int PFLAG_OPAQUE_BACKGROUND = 0x00800000; + + static final int PFLAG_OPAQUE_SCROLLBARS = 0x01000000; + + static final int PFLAG_OPAQUE_MASK = 0x01800000; + + static final int PFLAG_CANCEL_NEXT_UP_EVENT = 0x04000000; + + /** {@hide} */ + static final int PFLAG_ACTIVATED = 0x40000000; + + static final int PFLAG_INVALIDATED = 0x80000000; + + /* End of masks for mPrivateFlags */ + + /* + * Masks for mPrivateFlags2, as generated by dumpFlags(): + * + * |-------|-------|-------|-------| + * 1 PFLAG2_DRAG_CAN_ACCEPT + * 1 PFLAG2_DRAG_HOVERED + * 11 PFLAG2_LAYOUT_DIRECTION_MASK + * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL + * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED + * 11 PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK + * 1 PFLAG2_TEXT_DIRECTION_FLAGS[1] + * 1 PFLAG2_TEXT_DIRECTION_FLAGS[2] + * 11 PFLAG2_TEXT_DIRECTION_FLAGS[3] + * 1 PFLAG2_TEXT_DIRECTION_FLAGS[4] + * 1 1 PFLAG2_TEXT_DIRECTION_FLAGS[5] + * 11 PFLAG2_TEXT_DIRECTION_FLAGS[6] + * 111 PFLAG2_TEXT_DIRECTION_FLAGS[7] + * 111 PFLAG2_TEXT_DIRECTION_MASK + * 1 PFLAG2_TEXT_DIRECTION_RESOLVED + * 1 PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT + * 111 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK + * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[1] + * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[2] + * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[3] + * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[4] + * 1 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[5] + * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[6] + * 111 PFLAG2_TEXT_ALIGNMENT_MASK + * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED + * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT + * 111 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK + * 111 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK + * 11 PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK + * 1 PFLAG2_ACCESSIBILITY_FOCUSED + * 1 PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED + * 1 PFLAG2_VIEW_QUICK_REJECTED + * 1 PFLAG2_PADDING_RESOLVED + * 1 PFLAG2_DRAWABLE_RESOLVED + * 1 PFLAG2_HAS_TRANSIENT_STATE + * |-------|-------|-------|-------| + */ + + static final int PFLAG2_DRAG_CAN_ACCEPT = 0x00000001; + + static final int PFLAG2_DRAG_HOVERED = 0x00000002; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + // Not called LayoutDirection to avoid conflict with android.util.LayoutDirection + public @interface LayoutDir {} + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface ResolvedLayoutDir {} + + public static final int LAYOUT_DIRECTION_UNDEFINED = 0; + + public static final int LAYOUT_DIRECTION_LTR = LayoutDirection.LTR; + + public static final int LAYOUT_DIRECTION_RTL = LayoutDirection.RTL; + + public static final int LAYOUT_DIRECTION_INHERIT = LayoutDirection.INHERIT; + + public static final int LAYOUT_DIRECTION_LOCALE = LayoutDirection.LOCALE; + + static final int PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT = 2; + + static final int PFLAG2_LAYOUT_DIRECTION_MASK = 0x00000003 << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; + + static final int PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL = 4 << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; + + static final int PFLAG2_LAYOUT_DIRECTION_RESOLVED = 8 << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; + + static final int PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK = 0x0000000C + << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT; + + static final int LAYOUT_DIRECTION_RESOLVED_DEFAULT = LAYOUT_DIRECTION_LTR; + + public static final int TEXT_DIRECTION_INHERIT = 0; + + public static final int TEXT_DIRECTION_FIRST_STRONG = 1; + + public static final int TEXT_DIRECTION_ANY_RTL = 2; + + public static final int TEXT_DIRECTION_LTR = 3; + + public static final int TEXT_DIRECTION_RTL = 4; + + public static final int TEXT_DIRECTION_LOCALE = 5; + + public static final int TEXT_DIRECTION_FIRST_STRONG_LTR = 6; + + public static final int TEXT_DIRECTION_FIRST_STRONG_RTL = 7; + + static final int TEXT_DIRECTION_RESOLVED_DEFAULT = TEXT_DIRECTION_FIRST_STRONG; + + static final int PFLAG2_TEXT_DIRECTION_MASK_SHIFT = 6; + + static final int PFLAG2_TEXT_DIRECTION_MASK = 0x00000007 + << PFLAG2_TEXT_DIRECTION_MASK_SHIFT; + + static final int PFLAG2_TEXT_DIRECTION_RESOLVED = 0x00000008 + << PFLAG2_TEXT_DIRECTION_MASK_SHIFT; + + static final int PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT = 10; + + static final int PFLAG2_TEXT_DIRECTION_RESOLVED_MASK = 0x00000007 + << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; + + static final int PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT = + TEXT_DIRECTION_RESOLVED_DEFAULT << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface TextAlignment {} + + public static final int TEXT_ALIGNMENT_INHERIT = 0; + + public static final int TEXT_ALIGNMENT_GRAVITY = 1; + + public static final int TEXT_ALIGNMENT_TEXT_START = 2; + + public static final int TEXT_ALIGNMENT_TEXT_END = 3; + + public static final int TEXT_ALIGNMENT_CENTER = 4; + + public static final int TEXT_ALIGNMENT_VIEW_START = 5; + + public static final int TEXT_ALIGNMENT_VIEW_END = 6; + + static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT = TEXT_ALIGNMENT_GRAVITY; + + static final int PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT = 13; + + static final int PFLAG2_TEXT_ALIGNMENT_MASK = 0x00000007 << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT; + + static final int PFLAG2_TEXT_ALIGNMENT_RESOLVED = 0x00000008 << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT; + + static final int PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT = 17; + + static final int PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK = 0x00000007 + << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT; + + // Accessiblity constants for mPrivateFlags2 + + static final int PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT = 20; + + public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000; + + public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001; + + public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002; + + public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 0x00000004; + + static final int IMPORTANT_FOR_ACCESSIBILITY_DEFAULT = IMPORTANT_FOR_ACCESSIBILITY_AUTO; + + public static final int ACCESSIBILITY_DATA_SENSITIVE_AUTO = 0x00000000; + + public static final int ACCESSIBILITY_DATA_SENSITIVE_YES = 0x00000001; + + public static final int ACCESSIBILITY_DATA_SENSITIVE_NO = 0x00000002; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface AccessibilityDataSensitive {} + + static final int PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK = (IMPORTANT_FOR_ACCESSIBILITY_AUTO + | IMPORTANT_FOR_ACCESSIBILITY_YES | IMPORTANT_FOR_ACCESSIBILITY_NO + | IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) + << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT; + + static final int PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT = 23; + + public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0x00000000; + + public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001; + + public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002; + + static final int ACCESSIBILITY_LIVE_REGION_DEFAULT = ACCESSIBILITY_LIVE_REGION_NONE; + + 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; + + static final int PFLAG2_ACCESSIBILITY_FOCUSED = 0x04000000; + + static final int PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED = 0x08000000; + + static final int PFLAG2_VIEW_QUICK_REJECTED = 0x10000000; + + static final int PFLAG2_PADDING_RESOLVED = 0x20000000; + + static final int PFLAG2_DRAWABLE_RESOLVED = 0x40000000; + + static final int PFLAG2_HAS_TRANSIENT_STATE = 0x80000000; + + 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 + * |-------|-------|-------|-------| + */ + + static final int PFLAG3_VIEW_IS_ANIMATING_TRANSFORM = 0x1; + + static final int PFLAG3_VIEW_IS_ANIMATING_ALPHA = 0x2; + + static final int PFLAG3_IS_LAID_OUT = 0x4; + + static final int PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT = 0x8; + + static final int PFLAG3_CALLED_SUPER = 0x10; + + static final int PFLAG3_APPLYING_INSETS = 0x20; + + static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x40; + + static final int PFLAG3_NESTED_SCROLLING_ENABLED = 0x80; + + static final int PFLAG3_SCROLL_INDICATOR_TOP = 0x0100; + + static final int PFLAG3_SCROLL_INDICATOR_BOTTOM = 0x0200; + + static final int PFLAG3_SCROLL_INDICATOR_LEFT = 0x0400; + + static final int PFLAG3_SCROLL_INDICATOR_RIGHT = 0x0800; + + static final int PFLAG3_SCROLL_INDICATOR_START = 0x1000; + + 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; + + 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; + + static final int SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT = 8; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface ScrollIndicators {} + + public static final int SCROLL_INDICATOR_TOP = + PFLAG3_SCROLL_INDICATOR_TOP >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; + + public static final int SCROLL_INDICATOR_BOTTOM = + PFLAG3_SCROLL_INDICATOR_BOTTOM >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; + + public static final int SCROLL_INDICATOR_LEFT = + PFLAG3_SCROLL_INDICATOR_LEFT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; + + public static final int SCROLL_INDICATOR_RIGHT = + PFLAG3_SCROLL_INDICATOR_RIGHT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; + + public static final int SCROLL_INDICATOR_START = + PFLAG3_SCROLL_INDICATOR_START >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; + + public static final int SCROLL_INDICATOR_END = + PFLAG3_SCROLL_INDICATOR_END >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; + + static final int PFLAG3_ASSIST_BLOCKED = 0x4000; + + static final int PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT = 19; + + 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; + + static final int PFLAG3_TEMPORARY_DETACH = 0x2000000; + + static final int PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT = 0x8000000; + + /* 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 + * 1 PFLAG4_FORCED_OVERRIDE_FRAME_RATE + * 1 PFLAG4_SELF_REQUESTED_FRAME_RATE + * |-------|-------|-------|-------| + */ + + static final int PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS = 0x000000100; + + 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; + + /* 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 */ + @Retention(RetentionPolicy.SOURCE) + public @interface ViewStructureType {} + + public static final int OVER_SCROLL_ALWAYS = 0; + + public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; + + public static final int OVER_SCROLL_NEVER = 2; + + @Deprecated + public static final int SYSTEM_UI_FLAG_VISIBLE = 0; + + @Deprecated + public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001; + + @Deprecated + public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002; + + @Deprecated + public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004; + + @Deprecated + public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100; + + public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200; + + @Deprecated + public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400; + + @Deprecated + public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800; + + @Deprecated + public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000; + + @Deprecated + public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000; + + @Deprecated + public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 0x00000010; + + @Deprecated + public static final int STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE; + + @Deprecated + public static final int STATUS_BAR_VISIBLE = SYSTEM_UI_FLAG_VISIBLE; + + public static final int STATUS_BAR_DISABLE_EXPAND = 0x00010000; + + public static final int STATUS_BAR_DISABLE_NOTIFICATION_ICONS = 0x00020000; + + public static final int STATUS_BAR_DISABLE_NOTIFICATION_ALERTS = 0x00040000; + + public static final int STATUS_BAR_DISABLE_NOTIFICATION_TICKER = 0x00080000; + + public static final int STATUS_BAR_DISABLE_SYSTEM_INFO = 0x00100000; + + public static final int STATUS_BAR_DISABLE_HOME = 0x00200000; + + public static final int STATUS_BAR_DISABLE_BACK = 0x00400000; + + public static final int STATUS_BAR_DISABLE_CLOCK = 0x00800000; + + public static final int STATUS_BAR_DISABLE_RECENT = 0x01000000; + + public static final int STATUS_BAR_DISABLE_SEARCH = 0x02000000; + + public static final int STATUS_BAR_DISABLE_ONGOING_CALL_CHIP = 0x04000000; + + public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00003FF7; + + public static final int SYSTEM_UI_CLEARABLE_FLAGS = + SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION + | SYSTEM_UI_FLAG_FULLSCREEN; + + @Deprecated + public static final int SYSTEM_UI_LAYOUT_FLAGS = + SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface FindViewFlags {} + + public static final int FIND_VIEWS_WITH_TEXT = 0x00000001; + + public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 0x00000002; + + public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004; + + public static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1; + + public static final int SCREEN_STATE_OFF = 0x0; + + public static final int SCREEN_STATE_ON = 0x1; + + public static final int SCROLL_AXIS_NONE = 0; + + public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0; + + public static final int SCROLL_AXIS_VERTICAL = 1 << 1; + + protected ViewParent mParent; + + // AttachInfo mAttachInfo; + + /* @hide */ + public int mPrivateFlags; + int mPrivateFlags2; + int mPrivateFlags3; + + @SystemUiVisibility + int mSystemUiVisibility; + + @Retention(RetentionPolicy.SOURCE) + public @interface SystemUiVisibility {} + + int mTransientStateCount = 0; + + int mWindowAttachCount; + + protected ViewGroup.LayoutParams mLayoutParams; + + @ViewDebug.ExportedProperty(formatToHexString = true) + int mViewFlags; + + static class TransformationInfo { + float mTransitionAlpha = 1f; + } + + /** @hide */ + public TransformationInfo mTransformationInfo; + + @ViewDebug.ExportedProperty(category = "drawing") + Rect mClipBounds = null; + + @ViewDebug.ExportedProperty(category = "layout") + protected int mLeft; + @ViewDebug.ExportedProperty(category = "layout") + protected int mRight; + @ViewDebug.ExportedProperty(category = "layout") + protected int mTop; + @ViewDebug.ExportedProperty(category = "layout") + protected int mBottom; + + @ViewDebug.ExportedProperty(category = "scrolling") + protected int mScrollX; + @ViewDebug.ExportedProperty(category = "scrolling") + protected int mScrollY; + + @ViewDebug.ExportedProperty(category = "padding") + protected int mPaddingLeft = 0; + @ViewDebug.ExportedProperty(category = "padding") + protected int mPaddingRight = 0; + @ViewDebug.ExportedProperty(category = "padding") + protected int mPaddingTop; + @ViewDebug.ExportedProperty(category = "padding") + protected int mPaddingBottom; + + @ViewDebug.ExportedProperty(category = "padding") + protected int mUserPaddingRight; + + @ViewDebug.ExportedProperty(category = "padding") + protected int mUserPaddingBottom; + + @ViewDebug.ExportedProperty(category = "padding") + protected int mUserPaddingLeft; + + @ViewDebug.ExportedProperty(category = "padding") + int mUserPaddingStart; + + @ViewDebug.ExportedProperty(category = "padding") + int mUserPaddingEnd; + + int mUserPaddingLeftInitial; + + int mUserPaddingRightInitial; + + int mOldWidthMeasureSpec = Integer.MIN_VALUE; + int mOldHeightMeasureSpec = Integer.MIN_VALUE; + + RenderNode mBackgroundRenderNode; + + static class TintInfo { + ColorStateList mTintList; + BlendMode mBlendMode; + boolean mHasTintMode; + boolean mHasTintList; + } + + // ListenerInfo mListenerInfo; + + // TooltipInfo mTooltipInfo; + + @ViewDebug.ExportedProperty(deepExport = true) + protected Context mContext; + + ViewOutlineProvider mOutlineProvider = ViewOutlineProvider.BACKGROUND; + + int mNextFocusForwardId = View.NO_ID; + + int mNextClusterForwardId = View.NO_ID; + + boolean mDefaultFocusHighlightEnabled = true; + + public static final int DRAG_FLAG_GLOBAL = 1 << 8; // 256 + + public static final int DRAG_FLAG_GLOBAL_URI_READ = Intent.FLAG_GRANT_READ_URI_PERMISSION; + + public static final int DRAG_FLAG_GLOBAL_URI_WRITE = Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + + public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = + Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION; + + public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = + Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + + public static final int DRAG_FLAG_OPAQUE = 1 << 9; + + public static final int DRAG_FLAG_ACCESSIBILITY_ACTION = 1 << 10; + + public static final int DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION = 1 << 11; + + public static final int DRAG_FLAG_GLOBAL_SAME_APPLICATION = 1 << 12; + + public static final int DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG = 1 << 13; + + public static final int DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START = 1 << 14; + + public static final int SCROLLBAR_POSITION_DEFAULT = 0; + + public static final int SCROLLBAR_POSITION_LEFT = 1; + + public static final int SCROLLBAR_POSITION_RIGHT = 2; + + public static final int LAYER_TYPE_NONE = 0; + + public static final int LAYER_TYPE_SOFTWARE = 1; + + public static final int LAYER_TYPE_HARDWARE = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface LayerType {} + + int mLayerType = LAYER_TYPE_NONE; + Paint mLayerPaint; + + public boolean mCachingFailed; + + final RenderNode mRenderNode; + + AccessibilityDelegate mAccessibilityDelegate; + + ViewOverlay mOverlay; + + // protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier = + // InputEventConsistencyVerifier.isInstrumentationEnabled() ? + // new InputEventConsistencyVerifier(this, 0) : null; + + // GhostView mGhostView; + + @ViewDebug.ExportedProperty(category = "attributes", hasAdjacentMapping = true) + public String[] mAttributes; + + String mStartActivityRequestWho; + + int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE; + + static final float MAX_FRAME_RATE = 120; + + public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = Float.NaN; + public static final float REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE = -1; + public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -2; + public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -3; + public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -4; + + public View(Context context) { + mContext = context; + mRenderNode = null; + } + + public View(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + this(context); + } + + @NonNull + public int[] getAttributeResolutionStack(@AttrRes int attribute) { + throw new RuntimeException("Stub!"); + } + + @NonNull + @SuppressWarnings("AndroidFrameworkEfficientCollections") + public Map getAttributeSourceResourceMap() { + throw new RuntimeException("Stub!"); + } + + @StyleRes + public int getExplicitStyle() { + throw new RuntimeException("Stub!"); + } + + View() { + mRenderNode = null; + } + + public final boolean isShowingLayoutBounds() { + throw new RuntimeException("Stub!"); + } + + @TestApi + public final void setShowingLayoutBounds(boolean debugLayout) { + throw new RuntimeException("Stub!"); + } + + public final void saveAttributeDataForStyleable(@NonNull Context context, + @NonNull int[] styleable, @Nullable AttributeSet attrs, @NonNull TypedArray t, + int defStyleAttr, int defStyleRes) { + throw new RuntimeException("Stub!"); + } + + protected void initializeFadingEdge(TypedArray a) { + throw new RuntimeException("Stub!"); + } + + protected void initializeFadingEdgeInternal(TypedArray a) { + throw new RuntimeException("Stub!"); + } + + public int getVerticalFadingEdgeLength() { + throw new RuntimeException("Stub!"); + } + + public void setFadingEdgeLength(int length) { + throw new RuntimeException("Stub!"); + } + + public void clearPendingCredentialRequest() { + throw new RuntimeException("Stub!"); + } + + // public void setPendingCredentialRequest(@NonNull GetCredentialRequest request, + // @NonNull OutcomeReceiver callback) { + // throw new RuntimeException("Stub!"); + // } + + // @Nullable + // public ViewCredentialHandler getViewCredentialHandler() { + // throw new RuntimeException("Stub!"); + // } + + public int getHorizontalFadingEdgeLength() { + throw new RuntimeException("Stub!"); + } + + public int getVerticalScrollbarWidth() { + throw new RuntimeException("Stub!"); + } + + protected int getHorizontalScrollbarHeight() { + throw new RuntimeException("Stub!"); + } + + protected void initializeScrollbars(TypedArray a) { + throw new RuntimeException("Stub!"); + } + + protected void initializeScrollbarsInternal(TypedArray a) { + throw new RuntimeException("Stub!"); + } + + public void setVerticalScrollbarThumbDrawable(@Nullable Drawable drawable) { + throw new RuntimeException("Stub!"); + } + + public void setVerticalScrollbarTrackDrawable(@Nullable Drawable drawable) { + throw new RuntimeException("Stub!"); + } + + public void setHorizontalScrollbarThumbDrawable(@Nullable Drawable drawable) { + throw new RuntimeException("Stub!"); + } + + public void setHorizontalScrollbarTrackDrawable(@Nullable Drawable drawable) { + throw new RuntimeException("Stub!"); + } + + public @Nullable Drawable getVerticalScrollbarThumbDrawable() { + throw new RuntimeException("Stub!"); + } + + public @Nullable Drawable getVerticalScrollbarTrackDrawable() { + throw new RuntimeException("Stub!"); + } + + public @Nullable Drawable getHorizontalScrollbarThumbDrawable() { + throw new RuntimeException("Stub!"); + } + + public @Nullable Drawable getHorizontalScrollbarTrackDrawable() { + throw new RuntimeException("Stub!"); + } + + public void setVerticalScrollbarPosition(int position) { + throw new RuntimeException("Stub!"); + } + + public int getVerticalScrollbarPosition() { + throw new RuntimeException("Stub!"); + } + + boolean isOnScrollbar(float x, float y) { + throw new RuntimeException("Stub!"); + } + + boolean isOnScrollbarThumb(float x, float y) { + throw new RuntimeException("Stub!"); + } + + boolean isDraggingScrollBar() { + throw new RuntimeException("Stub!"); + } + + public void setScrollIndicators(@ScrollIndicators int indicators) { + throw new RuntimeException("Stub!"); + } + + public void setScrollIndicators(@ScrollIndicators int indicators, @ScrollIndicators int mask) { + throw new RuntimeException("Stub!"); + } + + @ScrollIndicators + public int getScrollIndicators() { + throw new RuntimeException("Stub!"); + } + + // ListenerInfo getListenerInfo() { + // throw new RuntimeException("Stub!"); + // } + + public void setOnScrollChangeListener(OnScrollChangeListener l) { + throw new RuntimeException("Stub!"); + } + + public void setOnFocusChangeListener(OnFocusChangeListener l) { + throw new RuntimeException("Stub!"); + } + + public void addOnLayoutChangeListener(OnLayoutChangeListener listener) { + throw new RuntimeException("Stub!"); + } + + public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) { + throw new RuntimeException("Stub!"); + } + + public void addOnAttachStateChangeListener(OnAttachStateChangeListener listener) { + throw new RuntimeException("Stub!"); + } + + public void removeOnAttachStateChangeListener(OnAttachStateChangeListener listener) { + throw new RuntimeException("Stub!"); + } + + public OnFocusChangeListener getOnFocusChangeListener() { + throw new RuntimeException("Stub!"); + } + + public void setOnClickListener(@Nullable OnClickListener l) { + throw new RuntimeException("Stub!"); + } + + public boolean hasOnClickListeners() { + throw new RuntimeException("Stub!"); + } + + public void setOnLongClickListener(@Nullable OnLongClickListener l) { + throw new RuntimeException("Stub!"); + } + + public boolean hasOnLongClickListeners() { + throw new RuntimeException("Stub!"); + } + + @Nullable + public OnLongClickListener getOnLongClickListener() { + throw new RuntimeException("Stub!"); + } + + public void setOnContextClickListener(@Nullable OnContextClickListener l) { + throw new RuntimeException("Stub!"); + } + + public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) { + throw new RuntimeException("Stub!"); + } + + public void addFrameMetricsListener(Window window, + Window.OnFrameMetricsAvailableListener listener, + Handler handler) { + throw new RuntimeException("Stub!"); + } + + public void removeFrameMetricsListener( + Window.OnFrameMetricsAvailableListener listener) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + public void setNotifyAutofillManagerOnClick(boolean notify) { + throw new RuntimeException("Stub!"); + } + + // NOTE: other methods on View should not call this method directly, but performClickInternal() + // instead, to guarantee that the autofill manager is notified when necessary (as subclasses + // could extend this method without calling super.performClick()). + public boolean performClick() { + throw new RuntimeException("Stub!"); + } + + public boolean callOnClick() { + throw new RuntimeException("Stub!"); + } + + public boolean performLongClick() { + throw new RuntimeException("Stub!"); + } + + public boolean performLongClick(float x, float y) { + throw new RuntimeException("Stub!"); + } + + public boolean performContextClick(float x, float y) { + throw new RuntimeException("Stub!"); + } + + public boolean performContextClick() { + throw new RuntimeException("Stub!"); + } + + protected boolean performButtonActionOnTouchDown(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean showContextMenu() { + throw new RuntimeException("Stub!"); + } + + public boolean showContextMenu(float x, float y) { + throw new RuntimeException("Stub!"); + } + + public ActionMode startActionMode(ActionMode.Callback callback) { + throw new RuntimeException("Stub!"); + } + + public ActionMode startActionMode(ActionMode.Callback callback, int type) { + throw new RuntimeException("Stub!"); + } + + public void startActivityForResult(Intent intent, int requestCode) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchActivityResult( + String who, int requestCode, int resultCode, Intent data) { + throw new RuntimeException("Stub!"); + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + // Do nothing. + } + + public void setOnKeyListener(OnKeyListener l) { + throw new RuntimeException("Stub!"); + } + + public void setOnTouchListener(OnTouchListener l) { + throw new RuntimeException("Stub!"); + } + + public void setOnGenericMotionListener(OnGenericMotionListener l) { + throw new RuntimeException("Stub!"); + } + + public void setOnHoverListener(OnHoverListener l) { + throw new RuntimeException("Stub!"); + } + + public void setOnDragListener(OnDragListener l) { + throw new RuntimeException("Stub!"); + } + + void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) { + throw new RuntimeException("Stub!"); + } + + public final void setRevealOnFocusHint(boolean revealOnFocus) { + throw new RuntimeException("Stub!"); + } + + public final boolean getRevealOnFocusHint() { + throw new RuntimeException("Stub!"); + } + + public void getHotspotBounds(Rect outRect) { + throw new RuntimeException("Stub!"); + } + + public boolean requestRectangleOnScreen(Rect rectangle) { + throw new RuntimeException("Stub!"); + } + + public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) { + throw new RuntimeException("Stub!"); + } + + public void clearFocus() { + throw new RuntimeException("Stub!"); + } + + public void clearFocusInternal(View focused, boolean propagate, boolean refocus) { + throw new RuntimeException("Stub!"); + } + + void notifyGlobalFocusCleared(View oldFocus) { + throw new RuntimeException("Stub!"); + } + + boolean rootViewRequestFocus() { + throw new RuntimeException("Stub!"); + } + + void unFocus(View focused) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "focus") + public boolean hasFocus() { + throw new RuntimeException("Stub!"); + } + + public boolean hasFocusable() { + throw new RuntimeException("Stub!"); + } + + public boolean hasExplicitFocusable() { + throw new RuntimeException("Stub!"); + } + + boolean hasFocusable(boolean allowAutoFocus, boolean dispatchExplicit) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction, + @Nullable Rect previouslyFocusedRect) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + public void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) { + throw new RuntimeException("Stub!"); + } + + public void setAccessibilityPaneTitle(@Nullable CharSequence accessibilityPaneTitle) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public CharSequence getAccessibilityPaneTitle() { + throw new RuntimeException("Stub!"); + } + + public void sendAccessibilityEvent(int eventType) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void announceForAccessibility(CharSequence text) { + throw new RuntimeException("Stub!"); + } + + public void sendAccessibilityEventInternal(int eventType) { + throw new RuntimeException("Stub!"); + } + + public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public AccessibilityNodeInfo createAccessibilityNodeInfo() { + throw new RuntimeException("Stub!"); + } + + public @Nullable AccessibilityNodeInfo createAccessibilityNodeInfoInternal() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + throw new RuntimeException("Stub!"); + } + + public void getBoundsOnScreen(Rect outRect) { + throw new RuntimeException("Stub!"); + } + + @TestApi + public void getBoundsOnScreen(@NonNull Rect outRect, boolean clipToParent) { + throw new RuntimeException("Stub!"); + } + + public void getBoundsOnScreen(RectF outRect, boolean clipToParent) { + throw new RuntimeException("Stub!"); + } + + public void getBoundsInWindow(Rect outRect, boolean clipToParent) { + throw new RuntimeException("Stub!"); + } + + public void mapRectFromViewToScreenCoords(RectF rect, boolean clipToParent) { + throw new RuntimeException("Stub!"); + } + + public void mapRectFromViewToWindowCoords(RectF rect, boolean clipToParent) { + throw new RuntimeException("Stub!"); + } + + public CharSequence getAccessibilityClassName() { + throw new RuntimeException("Stub!"); + } + + public void onProvideStructure(ViewStructure structure) { + throw new RuntimeException("Stub!"); + } + + public void onProvideAutofillStructure(ViewStructure structure, @AutofillFlags int flags) { + throw new RuntimeException("Stub!"); + } + + public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + protected void onProvideStructure(@NonNull ViewStructure structure, + @ViewStructureType int viewFor, int flags) { + throw new RuntimeException("Stub!"); + } + + public void onProvideVirtualStructure(ViewStructure structure) { + throw new RuntimeException("Stub!"); + } + + public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { + throw new RuntimeException("Stub!"); + } + + // public void setOnReceiveContentListener( + // @SuppressLint("NullableCollection") @Nullable String[] mimeTypes, + // @Nullable OnReceiveContentListener listener) { + // throw new RuntimeException("Stub!"); + // } + + // @Nullable + // public ContentInfo performReceiveContent(@NonNull ContentInfo payload) { + // throw new RuntimeException("Stub!"); + // } + + // @Nullable + // public ContentInfo onReceiveContent(@NonNull ContentInfo payload) { + // throw new RuntimeException("Stub!"); + // } + + @SuppressLint("NullableCollection") + @Nullable + public String[] getReceiveContentMimeTypes() { + throw new RuntimeException("Stub!"); + } + + public void autofill(@SuppressWarnings("unused") AutofillValue value) { + } + + public void autofill(@NonNull @SuppressWarnings("unused") SparseArray values) { + throw new RuntimeException("Stub!"); + } + + // public void onGetCredentialResponse(GetCredentialResponse response) { + // throw new RuntimeException("Stub!"); + // } + + public void onGetCredentialException(String errorType, String errorMsg) { + throw new RuntimeException("Stub!"); + } + + public final AutofillId getAutofillId() { + throw new RuntimeException("Stub!"); + } + + // @Nullable + // public final GetCredentialRequest getPendingCredentialRequest() { + // throw new RuntimeException("Stub!"); + // } + + // @Nullable + // public final OutcomeReceiver getPendingCredentialCallback() { + // throw new RuntimeException("Stub!"); + // } + + public void setAutofillId(@Nullable AutofillId id) { + throw new RuntimeException("Stub!"); + } + + public void resetSubtreeAutofillIds() { + throw new RuntimeException("Stub!"); + } + + public @AutofillType int getAutofillType() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty() + @Nullable public String[] getAutofillHints() { + throw new RuntimeException("Stub!"); + } + + @TestApi + public boolean isAutofilled() { + throw new RuntimeException("Stub!"); + } + + public boolean hideAutofillHighlight() { + throw new RuntimeException("Stub!"); + } + + @Nullable + public AutofillValue getAutofillValue() { + throw new RuntimeException("Stub!"); + } + + public @AutofillImportance int getImportantForAutofill() { + throw new RuntimeException("Stub!"); + } + + public void setImportantForAutofill(@AutofillImportance int mode) { + throw new RuntimeException("Stub!"); + } + + public final boolean isImportantForAutofill() { + throw new RuntimeException("Stub!"); + } + + public final void setContentSensitivity(@ContentSensitivity int mode) { + throw new RuntimeException("Stub!"); + } + + public @ContentSensitivity final int getContentSensitivity() { + throw new RuntimeException("Stub!"); + } + + public final boolean isContentSensitive() { + throw new RuntimeException("Stub!"); + } + + public @ContentCaptureImportance int getImportantForContentCapture() { + throw new RuntimeException("Stub!"); + } + + public void setImportantForContentCapture(@ContentCaptureImportance int mode) { + throw new RuntimeException("Stub!"); + } + + public final boolean isImportantForContentCapture() { + throw new RuntimeException("Stub!"); + } + /** @hide */ + protected boolean getNotifiedContentCaptureAppeared() { + throw new RuntimeException("Stub!"); + } + + + public void setContentCaptureSession(@Nullable ContentCaptureSession contentCaptureSession) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public final ContentCaptureSession getContentCaptureSession() { + throw new RuntimeException("Stub!"); + } + + final boolean isActivityDeniedForAutofillForUnimportantView() { + throw new RuntimeException("Stub!"); + } + + final boolean isMatchingAutofillableHeuristics() { + throw new RuntimeException("Stub!"); + } + /** @hide */ + public boolean canNotifyAutofillEnterExitEvent() { + throw new RuntimeException("Stub!"); + } + + public void dispatchProvideStructure(ViewStructure structure) { + throw new RuntimeException("Stub!"); + } + + public void dispatchProvideAutofillStructure(@NonNull ViewStructure structure, + @AutofillFlags int flags) { + throw new RuntimeException("Stub!"); + } + + public void dispatchInitialProvideContentCaptureStructure() { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + void dispatchProvideContentCaptureStructure() { + throw new RuntimeException("Stub!"); + } + + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + throw new RuntimeException("Stub!"); + } + + + public void addExtraDataToAccessibilityNodeInfo( + @NonNull AccessibilityNodeInfo info, @NonNull String extraDataKey, + @Nullable Bundle arguments) { + } + + public boolean isVisibleToUserForAutofill(int virtualId) { + throw new RuntimeException("Stub!"); + } + + public boolean isVisibleToUser() { + throw new RuntimeException("Stub!"); + } + + protected boolean isVisibleToUser(Rect boundInView) { + throw new RuntimeException("Stub!"); + } + + public AccessibilityDelegate getAccessibilityDelegate() { + throw new RuntimeException("Stub!"); + } + + public void setAccessibilityDelegate(@Nullable AccessibilityDelegate delegate) { + throw new RuntimeException("Stub!"); + } + + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + throw new RuntimeException("Stub!"); + } + + public int getAccessibilityViewId() { + throw new RuntimeException("Stub!"); + } + + public int getAutofillViewId() { + throw new RuntimeException("Stub!"); + } + + public int getAccessibilityWindowId() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "accessibility") + public final @Nullable CharSequence getStateDescription() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "accessibility") + public CharSequence getContentDescription() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "accessibility") + @Nullable + public CharSequence getSupplementalDescription() { + throw new RuntimeException("Stub!"); + } + + public void setStateDescription(@Nullable CharSequence stateDescription) { + throw new RuntimeException("Stub!"); + } + + public void setContentDescription(CharSequence contentDescription) { + throw new RuntimeException("Stub!"); + } + + public void setSupplementalDescription(@Nullable CharSequence supplementalDescription) { + throw new RuntimeException("Stub!"); + } + + public void setAccessibilityTraversalBefore(@IdRes int beforeId) { + throw new RuntimeException("Stub!"); + } + + @IdRes + public int getAccessibilityTraversalBefore() { + throw new RuntimeException("Stub!"); + } + + public void setAccessibilityTraversalAfter(@IdRes int afterId) { + throw new RuntimeException("Stub!"); + } + + @IdRes + public int getAccessibilityTraversalAfter() { + throw new RuntimeException("Stub!"); + } + + @IdRes + @ViewDebug.ExportedProperty(category = "accessibility") + public int getLabelFor() { + throw new RuntimeException("Stub!"); + } + + public void setLabelFor(@IdRes int id) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void onFocusLost() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "focus") + public boolean isFocused() { + throw new RuntimeException("Stub!"); + } + + public View findFocus() { + throw new RuntimeException("Stub!"); + } + + public boolean isScrollContainer() { + throw new RuntimeException("Stub!"); + } + + public void setScrollContainer(boolean isScrollContainer) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + @DrawingCacheQuality + public int getDrawingCacheQuality() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setDrawingCacheQuality(@DrawingCacheQuality int quality) { + throw new RuntimeException("Stub!"); + } + + public boolean getKeepScreenOn() { + throw new RuntimeException("Stub!"); + } + + public void setKeepScreenOn(boolean keepScreenOn) { + throw new RuntimeException("Stub!"); + } + + @IdRes + public int getNextFocusLeftId() { + throw new RuntimeException("Stub!"); + } + + public void setNextFocusLeftId(@IdRes int nextFocusLeftId) { + throw new RuntimeException("Stub!"); + } + + @IdRes + public int getNextFocusRightId() { + throw new RuntimeException("Stub!"); + } + + public void setNextFocusRightId(@IdRes int nextFocusRightId) { + throw new RuntimeException("Stub!"); + } + + @IdRes + public int getNextFocusUpId() { + throw new RuntimeException("Stub!"); + } + + public void setNextFocusUpId(@IdRes int nextFocusUpId) { + throw new RuntimeException("Stub!"); + } + + @IdRes + public int getNextFocusDownId() { + throw new RuntimeException("Stub!"); + } + + public void setNextFocusDownId(@IdRes int nextFocusDownId) { + throw new RuntimeException("Stub!"); + } + + @IdRes + public int getNextFocusForwardId() { + throw new RuntimeException("Stub!"); + } + + public void setNextFocusForwardId(@IdRes int nextFocusForwardId) { + throw new RuntimeException("Stub!"); + } + + @IdRes + public int getNextClusterForwardId() { + return mNextClusterForwardId; + } + + public void setNextClusterForwardId(@IdRes int nextClusterForwardId) { + throw new RuntimeException("Stub!"); + } + + public boolean isShown() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + protected boolean fitSystemWindows(Rect insets) { + throw new RuntimeException("Stub!"); + } + + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + throw new RuntimeException("Stub!"); + } + + public void setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener) { + throw new RuntimeException("Stub!"); + } + + public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { + throw new RuntimeException("Stub!"); + } + + public void setWindowInsetsAnimationCallback( + @Nullable WindowInsetsAnimation.Callback callback) { + throw new RuntimeException("Stub!"); + } + + public boolean hasWindowInsetsAnimationCallback() { + throw new RuntimeException("Stub!"); + } + + public void dispatchWindowInsetsAnimationPrepare( + @NonNull WindowInsetsAnimation animation) { + throw new RuntimeException("Stub!"); + } + + @NonNull + public Bounds dispatchWindowInsetsAnimationStart( + @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds) { + throw new RuntimeException("Stub!"); + } + + @NonNull + public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets, + @NonNull List runningAnimations) { + throw new RuntimeException("Stub!"); + } + + public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) { + throw new RuntimeException("Stub!"); + } + + public void setSystemGestureExclusionRects(@NonNull List rects) { + throw new RuntimeException("Stub!"); + } + + void updateSystemGestureExclusionRects() { + throw new RuntimeException("Stub!"); + } + + @NonNull + public List getSystemGestureExclusionRects() { + throw new RuntimeException("Stub!"); + } + + public final void setPreferKeepClear(boolean preferKeepClear) { + throw new RuntimeException("Stub!"); + } + + public final boolean isPreferKeepClear() { + throw new RuntimeException("Stub!"); + } + + public final void setPreferKeepClearRects(@NonNull List rects) { + throw new RuntimeException("Stub!"); + } + + @NonNull + public final List getPreferKeepClearRects() { + throw new RuntimeException("Stub!"); + } + + @SystemApi + public final void setUnrestrictedPreferKeepClearRects(@NonNull List rects) { + throw new RuntimeException("Stub!"); + } + + @SystemApi + @NonNull + public final List getUnrestrictedPreferKeepClearRects() { + throw new RuntimeException("Stub!"); + } + + void updateKeepClearRects() { + throw new RuntimeException("Stub!"); + } + + @NonNull + List collectPreferKeepClearRects() { + throw new RuntimeException("Stub!"); + } + + @NonNull + List collectUnrestrictedPreferKeepClearRects() { + throw new RuntimeException("Stub!"); + } + + public void setHandwritingBoundsOffsets(float offsetLeft, float offsetTop, + float offsetRight, float offsetBottom) { + throw new RuntimeException("Stub!"); + } + + public float getHandwritingBoundsOffsetLeft() { + throw new RuntimeException("Stub!"); + } + + public float getHandwritingBoundsOffsetTop() { + throw new RuntimeException("Stub!"); + } + + public float getHandwritingBoundsOffsetRight() { + throw new RuntimeException("Stub!"); + } + + public float getHandwritingBoundsOffsetBottom() { + throw new RuntimeException("Stub!"); + } + + + public void setHandwritingArea(@Nullable Rect rect) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public Rect getHandwritingArea() { + throw new RuntimeException("Stub!"); + } + + void updateHandwritingArea() { + throw new RuntimeException("Stub!"); + } + + boolean shouldInitiateHandwriting() { + throw new RuntimeException("Stub!"); + } + + public boolean shouldTrackHandwritingArea() { + throw new RuntimeException("Stub!"); + } + + public void setHandwritingDelegatorCallback(@Nullable Runnable callback) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public Runnable getHandwritingDelegatorCallback() { + throw new RuntimeException("Stub!"); + } + + public void setAllowedHandwritingDelegatePackage(@Nullable String allowedPackageName) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public String getAllowedHandwritingDelegatePackageName() { + throw new RuntimeException("Stub!"); + } + + public void setIsHandwritingDelegate(boolean isHandwritingDelegate) { + throw new RuntimeException("Stub!"); + } + + public boolean isHandwritingDelegate() { + throw new RuntimeException("Stub!"); + } + + public void setAllowedHandwritingDelegatorPackage(@Nullable String allowedPackageName) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public String getAllowedHandwritingDelegatorPackageName() { + throw new RuntimeException("Stub!"); + } + + public void setHandwritingDelegateFlags( + int flags) { + throw new RuntimeException("Stub!"); + } + + public int getHandwritingDelegateFlags() { + throw new RuntimeException("Stub!"); + } + + public void getLocationInSurface(@NonNull @Size(2) int[] location) { + throw new RuntimeException("Stub!"); + } + + public WindowInsets getRootWindowInsets() { + throw new RuntimeException("Stub!"); + } + + public @Nullable WindowInsetsController getWindowInsetsController() { + throw new RuntimeException("Stub!"); + } + + // @Nullable + // public final OnBackInvokedDispatcher findOnBackInvokedDispatcher() { + // throw new RuntimeException("Stub!"); + // } + + @Deprecated + protected boolean computeFitSystemWindows(Rect inoutInsets, Rect outLocalInsets) { + throw new RuntimeException("Stub!"); + } + + public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) { + throw new RuntimeException("Stub!"); + } + + protected boolean hasContentOnApplyWindowInsetsListener() { + throw new RuntimeException("Stub!"); + } + + public void setFitsSystemWindows(boolean fitSystemWindows) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public boolean getFitsSystemWindows() { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + public boolean fitsSystemWindows() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void requestFitSystemWindows() { + throw new RuntimeException("Stub!"); + } + + public void requestApplyInsets() { + throw new RuntimeException("Stub!"); + } + + public void makeOptionalFitsSystemWindows() { + throw new RuntimeException("Stub!"); + } + + public void makeFrameworkOptionalFitsSystemWindows() { + throw new RuntimeException("Stub!"); + } + + public boolean isFrameworkOptionalFitsSystemWindows() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.IntToString(from = VISIBLE, to = "VISIBLE"), + @ViewDebug.IntToString(from = INVISIBLE, to = "INVISIBLE"), + @ViewDebug.IntToString(from = GONE, to = "GONE") + }) + @Visibility + public int getVisibility() { + throw new RuntimeException("Stub!"); + } + + public void setVisibility(@Visibility int visibility) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public boolean isEnabled() { + throw new RuntimeException("Stub!"); + } + + public void setEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + public void setFocusable(boolean focusable) { + throw new RuntimeException("Stub!"); + } + + public void setFocusable(@Focusable int focusable) { + throw new RuntimeException("Stub!"); + } + + public void setFocusableInTouchMode(boolean focusableInTouchMode) { + throw new RuntimeException("Stub!"); + } + + public void setAutofillHints(@Nullable String... autofillHints) { + throw new RuntimeException("Stub!"); + } + + @TestApi + public void setAutofilled(boolean isAutofilled, boolean hideHighlight) { + throw new RuntimeException("Stub!"); + } + + public void setSoundEffectsEnabled(boolean soundEffectsEnabled) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public boolean isSoundEffectsEnabled() { + throw new RuntimeException("Stub!"); + } + + public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public boolean isHapticFeedbackEnabled() { + throw new RuntimeException("Stub!"); + } + + @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") + }) + @LayoutDir + public int getRawLayoutDirection() { + throw new RuntimeException("Stub!"); + } + + public void setLayoutDirection(@LayoutDir int layoutDirection) { + throw new RuntimeException("Stub!"); + } + + @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") + }) + @ResolvedLayoutDir + public int getLayoutDirection() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "layout") + public boolean isLayoutRtl() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "layout") + public boolean hasTransientState() { + throw new RuntimeException("Stub!"); + } + + public void setHasTransientState(boolean hasTransientState) { + throw new RuntimeException("Stub!"); + } + + public void setHasTranslationTransientState(boolean hasTranslationTransientState) { + throw new RuntimeException("Stub!"); + } + + public boolean hasTranslationTransientState() { + throw new RuntimeException("Stub!"); + } + + public void clearTranslationState() { + throw new RuntimeException("Stub!"); + } + + public boolean isAttachedToWindow() { + throw new RuntimeException("Stub!"); + } + + public boolean isLaidOut() { + throw new RuntimeException("Stub!"); + } + + boolean isLayoutValid() { + throw new RuntimeException("Stub!"); + } + + public void setWillNotDraw(boolean willNotDraw) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public boolean willNotDraw() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setWillNotCacheDrawing(boolean willNotCacheDrawing) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + @Deprecated + public boolean willNotCacheDrawing() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public boolean isClickable() { + throw new RuntimeException("Stub!"); + } + + public void setClickable(boolean clickable) { + throw new RuntimeException("Stub!"); + } + + public void setAllowClickWhenDisabled(boolean clickableWhenDisabled) { + throw new RuntimeException("Stub!"); + } + + public boolean isLongClickable() { + throw new RuntimeException("Stub!"); + } + + public void setLongClickable(boolean longClickable) { + throw new RuntimeException("Stub!"); + } + + public boolean isContextClickable() { + throw new RuntimeException("Stub!"); + } + + public void setContextClickable(boolean contextClickable) { + throw new RuntimeException("Stub!"); + } + + public void setPressed(boolean pressed) { + throw new RuntimeException("Stub!"); + } + + protected void dispatchSetPressed(boolean pressed) { + } + + @ViewDebug.ExportedProperty + public boolean isPressed() { + throw new RuntimeException("Stub!"); + } + + public boolean isAssistBlocked() { + throw new RuntimeException("Stub!"); + } + + public void setAssistBlocked(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + public boolean isSaveEnabled() { + throw new RuntimeException("Stub!"); + } + + public void setSaveEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public boolean getFilterTouchesWhenObscured() { + throw new RuntimeException("Stub!"); + } + + public void setFilterTouchesWhenObscured(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + public boolean isSaveFromParentEnabled() { + throw new RuntimeException("Stub!"); + } + + public void setSaveFromParentEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + + @ViewDebug.ExportedProperty(category = "focus") + public final boolean isFocusable() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.IntToString(from = NOT_FOCUSABLE, to = "NOT_FOCUSABLE"), + @ViewDebug.IntToString(from = FOCUSABLE, to = "FOCUSABLE"), + @ViewDebug.IntToString(from = FOCUSABLE_AUTO, to = "FOCUSABLE_AUTO") + }, category = "focus") + @Focusable + public int getFocusable() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "focus") + public final boolean isFocusableInTouchMode() { + throw new RuntimeException("Stub!"); + } + + public boolean isScreenReaderFocusable() { + throw new RuntimeException("Stub!"); + } + + public void setScreenReaderFocusable(boolean screenReaderFocusable) { + throw new RuntimeException("Stub!"); + } + + public boolean isAccessibilityHeading() { + throw new RuntimeException("Stub!"); + } + + public void setAccessibilityHeading(boolean isHeading) { + throw new RuntimeException("Stub!"); + } + + public View focusSearch(@FocusRealDirection int direction) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "focus") + public final boolean isKeyboardNavigationCluster() { + throw new RuntimeException("Stub!"); + } + + View findKeyboardNavigationCluster() { + throw new RuntimeException("Stub!"); + } + + public void setKeyboardNavigationCluster(boolean isCluster) { + throw new RuntimeException("Stub!"); + } + + @TestApi + public final void setFocusedInCluster() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "focus") + public final boolean isFocusedByDefault() { + throw new RuntimeException("Stub!"); + } + + public void setFocusedByDefault(boolean isFocusedByDefault) { + throw new RuntimeException("Stub!"); + } + + boolean hasDefaultFocus() { + throw new RuntimeException("Stub!"); + } + + public View keyboardNavigationClusterSearch(View currentCluster, + @FocusDirection int direction) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchUnhandledMove(View focused, @FocusRealDirection int direction) { + throw new RuntimeException("Stub!"); + } + + public void setDefaultFocusHighlightEnabled(boolean defaultFocusHighlightEnabled) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "focus") + public final boolean getDefaultFocusHighlightEnabled() { + throw new RuntimeException("Stub!"); + } + + View findUserSetNextFocus(View root, @FocusDirection int direction) { + throw new RuntimeException("Stub!"); + } + + View findUserSetNextKeyboardNavigationCluster(View root, @FocusDirection int direction) { + throw new RuntimeException("Stub!"); + } + + public ArrayList getFocusables(@FocusDirection int direction) { + throw new RuntimeException("Stub!"); + } + + public void addFocusables(ArrayList views, @FocusDirection int direction) { + throw new RuntimeException("Stub!"); + } + + public void addFocusables(ArrayList views, @FocusDirection int direction, + @FocusableMode int focusableMode) { + throw new RuntimeException("Stub!"); + } + + public void addKeyboardNavigationClusters( + @NonNull Collection views, + int direction) { + throw new RuntimeException("Stub!"); + } + + public void findViewsWithText(ArrayList outViews, CharSequence searched, + @FindViewFlags int flags) { + throw new RuntimeException("Stub!"); + } + + public ArrayList getTouchables() { + throw new RuntimeException("Stub!"); + } + + public void addTouchables(ArrayList views) { + throw new RuntimeException("Stub!"); + } + + public boolean isAccessibilityFocused() { + throw new RuntimeException("Stub!"); + } + + public boolean requestAccessibilityFocus() { + throw new RuntimeException("Stub!"); + } + + public void clearAccessibilityFocus() { + throw new RuntimeException("Stub!"); + } + + void clearAccessibilityFocusNoCallbacks(int action) { + throw new RuntimeException("Stub!"); + } + + public final boolean requestFocus() { + return requestFocus(View.FOCUS_DOWN); + } + + @TestApi + public boolean restoreFocusInCluster(@FocusRealDirection int direction) { + throw new RuntimeException("Stub!"); + } + + @TestApi + public boolean restoreFocusNotInCluster() { + throw new RuntimeException("Stub!"); + } + + public boolean restoreDefaultFocus() { + throw new RuntimeException("Stub!"); + } + + public final boolean requestFocus(int direction) { + throw new RuntimeException("Stub!"); + } + + public boolean requestFocus(int direction, Rect previouslyFocusedRect) { + throw new RuntimeException("Stub!"); + } + + void clearParentsWantFocus() { + throw new RuntimeException("Stub!"); + } + + public final boolean requestFocusFromTouch() { + throw new RuntimeException("Stub!"); + } + + public int getImportantForAccessibility() { + throw new RuntimeException("Stub!"); + } + + public void setAccessibilityLiveRegion(int mode) { + throw new RuntimeException("Stub!"); + } + + public int getAccessibilityLiveRegion() { + throw new RuntimeException("Stub!"); + } + + public void setImportantForAccessibility(int mode) { + throw new RuntimeException("Stub!"); + } + + public boolean isImportantForAccessibility() { + throw new RuntimeException("Stub!"); + } + + public ViewParent getParentForAccessibility() { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Nullable + View getSelfOrParentImportantForA11y() { + throw new RuntimeException("Stub!"); + } + + public void addChildrenForAccessibility(ArrayList outChildren) { + + } + + public boolean includeForAccessibility() { + throw new RuntimeException("Stub!"); + } + + public boolean includeForAccessibility(boolean forNodeTree) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "accessibility") + public boolean isAccessibilityDataSensitive() { + throw new RuntimeException("Stub!"); + } + + void calculateAccessibilityDataSensitive() { + throw new RuntimeException("Stub!"); + } + + public void setAccessibilityDataSensitive( + @AccessibilityDataSensitive int accessibilityDataSensitive) { + throw new RuntimeException("Stub!"); + } + + public boolean isActionableForAccessibility() { + throw new RuntimeException("Stub!"); + } + + public void notifyViewAccessibilityStateChangedIfNeeded(int changeType) { + throw new RuntimeException("Stub!"); + } + + public void notifySubtreeAccessibilityStateChangedIfNeeded() { + throw new RuntimeException("Stub!"); + } + + public void setTransitionVisibility(@Visibility int visibility) { + throw new RuntimeException("Stub!"); + } + + void resetSubtreeAccessibilityStateChanged() { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchNestedPrePerformAccessibilityAction(int action, + @Nullable Bundle arguments) { + throw new RuntimeException("Stub!"); + } + + public boolean performAccessibilityAction(int action, @Nullable Bundle arguments) { + throw new RuntimeException("Stub!"); + } + + public boolean performAccessibilityActionInternal(int action, @Nullable Bundle arguments) { + throw new RuntimeException("Stub!"); + } + + public CharSequence getIterableTextForAccessibility() { + throw new RuntimeException("Stub!"); + } + + public boolean isAccessibilitySelectionExtendable() { + throw new RuntimeException("Stub!"); + } + + public void prepareForExtendedAccessibilitySelection() { + throw new RuntimeException("Stub!"); + } + + public int getAccessibilitySelectionStart() { + throw new RuntimeException("Stub!"); + } + + public int getAccessibilitySelectionEnd() { + throw new RuntimeException("Stub!"); + } + + public void setAccessibilitySelection(int start, int end) { + throw new RuntimeException("Stub!"); + } + + // public TextSegmentIterator getIteratorForGranularity(int granularity) { + // throw new RuntimeException("Stub!"); + // } + + public final boolean isTemporarilyDetached() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void dispatchStartTemporaryDetach() { + throw new RuntimeException("Stub!"); + } + + public void onStartTemporaryDetach() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void dispatchFinishTemporaryDetach() { + throw new RuntimeException("Stub!"); + } + + public void onFinishTemporaryDetach() { + } + + public KeyEvent.DispatcherState getKeyDispatcherState() { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchKeyEventPreIme(KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchKeyEvent(KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchKeyShortcutEvent(KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchTouchEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + boolean isAccessibilityFocusedViewOrHost() { + throw new RuntimeException("Stub!"); + } + + protected boolean canReceivePointerEvents() { + throw new RuntimeException("Stub!"); + } + + public boolean onFilterTouchEventForSecurity(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchTrackballEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchCapturedPointerEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchGenericMotionEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + protected boolean dispatchHoverEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + protected boolean hasHoveredChild() { + return false; + } + + protected boolean pointInHoveredChild(MotionEvent event) { + return false; + } + + protected boolean dispatchGenericPointerEvent(MotionEvent event) { + return false; + } + + protected boolean dispatchGenericFocusedEvent(MotionEvent event) { + return false; + } + + public final boolean dispatchPointerEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + public void dispatchWindowFocusChanged(boolean hasFocus) { + throw new RuntimeException("Stub!"); + } + + public void onWindowFocusChanged(boolean hasWindowFocus) { + throw new RuntimeException("Stub!"); + } + + public boolean hasWindowFocus() { + throw new RuntimeException("Stub!"); + } + + public boolean hasImeFocus() { + throw new RuntimeException("Stub!"); + } + + protected void dispatchVisibilityChanged(@NonNull View changedView, + @Visibility int visibility) { + throw new RuntimeException("Stub!"); + } + + protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility) { + } + + public void dispatchDisplayHint(@Visibility int hint) { + throw new RuntimeException("Stub!"); + } + + protected void onDisplayHint(@Visibility int hint) { + } + + public void dispatchWindowVisibilityChanged(@Visibility int visibility) { + throw new RuntimeException("Stub!"); + } + + protected void onWindowVisibilityChanged(@Visibility int visibility) { + throw new RuntimeException("Stub!"); + } + + public boolean isAggregatedVisible() { + throw new RuntimeException("Stub!"); + } + + boolean dispatchVisibilityAggregated(boolean isVisible) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void onVisibilityAggregated(boolean isVisible) { + throw new RuntimeException("Stub!"); + } + + @Visibility + public int getWindowVisibility() { + throw new RuntimeException("Stub!"); + } + + public void getWindowVisibleDisplayFrame(Rect outRect) { + throw new RuntimeException("Stub!"); + } + + @TestApi + public void getWindowDisplayFrame(@NonNull Rect outRect) { + throw new RuntimeException("Stub!"); + } + + public void dispatchConfigurationChanged(Configuration newConfig) { + throw new RuntimeException("Stub!"); + } + + protected void onConfigurationChanged(Configuration newConfig) { + } + + // void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) { + // throw new RuntimeException("Stub!"); + // } + + // void performCollectViewAttributes(AttachInfo attachInfo, int visibility) { + // throw new RuntimeException("Stub!"); + // } + + void needGlobalAttributesUpdate(boolean force) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public boolean isInTouchMode() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.CapturedViewProperty + public final Context getContext() { + return mContext; + } + + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean onKeyDown(int keyCode, KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean onKeyUp(int keyCode, KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { + return false; + } + + public boolean onKeyShortcut(int keyCode, KeyEvent event) { + return false; + } + + public boolean onCheckIsTextEditor() { + return false; + } + + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + return null; + } + + public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection, + @NonNull EditorInfo editorInfo, @Nullable Handler handler) {} + + public void onInputConnectionClosedInternal() {} + + public boolean checkInputConnectionProxy(View view) { + return false; + } + + public void createContextMenu(ContextMenu menu) { + throw new RuntimeException("Stub!"); + } + + protected ContextMenuInfo getContextMenuInfo() { + return null; + } + + protected void onCreateContextMenu(ContextMenu menu) { + } + + public boolean onTrackballEvent(MotionEvent event) { + return false; + } + + public boolean onGenericMotionEvent(MotionEvent event) { + return false; + } + + public boolean onHoverEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public boolean isHovered() { + throw new RuntimeException("Stub!"); + } + + public void setHovered(boolean hovered) { + throw new RuntimeException("Stub!"); + } + + public void onHoverChanged(boolean hovered) { + } + + protected boolean handleScrollBarDragging(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean onTouchEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean isInScrollingContainer() { + throw new RuntimeException("Stub!"); + } + + public void cancelLongPress() { + throw new RuntimeException("Stub!"); + } + + public void setTouchDelegate(TouchDelegate delegate) { + throw new RuntimeException("Stub!"); + } + + public TouchDelegate getTouchDelegate() { + throw new RuntimeException("Stub!"); + } + + public final void requestUnbufferedDispatch(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + public final void requestUnbufferedDispatch(int source) { + throw new RuntimeException("Stub!"); + } + + void setFlags(int flags, int mask) { + throw new RuntimeException("Stub!"); + } + + public void bringToFront() { + throw new RuntimeException("Stub!"); + } + + void disableRotaryScrollFeedback() { + throw new RuntimeException("Stub!"); + } + + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + throw new RuntimeException("Stub!"); + } + + public interface OnScrollChangeListener { + void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY); + } + + public interface OnLayoutChangeListener { + void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom); + } + + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + } + + protected void dispatchDraw(@NonNull Canvas canvas) { + + } + + public final ViewParent getParent() { + throw new RuntimeException("Stub!"); + } + + public void setScrollX(int value) { + throw new RuntimeException("Stub!"); + } + + public void setScrollY(int value) { + throw new RuntimeException("Stub!"); + } + + public final int getScrollX() { + throw new RuntimeException("Stub!"); + } + + public final int getScrollY() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "layout") + public final int getWidth() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "layout") + public final int getHeight() { + throw new RuntimeException("Stub!"); + } + + public void getDrawingRect(Rect outRect) { + throw new RuntimeException("Stub!"); + } + + public final int getMeasuredWidth() { + throw new RuntimeException("Stub!"); + } + + public final int getMeasuredWidthAndState() { + throw new RuntimeException("Stub!"); + } + + public final int getMeasuredHeight() { + throw new RuntimeException("Stub!"); + } + + public final int getMeasuredHeightAndState() { + throw new RuntimeException("Stub!"); + } + + public final int getMeasuredState() { + throw new RuntimeException("Stub!"); + } + + public Matrix getMatrix() { + throw new RuntimeException("Stub!"); + } + + public final boolean hasIdentityMatrix() { + throw new RuntimeException("Stub!"); + } + + void ensureTransformationInfo() { + throw new RuntimeException("Stub!"); + } + + public final Matrix getInverseMatrix() { + throw new RuntimeException("Stub!"); + } + + public float getCameraDistance() { + throw new RuntimeException("Stub!"); + } + + public void setCameraDistance(float distance) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getRotation() { + throw new RuntimeException("Stub!"); + } + + public void setRotation(float rotation) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getRotationY() { + throw new RuntimeException("Stub!"); + } + + public void setRotationY(float rotationY) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getRotationX() { + throw new RuntimeException("Stub!"); + } + + public void setRotationX(float rotationX) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getScaleX() { + throw new RuntimeException("Stub!"); + } + + public void setScaleX(float scaleX) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getScaleY() { + throw new RuntimeException("Stub!"); + } + + public void setScaleY(float scaleY) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getPivotX() { + throw new RuntimeException("Stub!"); + } + + public void setPivotX(float pivotX) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getPivotY() { + throw new RuntimeException("Stub!"); + } + + public void setPivotY(float pivotY) { + throw new RuntimeException("Stub!"); + } + + public boolean isPivotSet() { + throw new RuntimeException("Stub!"); + } + + public void resetPivot() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getAlpha() { + throw new RuntimeException("Stub!"); + } + + public void forceHasOverlappingRendering(boolean hasOverlappingRendering) { + throw new RuntimeException("Stub!"); + } + + public final boolean getHasOverlappingRendering() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public boolean hasOverlappingRendering() { + throw new RuntimeException("Stub!"); + } + + public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) { + throw new RuntimeException("Stub!"); + } + + boolean setAlphaNoInvalidation(float alpha) { + throw new RuntimeException("Stub!"); + } + + void setAlphaInternal(float alpha) { + throw new RuntimeException("Stub!"); + } + + public void setTransitionAlpha(float alpha) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getTransitionAlpha() { + throw new RuntimeException("Stub!"); + } + + public void setForceDarkAllowed(boolean allow) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public boolean isForceDarkAllowed() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.CapturedViewProperty + public final int getTop() { + throw new RuntimeException("Stub!"); + } + + public final void setTop(int top) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.CapturedViewProperty + public final int getBottom() { + throw new RuntimeException("Stub!"); + } + + public boolean isDirty() { + throw new RuntimeException("Stub!"); + } + + public final void setBottom(int bottom) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.CapturedViewProperty + public final int getLeft() { + throw new RuntimeException("Stub!"); + } + + public final void setLeft(int left) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.CapturedViewProperty + public final int getRight() { + throw new RuntimeException("Stub!"); + } + + public final void setRight(int right) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getX() { + throw new RuntimeException("Stub!"); + } + + public void setX(float x) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getY() { + throw new RuntimeException("Stub!"); + } + + public void setY(float y) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getZ() { + throw new RuntimeException("Stub!"); + } + + public void setZ(float z) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getElevation() { + throw new RuntimeException("Stub!"); + } + + public void setElevation(float elevation) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getTranslationX() { + throw new RuntimeException("Stub!"); + } + + public void setTranslationX(float translationX) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getTranslationY() { + throw new RuntimeException("Stub!"); + } + + public void setTranslationY(float translationY) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public float getTranslationZ() { + throw new RuntimeException("Stub!"); + } + + public void setTranslationZ(float translationZ) { + throw new RuntimeException("Stub!"); + } + + public void setAnimationMatrix(@Nullable Matrix matrix) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public Matrix getAnimationMatrix() { + throw new RuntimeException("Stub!"); + } + + public StateListAnimator getStateListAnimator() { + throw new RuntimeException("Stub!"); + } + + public void setStateListAnimator(StateListAnimator stateListAnimator) { + throw new RuntimeException("Stub!"); + } + + public final boolean getClipToOutline() { + throw new RuntimeException("Stub!"); + } + + public void setClipToOutline(boolean clipToOutline) { + throw new RuntimeException("Stub!"); + } + + + public void setOutlineProvider(ViewOutlineProvider provider) { + throw new RuntimeException("Stub!"); + } + + public ViewOutlineProvider getOutlineProvider() { + throw new RuntimeException("Stub!"); + } + + public void invalidateOutline() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public boolean hasShadow() { + throw new RuntimeException("Stub!"); + } + + public void setOutlineSpotShadowColor(@ColorInt int color) { + throw new RuntimeException("Stub!"); + } + + public @ColorInt int getOutlineSpotShadowColor() { + throw new RuntimeException("Stub!"); + } + + public void setOutlineAmbientShadowColor(@ColorInt int color) { + throw new RuntimeException("Stub!"); + } + + public @ColorInt int getOutlineAmbientShadowColor() { + throw new RuntimeException("Stub!"); + } + + + /** @hide */ + public void setRevealClip(boolean shouldClip, float x, float y, float radius) { + throw new RuntimeException("Stub!"); + } + + public void getHitRect(Rect outRect) { + throw new RuntimeException("Stub!"); + } + + /*package*/ final boolean pointInView(float localX, float localY) { + throw new RuntimeException("Stub!"); + } + + public boolean pointInView(float localX, float localY, float slop) { + throw new RuntimeException("Stub!"); + } + + public void getFocusedRect(Rect r) { + throw new RuntimeException("Stub!"); + } + + public boolean getGlobalVisibleRect(Rect r, Point globalOffset) { + throw new RuntimeException("Stub!"); + } + + public final boolean getGlobalVisibleRect(Rect r) { + throw new RuntimeException("Stub!"); + } + + public final boolean getLocalVisibleRect(Rect r) { + throw new RuntimeException("Stub!"); + } + + public void offsetTopAndBottom(int offset) { + throw new RuntimeException("Stub!"); + } + + public void offsetLeftAndRight(int offset) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(deepExport = true, prefix = "layout_") + public ViewGroup.LayoutParams getLayoutParams() { + throw new RuntimeException("Stub!"); + } + + public void setLayoutParams(ViewGroup.LayoutParams params) { + throw new RuntimeException("Stub!"); + } + + public void resolveLayoutParams() { + throw new RuntimeException("Stub!"); + } + + public void scrollTo(int x, int y) { + throw new RuntimeException("Stub!"); + } + + public void scrollBy(int x, int y) { + throw new RuntimeException("Stub!"); + } + + protected boolean awakenScrollBars() { + throw new RuntimeException("Stub!"); + } + + protected boolean awakenScrollBars(int startDelay) { + throw new RuntimeException("Stub!"); + } + + protected boolean awakenScrollBars(int startDelay, boolean invalidate) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void invalidate(Rect dirty) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void invalidate(int l, int t, int r, int b) { + throw new RuntimeException("Stub!"); + } + + public void invalidate() { + throw new RuntimeException("Stub!"); + } + + public void invalidate(boolean invalidateCache) { + throw new RuntimeException("Stub!"); + } + + void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, + boolean fullInvalidate) { + throw new RuntimeException("Stub!"); + } + + void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) { + throw new RuntimeException("Stub!"); + } + + protected void damageInParent() { + throw new RuntimeException("Stub!"); + } + + protected void invalidateParentCaches() { + throw new RuntimeException("Stub!"); + } + + protected void invalidateParentIfNeeded() { + throw new RuntimeException("Stub!"); + } + + protected void invalidateParentIfNeededAndWasQuickRejected() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public boolean isOpaque() { + throw new RuntimeException("Stub!"); + } + + protected void computeOpaqueFlags() { + throw new RuntimeException("Stub!"); + } + + protected boolean hasOpaqueScrollbars() { + throw new RuntimeException("Stub!"); + } + + public Handler getHandler() { + throw new RuntimeException("Stub!"); + } + + // public ViewRootImpl getViewRootImpl() { + // throw new RuntimeException("Stub!"); + // } + + // public ThreadedRenderer getThreadedRenderer() { + // throw new RuntimeException("Stub!"); + // } + + public boolean post(Runnable action) { + throw new RuntimeException("Stub!"); + } + + public boolean postDelayed(Runnable action, long delayMillis) { + throw new RuntimeException("Stub!"); + } + + public void postOnAnimation(Runnable action) { + throw new RuntimeException("Stub!"); + } + + public void postOnAnimationDelayed(Runnable action, long delayMillis) { + throw new RuntimeException("Stub!"); + } + + public boolean removeCallbacks(Runnable action) { + throw new RuntimeException("Stub!"); + } + + public void postInvalidate() { + throw new RuntimeException("Stub!"); + } + + public void postInvalidate(int left, int top, int right, int bottom) { + throw new RuntimeException("Stub!"); + } + + public void postInvalidateDelayed(long delayMilliseconds) { + throw new RuntimeException("Stub!"); + } + + public void postInvalidateDelayed(long delayMilliseconds, int left, int top, + int right, int bottom) { + throw new RuntimeException("Stub!"); + } + + public void postInvalidateOnAnimation() { + throw new RuntimeException("Stub!"); + } + + public void postInvalidateOnAnimation(int left, int top, int right, int bottom) { + throw new RuntimeException("Stub!"); + } + + public void computeScroll() { + } + + public boolean isHorizontalFadingEdgeEnabled() { + throw new RuntimeException("Stub!"); + } + + public void setHorizontalFadingEdgeEnabled(boolean horizontalFadingEdgeEnabled) { + throw new RuntimeException("Stub!"); + } + + public boolean isVerticalFadingEdgeEnabled() { + throw new RuntimeException("Stub!"); + } + + public void setVerticalFadingEdgeEnabled(boolean verticalFadingEdgeEnabled) { + throw new RuntimeException("Stub!"); + } + + public int getFadingEdge() { + throw new RuntimeException("Stub!"); + } + + public int getFadingEdgeLength() { + throw new RuntimeException("Stub!"); + } + + protected float getTopFadingEdgeStrength() { + throw new RuntimeException("Stub!"); + } + + protected float getBottomFadingEdgeStrength() { + throw new RuntimeException("Stub!"); + } + + protected float getLeftFadingEdgeStrength() { + throw new RuntimeException("Stub!"); + } + + protected float getRightFadingEdgeStrength() { + throw new RuntimeException("Stub!"); + } + + public boolean isHorizontalScrollBarEnabled() { + throw new RuntimeException("Stub!"); + } + + public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) { + throw new RuntimeException("Stub!"); + } + + public boolean isVerticalScrollBarEnabled() { + throw new RuntimeException("Stub!"); + } + + public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) { + throw new RuntimeException("Stub!"); + } + + protected void recomputePadding() { + throw new RuntimeException("Stub!"); + } + + public void setScrollbarFadingEnabled(boolean fadeScrollbars) { + throw new RuntimeException("Stub!"); + } + + public boolean isScrollbarFadingEnabled() { + throw new RuntimeException("Stub!"); + } + + public int getScrollBarDefaultDelayBeforeFade() { + throw new RuntimeException("Stub!"); + } + + public void setScrollBarDefaultDelayBeforeFade(int scrollBarDefaultDelayBeforeFade) { + throw new RuntimeException("Stub!"); + } + + public int getScrollBarFadeDuration() { + throw new RuntimeException("Stub!"); + } + + public void setScrollBarFadeDuration(int scrollBarFadeDuration) { + throw new RuntimeException("Stub!"); + } + + public int getScrollBarSize() { + throw new RuntimeException("Stub!"); + } + + public void setScrollBarSize(int scrollBarSize) { + throw new RuntimeException("Stub!"); + } + + public void setScrollBarStyle(@ScrollBarStyle int style) { + throw new RuntimeException("Stub!"); + } + + @ScrollBarStyle + public int getScrollBarStyle() { + throw new RuntimeException("Stub!"); + } + + protected int computeHorizontalScrollRange() { + throw new RuntimeException("Stub!"); + } + + protected int computeHorizontalScrollOffset() { + throw new RuntimeException("Stub!"); + } + + protected int computeHorizontalScrollExtent() { + throw new RuntimeException("Stub!"); + } + + protected int computeVerticalScrollRange() { + throw new RuntimeException("Stub!"); + } + + protected int computeVerticalScrollOffset() { + throw new RuntimeException("Stub!"); + } + + protected int computeVerticalScrollExtent() { + throw new RuntimeException("Stub!"); + } + + public boolean canScrollHorizontally(int direction) { + throw new RuntimeException("Stub!"); + } + + public boolean canScrollVertically(int direction) { + throw new RuntimeException("Stub!"); + } + + void getScrollIndicatorBounds(@NonNull Rect out) { + throw new RuntimeException("Stub!"); + } + + protected final void onDrawScrollBars(@NonNull Canvas canvas) { + throw new RuntimeException("Stub!"); + } + + protected boolean isVerticalScrollBarHidden() { + return false; + } + + protected void onDrawHorizontalScrollBar(@NonNull Canvas canvas, Drawable scrollBar, + int l, int t, int r, int b) { + throw new RuntimeException("Stub!"); + } + + protected void onDrawVerticalScrollBar(@NonNull Canvas canvas, Drawable scrollBar, + int l, int t, int r, int b) { + throw new RuntimeException("Stub!"); + } + + protected void onDraw(@NonNull Canvas canvas) { + } + + /* + * Caller is responsible for calling requestLayout if necessary. + * (This allows addViewInLayout to not request a new layout.) + */ + void assignParent(ViewParent parent) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void onAttachedToWindow() { + throw new RuntimeException("Stub!"); + } + + public boolean resolveRtlPropertiesIfNeeded() { + throw new RuntimeException("Stub!"); + } + + @TestApi + public void resetRtlProperties() { + throw new RuntimeException("Stub!"); + } + + void dispatchScreenStateChanged(int screenState) { + throw new RuntimeException("Stub!"); + } + + public void onScreenStateChanged(int screenState) { + } + + void dispatchMovedToDisplay(Display display, Configuration config) { + throw new RuntimeException("Stub!"); + } + + public void onMovedToDisplay(int displayId, Configuration config) { + } + + public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) { + } + + public boolean resolveLayoutDirection() { + throw new RuntimeException("Stub!"); + } + + public boolean canResolveLayoutDirection() { + throw new RuntimeException("Stub!"); + } + + @TestApi + public void resetResolvedLayoutDirection() { + throw new RuntimeException("Stub!"); + } + + public boolean isLayoutDirectionInherited() { + throw new RuntimeException("Stub!"); + } + + public boolean isLayoutDirectionResolved() { + throw new RuntimeException("Stub!"); + } + + boolean isPaddingResolved() { + throw new RuntimeException("Stub!"); + } + + public void resolvePadding() { + throw new RuntimeException("Stub!"); + } + + @TestApi + public void resetResolvedPadding() { + throw new RuntimeException("Stub!"); + } + + void resetResolvedPaddingInternal() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void onDetachedFromWindow() { + } + + @CallSuper + protected void onDetachedFromWindowInternal() { + throw new RuntimeException("Stub!"); + } + + public IBinder getWindowToken() { + throw new RuntimeException("Stub!"); + } + + public WindowId getWindowId() { + throw new RuntimeException("Stub!"); + } + + public IBinder getApplicationWindowToken() { + throw new RuntimeException("Stub!"); + } + + public Display getDisplay() { + throw new RuntimeException("Stub!"); + } + + // /*package*/ IWindowSession getWindowSession() { + // throw new RuntimeException("Stub!"); + // } + + // protected IWindow getWindow() { + // throw new RuntimeException("Stub!"); + // } + + int combineVisibility(int vis1, int vis2) { + throw new RuntimeException("Stub!"); + } + + public void fakeFocusAfterAttachingToWindow() { + throw new RuntimeException("Stub!"); + } + + // void dispatchAttachedToWindow(AttachInfo info, int visibility) { + // throw new RuntimeException("Stub!"); + // } + + void dispatchDetachedFromWindow() { + throw new RuntimeException("Stub!"); + } + + public final void cancelPendingInputEvents() { + throw new RuntimeException("Stub!"); + } + + void dispatchCancelPendingInputEvents() { + throw new RuntimeException("Stub!"); + } + + public void onCancelPendingInputEvents() { + throw new RuntimeException("Stub!"); + } + + public void saveHierarchyState(SparseArray container) { + throw new RuntimeException("Stub!"); + } + + protected void dispatchSaveInstanceState(SparseArray container) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + @Nullable protected Parcelable onSaveInstanceState() { + throw new RuntimeException("Stub!"); + } + + public void restoreHierarchyState(SparseArray container) { + throw new RuntimeException("Stub!"); + } + + protected void dispatchRestoreInstanceState(SparseArray container) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void onRestoreInstanceState(Parcelable state) { + throw new RuntimeException("Stub!"); + } + + public long getDrawingTime() { + throw new RuntimeException("Stub!"); + } + + public void setDuplicateParentStateEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + public boolean isDuplicateParentStateEnabled() { + throw new RuntimeException("Stub!"); + } + + public void setLayerType(@LayerType int layerType, @Nullable Paint paint) { + throw new RuntimeException("Stub!"); + } + + // public void setRenderEffect(@Nullable RenderEffect renderEffect) { + // throw new RuntimeException("Stub!"); + // } + + // public void setBackdropRenderEffect(@Nullable RenderEffect renderEffect) { + // throw new RuntimeException("Stub!"); + // } + + public void setLayerPaint(@Nullable Paint paint) { + throw new RuntimeException("Stub!"); + } + + @LayerType + public int getLayerType() { + throw new RuntimeException("Stub!"); + } + + public void buildLayer() { + throw new RuntimeException("Stub!"); + } + + public boolean probablyHasInput() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void destroyHardwareResources() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setDrawingCacheEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + @ViewDebug.ExportedProperty(category = "drawing") + public boolean isDrawingCacheEnabled() { + throw new RuntimeException("Stub!"); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void outputDirtyFlags(String indent, boolean clear, int clearMask) { + throw new RuntimeException("Stub!"); + } + + protected void dispatchGetDisplayList() {} + + public boolean canHaveDisplayList() { + throw new RuntimeException("Stub!"); + } + + @NonNull + public RenderNode updateDisplayListIfDirty() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public Bitmap getDrawingCache() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public Bitmap getDrawingCache(boolean autoScale) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void destroyDrawingCache() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setDrawingCacheBackgroundColor(@ColorInt int color) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + @ColorInt + public int getDrawingCacheBackgroundColor() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void buildDrawingCache() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void buildDrawingCache(boolean autoScale) { + throw new RuntimeException("Stub!"); + } + + // public Bitmap createSnapshot(ViewDebug.CanvasProvider canvasProvider, boolean skipChildren) { + // throw new RuntimeException("Stub!"); + // } + + public boolean isInEditMode() { + return false; + } + + protected boolean isPaddingOffsetRequired() { + return false; + } + + protected int getLeftPaddingOffset() { + return 0; + } + + protected int getRightPaddingOffset() { + return 0; + } + + protected int getTopPaddingOffset() { + return 0; + } + + protected int getBottomPaddingOffset() { + return 0; + } + + protected int getFadeTop(boolean offsetRequired) { + throw new RuntimeException("Stub!"); + } + + protected int getFadeHeight(boolean offsetRequired) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public boolean isHardwareAccelerated() { + throw new RuntimeException("Stub!"); + } + + public void setClipBounds(Rect clipBounds) { + throw new RuntimeException("Stub!"); + } + + public Rect getClipBounds() { + throw new RuntimeException("Stub!"); + } + + + public boolean getClipBounds(Rect outRect) { + throw new RuntimeException("Stub!"); + } + + void setDisplayListProperties(RenderNode renderNode) { + throw new RuntimeException("Stub!"); + } + + protected final boolean drawsWithRenderNode(@NonNull Canvas canvas) { + throw new RuntimeException("Stub!"); + } + + boolean draw(@NonNull Canvas canvas, ViewGroup parent, long drawingTime) { + throw new RuntimeException("Stub!"); + } + + static Paint getDebugPaint() { + throw new RuntimeException("Stub!"); + } + + final int dipsToPixels(int dips) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void draw(@NonNull Canvas canvas) { + throw new RuntimeException("Stub!"); + } + + void setBackgroundBounds() { + throw new RuntimeException("Stub!"); + } + + public ViewOverlay getOverlay() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + @ColorInt + public int getSolidColor() { + return 0; + } + + public boolean isLayoutRequested() { + throw new RuntimeException("Stub!"); + } + + public static boolean isLayoutModeOptical(Object o) { + throw new RuntimeException("Stub!"); + } + + public static void setTraceLayoutSteps(boolean traceLayoutSteps) { + throw new RuntimeException("Stub!"); + } + + public static void setTracedRequestLayoutClassClass(String s) { + throw new RuntimeException("Stub!"); + } + + @SuppressWarnings({"unchecked"}) + public void layout(int l, int t, int r, int b) { + throw new RuntimeException("Stub!"); + } + + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + } + + protected boolean setFrame(int left, int top, int right, int bottom) { + throw new RuntimeException("Stub!"); + } + + public final void setLeftTopRightBottom(int left, int top, int right, int bottom) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void onFinishInflate() { + } + + public Resources getResources() { + throw new RuntimeException("Stub!"); + } + + @Override + public void invalidateDrawable(@NonNull Drawable drawable) { + throw new RuntimeException("Stub!"); + } + + @Override + public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { + throw new RuntimeException("Stub!"); + } + + @Override + public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { + throw new RuntimeException("Stub!"); + } + + public void unscheduleDrawable(Drawable who) { + throw new RuntimeException("Stub!"); + } + + protected void resolveDrawables() { + throw new RuntimeException("Stub!"); + } + + boolean areDrawablesResolved() { + throw new RuntimeException("Stub!"); + } + + public void onResolveDrawables(@ResolvedLayoutDir int layoutDirection) { + } + + @TestApi + protected void resetResolvedDrawables() { + throw new RuntimeException("Stub!"); + } + + void resetResolvedDrawablesInternal() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected boolean verifyDrawable(@NonNull Drawable who) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void drawableStateChanged() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void drawableHotspotChanged(float x, float y) { + throw new RuntimeException("Stub!"); + } + + public void dispatchDrawableHotspotChanged(float x, float y) { + } + + public void refreshDrawableState() { + throw new RuntimeException("Stub!"); + } + + @TestApi + public boolean isDefaultFocusHighlightNeeded(Drawable background, Drawable foreground) { + throw new RuntimeException("Stub!"); + } + + public final int[] getDrawableState() { + throw new RuntimeException("Stub!"); + } + + protected int[] onCreateDrawableState(int extraSpace) { + throw new RuntimeException("Stub!"); + } + + protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void jumpDrawablesToCurrentState() { + throw new RuntimeException("Stub!"); + } + + public void setBackgroundColor(@ColorInt int color) { + throw new RuntimeException("Stub!"); + } + + public void setBackgroundResource(@DrawableRes int resid) { + throw new RuntimeException("Stub!"); + } + + public void setBackground(Drawable background) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setBackgroundDrawable(Drawable background) { + throw new RuntimeException("Stub!"); + } + + public Drawable getBackground() { + throw new RuntimeException("Stub!"); + } + + public void setBackgroundTintList(@Nullable ColorStateList tint) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public ColorStateList getBackgroundTintList() { + throw new RuntimeException("Stub!"); + } + + public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { + throw new RuntimeException("Stub!"); + } + + public void setBackgroundTintBlendMode(@Nullable BlendMode blendMode) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public PorterDuff.Mode getBackgroundTintMode() { + throw new RuntimeException("Stub!"); + } + + public @Nullable BlendMode getBackgroundTintBlendMode() { + throw new RuntimeException("Stub!"); + } + + public Drawable getForeground() { + throw new RuntimeException("Stub!"); + } + + public void setForeground(Drawable foreground) { + throw new RuntimeException("Stub!"); + } + + public boolean isForegroundInsidePadding() { + throw new RuntimeException("Stub!"); + } + + public int getForegroundGravity() { + throw new RuntimeException("Stub!"); + } + + public void setForegroundGravity(int gravity) { + throw new RuntimeException("Stub!"); + } + + public void setForegroundTintList(@Nullable ColorStateList tint) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public ColorStateList getForegroundTintList() { + throw new RuntimeException("Stub!"); + } + + public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) { + throw new RuntimeException("Stub!"); + } + + public void setForegroundTintBlendMode(@Nullable BlendMode blendMode) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public PorterDuff.Mode getForegroundTintMode() { + throw new RuntimeException("Stub!"); + } + + public @Nullable BlendMode getForegroundTintBlendMode() { + throw new RuntimeException("Stub!"); + } + + public void onDrawForeground(@NonNull Canvas canvas) { + throw new RuntimeException("Stub!"); + } + + public void setPadding(int left, int top, int right, int bottom) { + throw new RuntimeException("Stub!"); + } + + protected void internalSetPadding(int left, int top, int right, int bottom) { + throw new RuntimeException("Stub!"); + } + + public void setPaddingRelative(int start, int top, int end, int bottom) { + throw new RuntimeException("Stub!"); + } + + @LayoutRes + public int getSourceLayoutResId() { + throw new RuntimeException("Stub!"); + } + + public int getPaddingTop() { + throw new RuntimeException("Stub!"); + } + + public int getPaddingBottom() { + throw new RuntimeException("Stub!"); + } + + public int getPaddingLeft() { + throw new RuntimeException("Stub!"); + } + + public int getPaddingStart() { + throw new RuntimeException("Stub!"); + } + + public int getPaddingRight() { + throw new RuntimeException("Stub!"); + } + + public int getPaddingEnd() { + throw new RuntimeException("Stub!"); + } + + public boolean isPaddingRelative() { + throw new RuntimeException("Stub!"); + } + + Insets computeOpticalInsets() { + throw new RuntimeException("Stub!"); + } + + public void resetPaddingToInitialValues() { + throw new RuntimeException("Stub!"); + } + + public Insets getOpticalInsets() { + throw new RuntimeException("Stub!"); + } + + public void setOpticalInsets(Insets insets) { + throw new RuntimeException("Stub!"); + } + + public void setSelected(boolean selected) { + throw new RuntimeException("Stub!"); + } + + protected void dispatchSetSelected(boolean selected) { + } + + @ViewDebug.ExportedProperty + public boolean isSelected() { + throw new RuntimeException("Stub!"); + } + + public void setActivated(boolean activated) { + throw new RuntimeException("Stub!"); + } + + protected void dispatchSetActivated(boolean activated) { + } + + @ViewDebug.ExportedProperty + public boolean isActivated() { + throw new RuntimeException("Stub!"); + } + + public ViewTreeObserver getViewTreeObserver() { + throw new RuntimeException("Stub!"); + } + + public View getRootView() { + throw new RuntimeException("Stub!"); + } + + public boolean toGlobalMotionEvent(MotionEvent ev) { + throw new RuntimeException("Stub!"); + } + + public boolean toLocalMotionEvent(MotionEvent ev) { + throw new RuntimeException("Stub!"); + } + + public void transformMatrixToGlobal(@NonNull Matrix matrix) { + throw new RuntimeException("Stub!"); + } + + public void transformMatrixToLocal(@NonNull Matrix matrix) { + throw new RuntimeException("Stub!"); + } + + public void transformMatrixRootToLocal(@NonNull Matrix matrix) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "layout", indexMapping = { + @ViewDebug.IntToString(from = 0, to = "x"), + @ViewDebug.IntToString(from = 1, to = "y") + }) + public int[] getLocationOnScreen() { + throw new RuntimeException("Stub!"); + } + + public void getLocationOnScreen(@Size(2) int[] outLocation) { + throw new RuntimeException("Stub!"); + } + + public void getLocationInWindow(@Size(2) int[] outLocation) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + public void transformFromViewToWindowSpace(@Size(2) int[] inOutLocation) { + throw new RuntimeException("Stub!"); + } + + protected T findViewTraversal(@IdRes int id) { + throw new RuntimeException("Stub!"); + } + + protected T findViewWithTagTraversal(Object tag) { + throw new RuntimeException("Stub!"); + } + + protected T findViewByPredicateTraversal(Predicate predicate, + View childToSkip) { + throw new RuntimeException("Stub!"); + } + + // Strictly speaking this should be marked as @Nullable but the nullability of the return value + // is deliberately left unspecified as idiomatically correct code can make assumptions either + // way based on local context, e.g. layout specification. + public final T findViewById(@IdRes int id) { + throw new RuntimeException("Stub!"); + } + + @NonNull + public final T requireViewById(@IdRes int id) { + throw new RuntimeException("Stub!"); + } + + public T findViewByAccessibilityIdTraversal(int accessibilityId) { + throw new RuntimeException("Stub!"); + } + + public T findViewByAutofillIdTraversal(int autofillId) { + throw new RuntimeException("Stub!"); + } + + + public void findAutofillableViewsByTraversal(@NonNull List autofillableViews) { + throw new RuntimeException("Stub!"); + } + + public final T findViewWithTag(Object tag) { + throw new RuntimeException("Stub!"); + } + + public final T findViewByPredicate(Predicate predicate) { + throw new RuntimeException("Stub!"); + } + + public final T findViewByPredicateInsideOut( + View start, Predicate predicate) { + throw new RuntimeException("Stub!"); + } + + public void setId(@IdRes int id) { + throw new RuntimeException("Stub!"); + } + + @TestApi + public void setIsRootNamespace(boolean isRoot) { + throw new RuntimeException("Stub!"); + } + + public boolean isRootNamespace() { + throw new RuntimeException("Stub!"); + } + + @IdRes + @ViewDebug.CapturedViewProperty + public int getId() { + throw new RuntimeException("Stub!"); + } + + public long getUniqueDrawingId() { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public Object getTag() { + throw new RuntimeException("Stub!"); + } + + public void setTag(final Object tag) { + throw new RuntimeException("Stub!"); + } + + public Object getTag(int key) { + throw new RuntimeException("Stub!"); + } + + public void setTag(int key, final Object tag) { + throw new RuntimeException("Stub!"); + } + + public void setTagInternal(int key, Object tag) { + throw new RuntimeException("Stub!"); + } + + public void debug() { + throw new RuntimeException("Stub!"); + } + + protected void debug(int depth) { + throw new RuntimeException("Stub!"); + } + + protected static String debugIndent(int depth) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "layout") + public int getBaseline() { + throw new RuntimeException("Stub!"); + } + + public boolean isInLayout() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void requestLayout() { + throw new RuntimeException("Stub!"); + } + + public void forceLayout() { + throw new RuntimeException("Stub!"); + } + + public final void measure(int widthMeasureSpec, int heightMeasureSpec) { + throw new RuntimeException("Stub!"); + } + + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + throw new RuntimeException("Stub!"); + } + + protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { + throw new RuntimeException("Stub!"); + } + + public static int combineMeasuredStates(int curState, int newState) { + throw new RuntimeException("Stub!"); + } + + public static int resolveSize(int size, int measureSpec) { + throw new RuntimeException("Stub!"); + } + + public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { + throw new RuntimeException("Stub!"); + } + + public static int getDefaultSize(int size, int measureSpec) { + throw new RuntimeException("Stub!"); + } + + protected int getSuggestedMinimumHeight() { + throw new RuntimeException("Stub!"); + } + + protected int getSuggestedMinimumWidth() { + throw new RuntimeException("Stub!"); + } + + public int getMinimumHeight() { + throw new RuntimeException("Stub!"); + } + + public void setMinimumHeight(int minHeight) { + throw new RuntimeException("Stub!"); + } + + public int getMinimumWidth() { + throw new RuntimeException("Stub!"); + } + + public void setMinimumWidth(int minWidth) { + throw new RuntimeException("Stub!"); + } + + public Animation getAnimation() { + throw new RuntimeException("Stub!"); + } + + public void startAnimation(Animation animation) { + throw new RuntimeException("Stub!"); + } + + public void clearAnimation() { + throw new RuntimeException("Stub!"); + } + + public void setAnimation(Animation animation) { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void onAnimationStart() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + protected void onAnimationEnd() { + throw new RuntimeException("Stub!"); + } + + protected boolean onSetAlpha(int alpha) { + return false; + } + + public boolean gatherTransparentRegion(@Nullable Region region) { + throw new RuntimeException("Stub!"); + } + + public void playSoundEffect(int soundConstant) { + throw new RuntimeException("Stub!"); + } + + public boolean performHapticFeedback(int feedbackConstant) { + throw new RuntimeException("Stub!"); + } + + public boolean performHapticFeedback(int feedbackConstant, int flags) { + throw new RuntimeException("Stub!"); + } + + public void performHapticFeedbackForInputDevice(int feedbackConstant, int inputDeviceId, + int inputSource, int flags) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setSystemUiVisibility(int visibility) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public int getSystemUiVisibility() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public int getWindowSystemUiVisibility() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void onWindowSystemUiVisibilityChanged(int visible) { + } + + @Deprecated + public void dispatchWindowSystemUiVisiblityChanged(int visible) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener l) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void dispatchSystemUiVisibilityChanged(int visibility) { + throw new RuntimeException("Stub!"); + } + + boolean updateLocalSystemUiVisibility(int localValue, int localChanges) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + public void setDisabledSystemUiVisibility(int flags) { + throw new RuntimeException("Stub!"); + } + + public void onSystemBarAppearanceChanged(int appearance) { + } + + public static class DragShadowBuilder { + public DragShadowBuilder(View view) { + throw new RuntimeException("Stub!"); + } + + public DragShadowBuilder() { + throw new RuntimeException("Stub!"); + } + + @SuppressWarnings({"JavadocReference"}) + final public View getView() { + throw new RuntimeException("Stub!"); + } + + public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) { + throw new RuntimeException("Stub!"); + } + + public void onDrawShadow(@NonNull Canvas canvas) { + throw new RuntimeException("Stub!"); + } + } + + @Deprecated + public final boolean startDrag(ClipData data, DragShadowBuilder shadowBuilder, + Object myLocalState, int flags) { + throw new RuntimeException("Stub!"); + } + + public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder, + Object myLocalState, int flags) { + throw new RuntimeException("Stub!"); + } + + static boolean hasActivityPendingIntents(ClipData data) { + throw new RuntimeException("Stub!"); + } + + static void cleanUpPendingIntents(ClipData data) { + throw new RuntimeException("Stub!"); + } + + void setAccessibilityDragStarted(boolean started) { + throw new RuntimeException("Stub!"); + } + + public final void cancelDragAndDrop() { + throw new RuntimeException("Stub!"); + } + + public final void updateDragShadow(DragShadowBuilder shadowBuilder) { + throw new RuntimeException("Stub!"); + } + + public final boolean startMovingTask(float startX, float startY) { + throw new RuntimeException("Stub!"); + } + + public void finishMovingTask() { + throw new RuntimeException("Stub!"); + } + + public boolean onDragEvent(DragEvent event) { + throw new RuntimeException("Stub!"); + } + + // Dispatches ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED events for pre-Nougat apps. + boolean dispatchDragEnterExitInPreN(DragEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchDragEvent(DragEvent event) { + throw new RuntimeException("Stub!"); + } + + final boolean callDragEventHandler(DragEvent event) { + throw new RuntimeException("Stub!"); + } + + boolean canAcceptDrag() { + throw new RuntimeException("Stub!"); + } + + void sendWindowContentChangedAccessibilityEvent(int changeType) { + throw new RuntimeException("Stub!"); + } + + public void onCloseSystemDialogs(String reason) { + } + + public void applyDrawableToTransparentRegion(Drawable dr, Region region) { + throw new RuntimeException("Stub!"); + } + + public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) { + throw new RuntimeException("Stub!"); + } + + @SuppressWarnings({"UnusedParameters"}) + protected boolean overScrollBy(int deltaX, int deltaY, + int scrollX, int scrollY, + int scrollRangeX, int scrollRangeY, + int maxOverScrollX, int maxOverScrollY, + boolean isTouchEvent) { + throw new RuntimeException("Stub!"); + } + + protected void onOverScrolled(int scrollX, int scrollY, + boolean clampedX, boolean clampedY) { + // Intentionally empty. + } + + public int getOverScrollMode() { + throw new RuntimeException("Stub!"); + } + + public void setOverScrollMode(int overScrollMode) { + throw new RuntimeException("Stub!"); + } + + public void setNestedScrollingEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + public boolean isNestedScrollingEnabled() { + throw new RuntimeException("Stub!"); + } + + public boolean startNestedScroll(int axes) { + throw new RuntimeException("Stub!"); + } + + public void stopNestedScroll() { + throw new RuntimeException("Stub!"); + } + + public boolean hasNestedScrollingParent() { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, + int dxUnconsumed, int dyUnconsumed, @Nullable @Size(2) int[] offsetInWindow) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchNestedPreScroll(int dx, int dy, + @Nullable @Size(2) int[] consumed, @Nullable @Size(2) int[] offsetInWindow) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchNestedPreFling(float velocityX, float velocityY) { + throw new RuntimeException("Stub!"); + } + + protected float getVerticalScrollFactor() { + throw new RuntimeException("Stub!"); + } + + protected float getHorizontalScrollFactor() { + throw new RuntimeException("Stub!"); + } + + public int getRawTextDirection() { + throw new RuntimeException("Stub!"); + } + + public void setTextDirection(int textDirection) { + throw new RuntimeException("Stub!"); + } + + public int getTextDirection() { + throw new RuntimeException("Stub!"); + } + + public boolean resolveTextDirection() { + throw new RuntimeException("Stub!"); + } + + public boolean canResolveTextDirection() { + throw new RuntimeException("Stub!"); + } + + @TestApi + public void resetResolvedTextDirection() { + throw new RuntimeException("Stub!"); + } + + public boolean isTextDirectionInherited() { + throw new RuntimeException("Stub!"); + } + + public boolean isTextDirectionResolved() { + throw new RuntimeException("Stub!"); + } + + @TextAlignment + public int getRawTextAlignment() { + throw new RuntimeException("Stub!"); + } + + public void setTextAlignment(@TextAlignment int textAlignment) { + throw new RuntimeException("Stub!"); + } + + @TextAlignment + public int getTextAlignment() { + throw new RuntimeException("Stub!"); + } + + public boolean resolveTextAlignment() { + throw new RuntimeException("Stub!"); + } + + public boolean canResolveTextAlignment() { + throw new RuntimeException("Stub!"); + } + + @TestApi + public void resetResolvedTextAlignment() { + throw new RuntimeException("Stub!"); + } + + public boolean isTextAlignmentInherited() { + throw new RuntimeException("Stub!"); + } + + public boolean isTextAlignmentResolved() { + throw new RuntimeException("Stub!"); + } + + public static int generateViewId() { + throw new RuntimeException("Stub!"); + } + + public void captureTransitioningViews(List transitioningViews) { + throw new RuntimeException("Stub!"); + } + + public void findNamedViews(Map namedElements) { + throw new RuntimeException("Stub!"); + } + + public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { + throw new RuntimeException("Stub!"); + } + + public void setPointerIcon(PointerIcon pointerIcon) { + throw new RuntimeException("Stub!"); + } + + public PointerIcon getPointerIcon() { + throw new RuntimeException("Stub!"); + } + + public boolean hasPointerCapture() { + throw new RuntimeException("Stub!"); + } + + public void requestPointerCapture() { + throw new RuntimeException("Stub!"); + } + + + public void releasePointerCapture() { + throw new RuntimeException("Stub!"); + } + + @CallSuper + public void onPointerCaptureChange(boolean hasCapture) { + } + + public void dispatchPointerCaptureChanged(boolean hasCapture) { + throw new RuntimeException("Stub!"); + } + + public boolean onCapturedPointerEvent(MotionEvent event) { + return false; + } + + public interface OnCapturedPointerListener { + boolean onCapturedPointer(View view, MotionEvent event); + } + + public void setOnCapturedPointerListener(OnCapturedPointerListener l) { + throw new RuntimeException("Stub!"); + } + + // Properties + // + public static final Property ALPHA = null; + + public static final Property TRANSLATION_X = null; + + public static final Property TRANSLATION_Y = null; + + public static final Property TRANSLATION_Z = null; + + public static final Property X = null; + + public static final Property Y = null; + + public static final Property Z = null; + + public static final Property ROTATION = null; + + public static final Property ROTATION_X = null; + + public static final Property ROTATION_Y = null; + + public static final Property SCALE_X = null; + + public static final Property SCALE_Y = null; + + public static class MeasureSpec { + private static final int MODE_SHIFT = 30; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface MeasureSpecMode {} + + public static final int UNSPECIFIED = 0 << MODE_SHIFT; + + public static final int EXACTLY = 1 << MODE_SHIFT; + + public static final int AT_MOST = 2 << MODE_SHIFT; + + public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size, + @MeasureSpecMode int mode) { + throw new RuntimeException("Stub!"); + } + + public static int makeSafeMeasureSpec(int size, int mode) { + throw new RuntimeException("Stub!"); + } + + @MeasureSpecMode + public static int getMode(int measureSpec) { + throw new RuntimeException("Stub!"); + } + + public static int getSize(int measureSpec) { + throw new RuntimeException("Stub!"); + } + + static int adjust(int measureSpec, int delta) { + throw new RuntimeException("Stub!"); + } + + public static String toString(int measureSpec) { + throw new RuntimeException("Stub!"); + } + } + + public ViewPropertyAnimator animate() { + throw new RuntimeException("Stub!"); + } + + public final void setTransitionName(String transitionName) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty + public String getTransitionName() { + throw new RuntimeException("Stub!"); + } + + public void requestKeyboardShortcuts(List data, int deviceId) { + // Do nothing. + } + + public interface OnKeyListener { + boolean onKey(View v, int keyCode, KeyEvent event); + } + + public interface OnUnhandledKeyEventListener { + boolean onUnhandledKeyEvent(View v, KeyEvent event); + } + + public interface OnTouchListener { + boolean onTouch(View v, MotionEvent event); + } + + public interface OnHoverListener { + boolean onHover(View v, MotionEvent event); + } + + public interface OnGenericMotionListener { + boolean onGenericMotion(View v, MotionEvent event); + } + + public interface OnLongClickListener { + boolean onLongClick(View v); + + default boolean onLongClickUseDefaultHapticFeedback(@NonNull View v) { + return true; + } + } + + public interface OnDragListener { + boolean onDrag(View v, DragEvent event); + } + + public interface OnFocusChangeListener { + void onFocusChange(View v, boolean hasFocus); + } + + public interface OnClickListener { + void onClick(View v); + } + + public interface OnContextClickListener { + boolean onContextClick(View v); + } + + public interface OnCreateContextMenuListener { + void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo); + } + + @Deprecated + public interface OnSystemUiVisibilityChangeListener { + public void onSystemUiVisibilityChange(int visibility); + } + + public interface OnAttachStateChangeListener { + public void onViewAttachedToWindow(@NonNull View v); + public void onViewDetachedFromWindow(@NonNull View v); + } + + public interface OnApplyWindowInsetsListener { + public @NonNull WindowInsets onApplyWindowInsets(@NonNull View v, + @NonNull WindowInsets insets); + } + + public static class BaseSavedState extends AbsSavedState { + static final int START_ACTIVITY_REQUESTED_WHO_SAVED = 0b1; + static final int IS_AUTOFILLED = 0b10; + static final int AUTOFILL_ID = 0b100; + + // Flags that describe what data in this state is valid + int mSavedData; + String mStartActivityRequestWhoSaved; + boolean mIsAutofilled; + boolean mHideHighlight; + int mAutofillViewId; + + public BaseSavedState(Parcel source) { + this(source, null); + } + + public BaseSavedState(Parcel source, ClassLoader loader) { + super(source, loader); + mSavedData = source.readInt(); + mStartActivityRequestWhoSaved = source.readString(); + mIsAutofilled = source.readBoolean(); + mHideHighlight = source.readBoolean(); + mAutofillViewId = source.readInt(); + } + + public BaseSavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + + out.writeInt(mSavedData); + out.writeString(mStartActivityRequestWhoSaved); + out.writeBoolean(mIsAutofilled); + out.writeBoolean(mHideHighlight); + out.writeInt(mAutofillViewId); + } + + public static final @android.annotation.NonNull Parcelable.Creator CREATOR + = new Parcelable.ClassLoaderCreator() { + @Override + public BaseSavedState createFromParcel(Parcel in) { + return new BaseSavedState(in); + } + + @Override + public BaseSavedState createFromParcel(Parcel in, ClassLoader loader) { + return new BaseSavedState(in, loader); + } + + @Override + public BaseSavedState[] newArray(int size) { + return new BaseSavedState[size]; + } + }; + } + + public static class AccessibilityDelegate { + + public void sendAccessibilityEvent(@NonNull View host, int eventType) { + throw new RuntimeException("Stub!"); + } + + public boolean performAccessibilityAction(@NonNull View host, int action, + @Nullable Bundle args) { + throw new RuntimeException("Stub!"); + } + + public void sendAccessibilityEventUnchecked(@NonNull View host, + @NonNull AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean dispatchPopulateAccessibilityEvent(@NonNull View host, + @NonNull AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public void onPopulateAccessibilityEvent(@NonNull View host, + @NonNull AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public void onInitializeAccessibilityEvent(@NonNull View host, + @NonNull AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public void onInitializeAccessibilityNodeInfo(@NonNull View host, + @NonNull AccessibilityNodeInfo info) { + throw new RuntimeException("Stub!"); + } + + public void addExtraDataToAccessibilityNodeInfo(@NonNull View host, + @NonNull AccessibilityNodeInfo info, @NonNull String extraDataKey, + @Nullable Bundle arguments) { + throw new RuntimeException("Stub!"); + } + + public boolean onRequestSendAccessibilityEvent(@NonNull ViewGroup host, @NonNull View child, + @NonNull AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public @Nullable AccessibilityNodeProvider getAccessibilityNodeProvider( + @NonNull View host) { + return null; + } + + public AccessibilityNodeInfo createAccessibilityNodeInfo(@NonNull View host) { + throw new RuntimeException("Stub!"); + } + } + + @ScrollCaptureHint + public int getScrollCaptureHint() { + throw new RuntimeException("Stub!"); + } + + public void setScrollCaptureHint(@ScrollCaptureHint int hint) { + throw new RuntimeException("Stub!"); + } + + // public final void setScrollCaptureCallback(@Nullable ScrollCaptureCallback callback) { + // throw new RuntimeException("Stub!"); + // } + + // /** {@hide} */ + // @Nullable + // public ScrollCaptureCallback createScrollCaptureCallbackInternal(@NonNull Rect localVisibleRect, + // @NonNull Point windowOffset) { + // throw new RuntimeException("Stub!"); + // } + + // public void dispatchScrollCaptureSearch( + // @NonNull Rect localVisibleRect, @NonNull Point windowOffset, + // @NonNull Consumer targets) { + // throw new RuntimeException("Stub!"); + // } + + // public void onScrollCaptureSearch(@NonNull Rect localVisibleRect, + // @NonNull Point windowOffset, @NonNull Consumer targets) { + // throw new RuntimeException("Stub!"); + // } + + // /** {@hide} */ + // public void encode(@NonNull ViewHierarchyEncoder stream) { + // throw new RuntimeException("Stub!"); + // } + + // /** {@hide} */ + // @CallSuper + // protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + // throw new RuntimeException("Stub!"); + // } + + boolean shouldDrawRoundScrollbar() { + throw new RuntimeException("Stub!"); + } + + public void setTooltipText(@Nullable CharSequence tooltipText) { + throw new RuntimeException("Stub!"); + } + + public void setTooltip(@Nullable CharSequence tooltipText) { + throw new RuntimeException("Stub!"); + } + + @Nullable + public CharSequence getTooltipText() { + throw new RuntimeException("Stub!"); + } + + @Nullable + public CharSequence getTooltip() { + throw new RuntimeException("Stub!"); + } + + void hideTooltip() { + throw new RuntimeException("Stub!"); + } + + boolean dispatchTooltipHoverEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + void handleTooltipKey(KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + @TestApi + public View getTooltipView() { + throw new RuntimeException("Stub!"); + } + + @TestApi + public static boolean isDefaultFocusHighlightEnabled() { + throw new RuntimeException("Stub!"); + } + + View dispatchUnhandledKeyEvent(KeyEvent evt) { + throw new RuntimeException("Stub!"); + } + + boolean onUnhandledKeyEvent(@NonNull KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + boolean hasUnhandledKeyListener() { + throw new RuntimeException("Stub!"); + } + + public void addOnUnhandledKeyEventListener(OnUnhandledKeyEventListener listener) { + throw new RuntimeException("Stub!"); + } + + public void removeOnUnhandledKeyEventListener(OnUnhandledKeyEventListener listener) { + throw new RuntimeException("Stub!"); + } + + protected void setDetached(boolean detached) { + throw new RuntimeException("Stub!"); + } + + public void setIsCredential(boolean isCredential) { + throw new RuntimeException("Stub!"); + } + + public boolean isCredential() { + throw new RuntimeException("Stub!"); + } + + // TODO(316208691): Revive following removed API docs. + // @see EditorInfo#setStylusHandwritingEnabled(boolean) + public void setAutoHandwritingEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + public boolean isAutoHandwritingEnabled() { + throw new RuntimeException("Stub!"); + } + + public boolean isStylusHandwritingAvailable() { + throw new RuntimeException("Stub!"); + } + + // public void onCreateViewTranslationRequest(@NonNull int[] supportedFormats, + // @NonNull Consumer requestsCollector) { + // } + + // @SuppressLint("NullableCollection") + // public void onCreateVirtualViewTranslationRequests(@NonNull long[] virtualIds, + // @NonNull int[] supportedFormats, + // @NonNull Consumer requestsCollector) { + // // no-op + // } + + // @Nullable + // public ViewTranslationCallback getViewTranslationCallback() { + // throw new RuntimeException("Stub!"); + // } + + // public void setViewTranslationCallback(@NonNull ViewTranslationCallback callback) { + // throw new RuntimeException("Stub!"); + // } + + // public void clearViewTranslationCallback() { + // throw new RuntimeException("Stub!"); + // } + + // @Nullable + // public ViewTranslationResponse getViewTranslationResponse() { + // throw new RuntimeException("Stub!"); + // } + + // public void onViewTranslationResponse(@NonNull ViewTranslationResponse response) { + // throw new RuntimeException("Stub!"); + // } + + // public void clearViewTranslationResponse() { + // throw new RuntimeException("Stub!"); + // } + + // public void onVirtualViewTranslationResponses( + // @NonNull LongSparseArray response) { + // // no-op + // } + + // public void dispatchCreateViewTranslationRequest(@NonNull Map viewIds, + // @NonNull int[] supportedFormats, + // @NonNull TranslationCapability capability, + // @NonNull List requests) { + // throw new RuntimeException("Stub!"); + // } + + // public void generateDisplayHash(@NonNull String hashAlgorithm, + // @Nullable Rect bounds, @NonNull Executor executor, + // @NonNull DisplayHashResultCallback callback) { + // throw new RuntimeException("Stub!"); + // } + + /** + * The AttachedSurfaceControl itself is not a View, it is just the interface to the + * windowing-system object that contains the entire view hierarchy. + * For the root View of a given hierarchy see {@link #getRootView}. + + * @return The {@link android.view.AttachedSurfaceControl} interface for this View. + * This will only return a non-null value when called between {@link #onAttachedToWindow} + * and {@link #onDetachedFromWindow}. + */ + // public @Nullable AttachedSurfaceControl getRootSurfaceControl() { + // throw new RuntimeException("Stub!"); + // } + + protected int calculateFrameRateCategory() { + throw new RuntimeException("Stub!"); + } + + protected void votePreferredFrameRate() { + throw new RuntimeException("Stub!"); + } + + public void setFrameContentVelocity(float pixelsPerSecond) { + throw new RuntimeException("Stub!"); + } + + public float getFrameContentVelocity() { + throw new RuntimeException("Stub!"); + } + + public void setRequestedFrameRate(float frameRate) { + throw new RuntimeException("Stub!"); + } + + public float getRequestedFrameRate() { + throw new RuntimeException("Stub!"); + } + + void overrideFrameRate(float frameRate, boolean forceOverride) { + throw new RuntimeException("Stub!"); + } + + void setForcedOverrideFrameRateFlag(boolean forcedOverride) { + throw new RuntimeException("Stub!"); + } + + boolean getForcedOverrideFrameRateFlag() { + throw new RuntimeException("Stub!"); + } + + void setSelfRequestedFrameRateFlag(boolean forcedOverride) { + throw new RuntimeException("Stub!"); + } + + boolean getSelfRequestedFrameRateFlag() { + throw new RuntimeException("Stub!"); + } + + // public void reportAppJankStats(@NonNull AppJankStats appJankStats) { + // throw new RuntimeException("Stub!"); + // } + + // public @Nullable JankTracker getJankTracker() { + // throw new RuntimeException("Stub!"); + // } +} diff --git a/AndroidCompat/src/main/java/android/view/ViewGroup.java b/AndroidCompat/src/main/java/android/view/ViewGroup.java new file mode 100644 index 00000000..396aaa87 --- /dev/null +++ b/AndroidCompat/src/main/java/android/view/ViewGroup.java @@ -0,0 +1,1787 @@ +/* + * 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 android.animation.LayoutTransition; +import android.annotation.CallSuper; +import android.annotation.IdRes; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.TestApi; +import android.annotation.UiThread; +import android.content.ClipData; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.os.Build; +import android.os.Bundle; +import android.os.Parcelable; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseArray; +import android.view.WindowInsetsAnimation.Bounds; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.LayoutAnimationController; +import android.view.animation.Transformation; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillManager; +import android.webkit.WebView; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Predicate; + +@UiThread +public abstract class ViewGroup extends View implements ViewParent, ViewManager { + protected ArrayList mDisappearingChildren; + + protected OnHierarchyChangeListener mOnHierarchyChangeListener; + + // The last child of this ViewGroup which held focus within the current cluster + View mFocusedInCluster; + + RectF mInvalidateRegion; + + Transformation mInvalidationTransformation; + + protected int mGroupFlags; + + + // When set, ViewGroup invalidates only the child's rectangle + // Set by default + static final int FLAG_CLIP_CHILDREN = 0x1; + + // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when + // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set + static final int FLAG_INVALIDATE_REQUIRED = 0x4; + + // When set, there is either no layout animation on the ViewGroup or the layout + // animation is over + // Set by default + static final int FLAG_ANIMATION_DONE = 0x10; + + // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a + // layout animation; this avoid clobbering the hierarchy + // Automatically set when the layout animation starts, depending on the animation's + // characteristics + static final int FLAG_OPTIMIZE_INVALIDATE = 0x80; + + // When set, the next call to drawChild() will clear mChildTransformation's matrix + static final int FLAG_CLEAR_TRANSFORMATION = 0x100; + + protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400; + + protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800; + + // UNUSED FLAG VALUE: 0x1000; + + public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000; + + public static final int FOCUS_AFTER_DESCENDANTS = 0x40000; + + public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000; + + protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000; + + static final int FLAG_IS_TRANSITION_GROUP = 0x1000000; + + static final int FLAG_IS_TRANSITION_GROUP_SET = 0x2000000; + + static final int FLAG_TOUCHSCREEN_BLOCKS_FOCUS = 0x4000000; + + protected int mPersistentDrawingCache; + + @Deprecated + public static final int PERSISTENT_NO_CACHE = 0x0; + + @Deprecated + public static final int PERSISTENT_ANIMATION_CACHE = 0x1; + + @Deprecated + public static final int PERSISTENT_SCROLLING_CACHE = 0x2; + + @Deprecated + public static final int PERSISTENT_ALL_CACHES = 0x3; + + // Layout Modes + + public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; + + public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; + + /** @hide */ + public static int LAYOUT_MODE_DEFAULT = LAYOUT_MODE_CLIP_BOUNDS; + + protected static final int CLIP_TO_PADDING_MASK = 0; + + // Whether layout calls are currently being suppressed, controlled by calls to + // suppressLayout() + boolean mSuppressLayout = false; + + // Used to draw cached views + Paint mCachePaint; + + int mChildUnhandledKeyListeners = 0; + + public ViewGroup(Context context) { + this(context, null); + } + + public ViewGroup(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public int getDescendantFocusability() { + throw new RuntimeException("Stub!"); + } + + public void setDescendantFocusability(int focusability) { + throw new RuntimeException("Stub!"); + } + + @Override + void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) { + throw new RuntimeException("Stub!"); + } + + @Override + public void requestChildFocus(View child, View focused) { + throw new RuntimeException("Stub!"); + } + + void setDefaultFocus(View child) { + throw new RuntimeException("Stub!"); + } + + void clearDefaultFocus(View child) { + throw new RuntimeException("Stub!"); + } + + @Override + boolean hasDefaultFocus() { + throw new RuntimeException("Stub!"); + } + + void clearFocusedInCluster(View child) { + throw new RuntimeException("Stub!"); + } + + void clearFocusedInCluster() { + throw new RuntimeException("Stub!"); + } + + @Override + public void focusableViewAvailable(View v) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean showContextMenuForChild(View originalView) { + throw new RuntimeException("Stub!"); + } + + public final boolean isShowingContextMenuWithCoords() { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean showContextMenuForChild(View originalView, float x, float y) { + throw new RuntimeException("Stub!"); + } + + @Override + public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { + throw new RuntimeException("Stub!"); + } + + @Override + public ActionMode startActionModeForChild( + View originalView, ActionMode.Callback callback, int type) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean dispatchActivityResult( + String who, int requestCode, int resultCode, Intent data) { + throw new RuntimeException("Stub!"); + } + + @Override + public View focusSearch(View focused, int direction) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { + return false; + } + + @Override + public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) { + return true; + } + + @Override + public void childHasTransientStateChanged(View child, boolean childHasTransientState) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean hasTransientState() { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean dispatchUnhandledMove(View focused, int direction) { + throw new RuntimeException("Stub!"); + } + + @Override + public void clearChildFocus(View child) { + throw new RuntimeException("Stub!"); + } + + @Override + public void clearFocus() { + throw new RuntimeException("Stub!"); + } + + @Override + void unFocus(View focused) { + throw new RuntimeException("Stub!"); + } + + public View getFocusedChild() { + throw new RuntimeException("Stub!"); + } + + View getDeepestFocusedChild() { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean hasFocus() { + throw new RuntimeException("Stub!"); + } + + /* + * (non-Javadoc) + * + * @see android.view.View#findFocus() + */ + @Override + public View findFocus() { + throw new RuntimeException("Stub!"); + } + + @Override + boolean hasFocusable(boolean allowAutoFocus, boolean dispatchExplicit) { + throw new RuntimeException("Stub!"); + } + + boolean hasFocusableChild(boolean dispatchExplicit) { + throw new RuntimeException("Stub!"); + } + + @Override + public void addFocusables(ArrayList views, int direction, int focusableMode) { + throw new RuntimeException("Stub!"); + } + + @Override + public void addKeyboardNavigationClusters(Collection views, int direction) { + throw new RuntimeException("Stub!"); + } + + public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "focus") + public boolean getTouchscreenBlocksFocus() { + throw new RuntimeException("Stub!"); + } + + boolean shouldBlockFocusForTouchscreen() { + throw new RuntimeException("Stub!"); + } + + @Override + public void findViewsWithText(ArrayList outViews, CharSequence text, int flags) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public View findViewByAccessibilityIdTraversal(int accessibilityId) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public View findViewByAutofillIdTraversal(int autofillId) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public void findAutofillableViewsByTraversal(@NonNull List autofillableViews) { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchWindowFocusChanged(boolean hasFocus) { + throw new RuntimeException("Stub!"); + } + + @Override + public void addTouchables(ArrayList views) { + throw new RuntimeException("Stub!"); + } + + @Override + public void makeOptionalFitsSystemWindows() { + throw new RuntimeException("Stub!"); + } + + @Override + public void makeFrameworkOptionalFitsSystemWindows() { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchDisplayHint(int hint) { + throw new RuntimeException("Stub!"); + } + + protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) { + throw new RuntimeException("Stub!"); + } + + @Override + protected void dispatchVisibilityChanged(View changedView, int visibility) { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchWindowVisibilityChanged(int visibility) { + throw new RuntimeException("Stub!"); + } + + @Override + boolean dispatchVisibilityAggregated(boolean isVisible) { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchConfigurationChanged(Configuration newConfig) { + throw new RuntimeException("Stub!"); + } + + @Override + public void recomputeViewAttributes(View child) { + throw new RuntimeException("Stub!"); + } + + // @Override + // void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) { + // throw new RuntimeException("Stub!"); + // } + + @Override + public void bringChildToFront(View child) { + throw new RuntimeException("Stub!"); + } + + @Override + boolean dispatchDragEnterExitInPreN(DragEvent event) { + throw new RuntimeException("Stub!"); + } + + // TODO: Write real docs + @Override + public boolean dispatchDragEvent(DragEvent event) { + throw new RuntimeException("Stub!"); + } + + // Find the frontmost child view that lies under the given point, and calculate + // the position within its own local coordinate system. + View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) { + throw new RuntimeException("Stub!"); + } + + boolean notifyChildOfDragStart(View child) { + throw new RuntimeException("Stub!"); + } + + @Override + @Deprecated + public void dispatchWindowSystemUiVisiblityChanged(int visible) { + throw new RuntimeException("Stub!"); + } + + @Override + @Deprecated + public void dispatchSystemUiVisibilityChanged(int visible) { + throw new RuntimeException("Stub!"); + } + + @Override + boolean updateLocalSystemUiVisibility(int localValue, int localChanges) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean dispatchKeyEventPreIme(KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean dispatchTrackballEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean dispatchCapturedPointerEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchPointerCaptureChanged(boolean hasCapture) { + throw new RuntimeException("Stub!"); + } + + @Override + public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { + throw new RuntimeException("Stub!"); + } + + @SuppressWarnings({"ConstantConditions"}) + @Override + protected boolean dispatchHoverEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + boolean dispatchTooltipHoverEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + protected boolean hasHoveredChild() { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + protected boolean pointInHoveredChild(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + public void addChildrenForAccessibility(ArrayList outChildren) { + throw new RuntimeException("Stub!"); + } + + public boolean onInterceptHoverEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + protected boolean dispatchGenericPointerEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + protected boolean dispatchGenericFocusedEvent(MotionEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + throw new RuntimeException("Stub!"); + } + + public ArrayList buildTouchDispatchChildList() { + throw new RuntimeException("Stub!"); + } + + protected boolean isTransformedTouchPointInView(float x, float y, View child, + PointF outLocalPoint) { + throw new RuntimeException("Stub!"); + } + + public void transformPointToViewLocal(float[] point, View child) { + throw new RuntimeException("Stub!"); + } + + public void setMotionEventSplittingEnabled(boolean split) { + throw new RuntimeException("Stub!"); + } + + public boolean isMotionEventSplittingEnabled() { + throw new RuntimeException("Stub!"); + } + + public boolean isTransitionGroup() { + throw new RuntimeException("Stub!"); + } + + public void setTransitionGroup(boolean isTransitionGroup) { + throw new RuntimeException("Stub!"); + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + throw new RuntimeException("Stub!"); + } + + public boolean onInterceptTouchEvent(MotionEvent ev) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean requestFocus(int direction, Rect previouslyFocusedRect) { + throw new RuntimeException("Stub!"); + } + + @SuppressWarnings({"ConstantConditions"}) + protected boolean onRequestFocusInDescendants(int direction, + Rect previouslyFocusedRect) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean restoreDefaultFocus() { + throw new RuntimeException("Stub!"); + } + + @TestApi + @Override + public boolean restoreFocusInCluster(@FocusRealDirection int direction) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean restoreFocusNotInCluster() { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchStartTemporaryDetach() { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchFinishTemporaryDetach() { + throw new RuntimeException("Stub!"); + } + + // @Override + // void dispatchAttachedToWindow(AttachInfo info, int visibility) { + // throw new RuntimeException("Stub!"); + // } + + @Override + void dispatchScreenStateChanged(int screenState) { + throw new RuntimeException("Stub!"); + } + + @Override + void dispatchMovedToDisplay(Display display, Configuration config) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchProvideStructure(ViewStructure structure) { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchProvideAutofillStructure(ViewStructure structure, + @AutofillFlags int flags) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public void dispatchProvideContentCaptureStructure() { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public void resetSubtreeAutofillIds() { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + throw new RuntimeException("Stub!"); + } + + @Override + public void addExtraDataToAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info, + @NonNull String extraDataKey, @Nullable Bundle arguments) { + throw new RuntimeException("Stub!"); + } + + @Override + public CharSequence getAccessibilityClassName() { + throw new RuntimeException("Stub!"); + } + + @Override + public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public void notifySubtreeAccessibilityStateChangedIfNeeded() { + throw new RuntimeException("Stub!"); + } + + @Override + void resetSubtreeAccessibilityStateChanged() { + throw new RuntimeException("Stub!"); + } + + int getNumChildrenForAccessibility() { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) { + return false; + } + + @Override + void calculateAccessibilityDataSensitive() { + throw new RuntimeException("Stub!"); + } + + @Override + void dispatchDetachedFromWindow() { + throw new RuntimeException("Stub!"); + } + + @Override + protected void internalSetPadding(int left, int top, int right, int bottom) { + throw new RuntimeException("Stub!"); + } + + @Override + protected void dispatchSaveInstanceState(SparseArray container) { + throw new RuntimeException("Stub!"); + } + + protected void dispatchFreezeSelfOnly(SparseArray container) { + throw new RuntimeException("Stub!"); + } + + @Override + protected void dispatchRestoreInstanceState(SparseArray container) { + throw new RuntimeException("Stub!"); + } + + protected void dispatchThawSelfOnly(SparseArray container) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + protected void setChildrenDrawingCacheEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + // @Override + // public Bitmap createSnapshot(ViewDebug.CanvasProvider canvasProvider, boolean skipChildren) { + // throw new RuntimeException("Stub!"); + // } + + /** Return true if this ViewGroup is laying out using optical bounds. */ + boolean isLayoutModeOptical() { + throw new RuntimeException("Stub!"); + } + + @Override + Insets computeOpticalInsets() { + throw new RuntimeException("Stub!"); + } + + protected void onDebugDrawMargins(@NonNull Canvas canvas, Paint paint) { + throw new RuntimeException("Stub!"); + } + + protected void onDebugDraw(@NonNull Canvas canvas) { + throw new RuntimeException("Stub!"); + } + + @Override + protected void dispatchDraw(@NonNull Canvas canvas) { + throw new RuntimeException("Stub!"); + } + + @Override + public ViewGroupOverlay getOverlay() { + throw new RuntimeException("Stub!"); + } + + protected int getChildDrawingOrder(int childCount, int drawingPosition) { + throw new RuntimeException("Stub!"); + } + + public final int getChildDrawingOrder(int drawingPosition) { + throw new RuntimeException("Stub!"); + } + + ArrayList buildOrderedChildList() { + throw new RuntimeException("Stub!"); + } + + @Override + protected void dispatchGetDisplayList() { + throw new RuntimeException("Stub!"); + } + + protected boolean drawChild(@NonNull Canvas canvas, View child, long drawingTime) { + throw new RuntimeException("Stub!"); + } + + @Override + void getScrollIndicatorBounds(@NonNull Rect out) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public boolean getClipChildren() { + throw new RuntimeException("Stub!"); + } + + public void setClipChildren(boolean clipChildren) { + throw new RuntimeException("Stub!"); + } + + public void setClipToPadding(boolean clipToPadding) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + public boolean getClipToPadding() { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchSetSelected(boolean selected) { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchSetActivated(boolean activated) { + throw new RuntimeException("Stub!"); + } + + @Override + protected void dispatchSetPressed(boolean pressed) { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchDrawableHotspotChanged(float x, float y) { + throw new RuntimeException("Stub!"); + } + + @Override + void dispatchCancelPendingInputEvents() { + throw new RuntimeException("Stub!"); + } + + protected void setStaticTransformationsEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + protected boolean getChildStaticTransformation(View child, Transformation t) { + return false; + } + + Transformation getChildTransformation() { + throw new RuntimeException("Stub!"); + } + + @Override + protected T findViewTraversal(@IdRes int id) { + throw new RuntimeException("Stub!"); + } + + @Override + protected T findViewWithTagTraversal(Object tag) { + throw new RuntimeException("Stub!"); + } + + @Override + protected T findViewByPredicateTraversal(Predicate predicate, + View childToSkip) { + throw new RuntimeException("Stub!"); + } + + public void addTransientView(View view, int index) { + throw new RuntimeException("Stub!"); + } + + public void removeTransientView(View view) { + throw new RuntimeException("Stub!"); + } + + public int getTransientViewCount() { + throw new RuntimeException("Stub!"); + } + + public int getTransientViewIndex(int position) { + throw new RuntimeException("Stub!"); + } + + public View getTransientView(int position) { + throw new RuntimeException("Stub!"); + } + + public void addView(View child) { + throw new RuntimeException("Stub!"); + } + + public void addView(View child, int index) { + throw new RuntimeException("Stub!"); + } + + public void addView(View child, int width, int height) { + throw new RuntimeException("Stub!"); + } + + @Override + public void addView(View child, LayoutParams params) { + throw new RuntimeException("Stub!"); + } + + public void addView(View child, int index, LayoutParams params) { + throw new RuntimeException("Stub!"); + } + + @Override + public void updateViewLayout(View view, ViewGroup.LayoutParams params) { + throw new RuntimeException("Stub!"); + } + + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + throw new RuntimeException("Stub!"); + } + + public interface OnHierarchyChangeListener { + void onChildViewAdded(View parent, View child); + + void onChildViewRemoved(View parent, View child); + } + + public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { + throw new RuntimeException("Stub!"); + } + + void dispatchViewAdded(View child) { + throw new RuntimeException("Stub!"); + } + + public void onViewAdded(View child) { + } + + void dispatchViewRemoved(View child) { + throw new RuntimeException("Stub!"); + } + + public void onViewRemoved(View child) { + } + + @Override + protected void onAttachedToWindow() { + throw new RuntimeException("Stub!"); + } + + @Override + protected void onDetachedFromWindow() { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + protected void destroyHardwareResources() { + throw new RuntimeException("Stub!"); + } + + protected boolean addViewInLayout(View child, int index, LayoutParams params) { + throw new RuntimeException("Stub!"); + } + + protected boolean addViewInLayout(View child, int index, LayoutParams params, + boolean preventRequestLayout) { + throw new RuntimeException("Stub!"); + } + + protected void cleanupLayoutState(View child) { + throw new RuntimeException("Stub!"); + } + + protected void attachLayoutAnimationParameters(View child, + LayoutParams params, int index, int count) { + throw new RuntimeException("Stub!"); + } + + @Override + public void removeView(View view) { + throw new RuntimeException("Stub!"); + } + + public void removeViewInLayout(View view) { + throw new RuntimeException("Stub!"); + } + + public void removeViewsInLayout(int start, int count) { + throw new RuntimeException("Stub!"); + } + + public void removeViewAt(int index) { + throw new RuntimeException("Stub!"); + } + + public void removeViews(int start, int count) { + throw new RuntimeException("Stub!"); + } + + public void setLayoutTransition(LayoutTransition transition) { + throw new RuntimeException("Stub!"); + } + + public LayoutTransition getLayoutTransition() { + throw new RuntimeException("Stub!"); + } + + public void removeAllViews() { + throw new RuntimeException("Stub!"); + } + + public void removeAllViewsInLayout() { + throw new RuntimeException("Stub!"); + } + + protected void removeDetachedView(View child, boolean animate) { + throw new RuntimeException("Stub!"); + } + + protected void attachViewToParent(View child, int index, LayoutParams params) { + throw new RuntimeException("Stub!"); + } + + protected void detachViewFromParent(View child) { + throw new RuntimeException("Stub!"); + } + + protected void detachViewFromParent(int index) { + throw new RuntimeException("Stub!"); + } + + protected void detachViewsFromParent(int start, int count) { + throw new RuntimeException("Stub!"); + } + + protected void detachAllViewsFromParent() { + throw new RuntimeException("Stub!"); + } + + @Override + @CallSuper + public void onDescendantInvalidated(@NonNull View child, @NonNull View target) { + throw new RuntimeException("Stub!"); + } + + + @Deprecated + @Override + public final void invalidateChild(View child, final Rect dirty) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + @Override + public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { + throw new RuntimeException("Stub!"); + } + + public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) { + throw new RuntimeException("Stub!"); + } + + public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) { + throw new RuntimeException("Stub!"); + } + + void offsetRectBetweenParentAndChild(View descendant, Rect rect, + boolean offsetFromChildToParent, boolean clipToBounds) { + throw new RuntimeException("Stub!"); + } + + public void offsetChildrenTopAndBottom(int offset) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { + throw new RuntimeException("Stub!"); + } + + public boolean getChildVisibleRect( + View child, Rect r, android.graphics.Point offset, boolean forceParentCheck) { + throw new RuntimeException("Stub!"); + } + + @Override + public final void layout(int l, int t, int r, int b) { + throw new RuntimeException("Stub!"); + } + + @Override + protected abstract void onLayout(boolean changed, + int l, int t, int r, int b); + + protected boolean canAnimate() { + throw new RuntimeException("Stub!"); + } + + public void startLayoutAnimation() { + throw new RuntimeException("Stub!"); + } + + public void scheduleLayoutAnimation() { + throw new RuntimeException("Stub!"); + } + + public void setLayoutAnimation(LayoutAnimationController controller) { + throw new RuntimeException("Stub!"); + } + + public LayoutAnimationController getLayoutAnimation() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public boolean isAnimationCacheEnabled() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setAnimationCacheEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public boolean isAlwaysDrawnWithCacheEnabled() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setAlwaysDrawnWithCacheEnabled(boolean always) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + protected boolean isChildrenDrawnWithCacheEnabled() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + protected void setChildrenDrawnWithCacheEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + @ViewDebug.ExportedProperty(category = "drawing") + protected boolean isChildrenDrawingOrderEnabled() { + throw new RuntimeException("Stub!"); + } + + protected void setChildrenDrawingOrderEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + @ViewDebug.ExportedProperty(category = "drawing", mapping = { + @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"), + @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"), + @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"), + @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL") + }) + public int getPersistentDrawingCache() { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public void setPersistentDrawingCache(int drawingCacheToKeep) { + throw new RuntimeException("Stub!"); + } + + // @Override + void invalidateInheritedLayoutMode(int layoutModeOfRoot) { + throw new RuntimeException("Stub!"); + } + + public int getLayoutMode() { + throw new RuntimeException("Stub!"); + } + + public void setLayoutMode(int layoutMode) { + throw new RuntimeException("Stub!"); + } + + public LayoutParams generateLayoutParams(AttributeSet attrs) { + throw new RuntimeException("Stub!"); + } + + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + throw new RuntimeException("Stub!"); + } + + protected LayoutParams generateDefaultLayoutParams() { + throw new RuntimeException("Stub!"); + } + + @Override + protected void debug(int depth) { + throw new RuntimeException("Stub!"); + } + + public int indexOfChild(View child) { + throw new RuntimeException("Stub!"); + } + + public int getChildCount() { + throw new RuntimeException("Stub!"); + } + + public View getChildAt(int index) { + throw new RuntimeException("Stub!"); + } + + protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { + throw new RuntimeException("Stub!"); + } + + protected void measureChild(View child, int parentWidthMeasureSpec, + int parentHeightMeasureSpec) { + throw new RuntimeException("Stub!"); + } + + protected void measureChildWithMargins(View child, + int parentWidthMeasureSpec, int widthUsed, + int parentHeightMeasureSpec, int heightUsed) { + throw new RuntimeException("Stub!"); + } + + public static int getChildMeasureSpec(int spec, int padding, int childDimension) { + throw new RuntimeException("Stub!"); + } + + + public void clearDisappearingChildren() { + throw new RuntimeException("Stub!"); + } + + void finishAnimatingView(final View view, Animation animation) { + throw new RuntimeException("Stub!"); + } + + boolean isViewTransitioning(View view) { + throw new RuntimeException("Stub!"); + } + + public void startViewTransition(View view) { + throw new RuntimeException("Stub!"); + } + + public void endViewTransition(View view) { + throw new RuntimeException("Stub!"); + } + + public void suppressLayout(boolean suppress) { + throw new RuntimeException("Stub!"); + } + + public boolean isLayoutSuppressed() { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean gatherTransparentRegion(Region region) { + throw new RuntimeException("Stub!"); + } + + @Override + public void requestTransparentRegion(View child) { + throw new RuntimeException("Stub!"); + } + + // @Override + public void subtractObscuredTouchableRegion(Region touchableRegion, View view) { + throw new RuntimeException("Stub!"); + } + + // @Override + public boolean getChildLocalHitRegion(@NonNull View child, @NonNull Region region, + @NonNull Matrix matrix, boolean isHover) { + throw new RuntimeException("Stub!"); + } + + @Override + public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { + throw new RuntimeException("Stub!"); + } + + @Override + public void setWindowInsetsAnimationCallback( + @Nullable WindowInsetsAnimation.Callback callback) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean hasWindowInsetsAnimationCallback() { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchWindowInsetsAnimationPrepare( + @NonNull WindowInsetsAnimation animation) { + throw new RuntimeException("Stub!"); + } + + @Override + @NonNull + public Bounds dispatchWindowInsetsAnimationStart( + @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds) { + throw new RuntimeException("Stub!"); + } + + @Override + @NonNull + public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets, + @NonNull List runningAnimations) { + throw new RuntimeException("Stub!"); + } + + @Override + public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) { + throw new RuntimeException("Stub!"); + } + + // @Override + // public void dispatchScrollCaptureSearch( + // @NonNull Rect localVisibleRect, @NonNull Point windowOffset, + // @NonNull Consumer targets) { + // throw new RuntimeException("Stub!"); + // } + + public Animation.AnimationListener getLayoutAnimationListener() { + throw new RuntimeException("Stub!"); + } + + @Override + protected void drawableStateChanged() { + throw new RuntimeException("Stub!"); + } + + @Override + public void jumpDrawablesToCurrentState() { + throw new RuntimeException("Stub!"); + } + + @Override + protected int[] onCreateDrawableState(int extraSpace) { + throw new RuntimeException("Stub!"); + } + + public void setAddStatesFromChildren(boolean addsStates) { + throw new RuntimeException("Stub!"); + } + + public boolean addStatesFromChildren() { + throw new RuntimeException("Stub!"); + } + + @Override + public void childDrawableStateChanged(View child) { + throw new RuntimeException("Stub!"); + } + + public void setLayoutAnimationListener(Animation.AnimationListener animationListener) { + throw new RuntimeException("Stub!"); + } + + public void requestTransitionStart(LayoutTransition transition) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean resolveRtlPropertiesIfNeeded() { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean resolveLayoutDirection() { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean resolveTextDirection() { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean resolveTextAlignment() { + throw new RuntimeException("Stub!"); + } + + @Override + public void resolvePadding() { + throw new RuntimeException("Stub!"); + } + + @Override + protected void resolveDrawables() { + throw new RuntimeException("Stub!"); + } + + @Override + public void resolveLayoutParams() { + throw new RuntimeException("Stub!"); + } + + @TestApi + @Override + public void resetResolvedLayoutDirection() { + throw new RuntimeException("Stub!"); + } + + @TestApi + @Override + public void resetResolvedTextDirection() { + throw new RuntimeException("Stub!"); + } + + @TestApi + @Override + public void resetResolvedTextAlignment() { + throw new RuntimeException("Stub!"); + } + + @TestApi + @Override + public void resetResolvedPadding() { + throw new RuntimeException("Stub!"); + } + + @TestApi + @Override + protected void resetResolvedDrawables() { + throw new RuntimeException("Stub!"); + } + + public boolean shouldDelayChildPressedState() { + return true; + } + + @Override + public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { + return false; + } + + @Override + public void onNestedScrollAccepted(View child, View target, int axes) { + throw new RuntimeException("Stub!"); + } + + @Override + public void onStopNestedScroll(View child) { + throw new RuntimeException("Stub!"); + } + + @Override + public void onNestedScroll(View target, int dxConsumed, int dyConsumed, + int dxUnconsumed, int dyUnconsumed) { + throw new RuntimeException("Stub!"); + } + + @Override + public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean onNestedPreFling(View target, float velocityX, float velocityY) { + throw new RuntimeException("Stub!"); + } + + public int getNestedScrollAxes() { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + protected void onSetLayoutParams(View child, LayoutParams layoutParams) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public void captureTransitioningViews(List transitioningViews) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + @Override + public void findNamedViews(Map namedElements) { + throw new RuntimeException("Stub!"); + } + + @Override + boolean hasUnhandledKeyListener() { + throw new RuntimeException("Stub!"); + } + + void incrementChildUnhandledKeyListeners() { + throw new RuntimeException("Stub!"); + } + + void decrementChildUnhandledKeyListeners() { + throw new RuntimeException("Stub!"); + } + + @Override + View dispatchUnhandledKeyEvent(KeyEvent evt) { + throw new RuntimeException("Stub!"); + } + + public static class LayoutParams { + @SuppressWarnings({"UnusedDeclaration"}) + @Deprecated + public static final int FILL_PARENT = -1; + + public static final int MATCH_PARENT = -1; + + public static final int WRAP_CONTENT = -2; + + public int width; + + public int height; + + public LayoutAnimationController.AnimationParameters layoutAnimationParameters; + + public LayoutParams(Context c, AttributeSet attrs) { + throw new RuntimeException("Stub!"); + } + + public LayoutParams(int width, int height) { + this.width = width; + this.height = height; + } + + public LayoutParams(LayoutParams source) { + this.width = source.width; + this.height = source.height; + } + + LayoutParams() { + } + + protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { + throw new RuntimeException("Stub!"); + } + + public void resolveLayoutDirection(int layoutDirection) { + } + + public String debug(String output) { + return output + "ViewGroup.LayoutParams={ width=" + + sizeToString(width) + ", height=" + sizeToString(height) + " }"; + } + + public void onDebugDraw(View view, Canvas canvas, Paint paint) { + } + + protected static String sizeToString(int size) { + if (size == WRAP_CONTENT) { + return "wrap-content"; + } + if (size == MATCH_PARENT) { + return "match-parent"; + } + return String.valueOf(size); + } + + // /** @hide */ + // void encode(@NonNull ViewHierarchyEncoder encoder) { + // throw new RuntimeException("Stub!"); + // } + + // /** @hide */ + // protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + // throw new RuntimeException("Stub!"); + // } + } + + public static class MarginLayoutParams extends ViewGroup.LayoutParams { + @ViewDebug.ExportedProperty(category = "layout") + public int leftMargin; + + @ViewDebug.ExportedProperty(category = "layout") + public int topMargin; + + @ViewDebug.ExportedProperty(category = "layout") + public int rightMargin; + + @ViewDebug.ExportedProperty(category = "layout") + public int bottomMargin; + + byte mMarginFlags; + + public MarginLayoutParams(Context c, AttributeSet attrs) { + throw new RuntimeException("Stub!"); + } + + public MarginLayoutParams(int width, int height) { + super(width, height); + throw new RuntimeException("Stub!"); + } + + public MarginLayoutParams(MarginLayoutParams source) { + throw new RuntimeException("Stub!"); + } + + public MarginLayoutParams(LayoutParams source) { + super(source); + throw new RuntimeException("Stub!"); + } + + public final void copyMarginsFrom(MarginLayoutParams source) { + throw new RuntimeException("Stub!"); + } + + public void setMargins(int left, int top, int right, int bottom) { + throw new RuntimeException("Stub!"); + } + + public void setMarginsRelative(int start, int top, int end, int bottom) { + throw new RuntimeException("Stub!"); + } + + public void setMarginStart(int start) { + throw new RuntimeException("Stub!"); + } + + public int getMarginStart() { + throw new RuntimeException("Stub!"); + } + + public void setMarginEnd(int end) { + throw new RuntimeException("Stub!"); + } + + public int getMarginEnd() { + throw new RuntimeException("Stub!"); + } + + public boolean isMarginRelative() { + throw new RuntimeException("Stub!"); + } + + public void setLayoutDirection(int layoutDirection) { + throw new RuntimeException("Stub!"); + } + + public int getLayoutDirection() { + throw new RuntimeException("Stub!"); + } + + @Override + public void resolveLayoutDirection(int layoutDirection) { + throw new RuntimeException("Stub!"); + } + + public boolean isLayoutRtl() { + throw new RuntimeException("Stub!"); + } + + @Override + public void onDebugDraw(View view, Canvas canvas, Paint paint) { + throw new RuntimeException("Stub!"); + } + + // /** @hide */ + // @Override + // protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + // throw new RuntimeException("Stub!"); + // } + } + + static class ChildListForAccessibility { + + public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) { + throw new RuntimeException("Stub!"); + } + + public void recycle() { + throw new RuntimeException("Stub!"); + } + + public int getChildCount() { + throw new RuntimeException("Stub!"); + } + + public View getChildAt(int index) { + throw new RuntimeException("Stub!"); + } + } + + static class ViewLocationHolder implements Comparable { + + public static final int COMPARISON_STRATEGY_STRIPE = 1; + + public static final int COMPARISON_STRATEGY_LOCATION = 2; + + public View mView; + + public static ViewLocationHolder obtain(ViewGroup root, View view) { + throw new RuntimeException("Stub!"); + } + + public static void setComparisonStrategy(int strategy) { + throw new RuntimeException("Stub!"); + } + + public void recycle() { + throw new RuntimeException("Stub!"); + } + + @Override + public int compareTo(ViewLocationHolder another) { + throw new RuntimeException("Stub!"); + } + } + + // /** @hide */ + // @Override + // protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + // throw new RuntimeException("Stub!"); + // } + + /** @hide */ + // @Override + public final void onDescendantUnbufferedRequested() { + throw new RuntimeException("Stub!"); + } + + // @Override + // public void dispatchCreateViewTranslationRequest(@NonNull Map viewIds, + // @NonNull int[] supportedFormats, + // @Nullable TranslationCapability capability, + // @NonNull List requests) { + // throw new RuntimeException("Stub!"); + // } + + // @Nullable + // @Override + // public OnBackInvokedDispatcher findOnBackInvokedDispatcherForChild(@NonNull View child, + // @NonNull View requester) { + // throw new RuntimeException("Stub!"); + // } + + @Override + public void setRequestedFrameRate(float frameRate) { + throw new RuntimeException("Stub!"); + } + + public void propagateRequestedFrameRate(float frameRate, boolean forceOverride) { + throw new RuntimeException("Stub!"); + } + + @Override + void overrideFrameRate(float frameRate, boolean forceOverride) { + throw new RuntimeException("Stub!"); + } +} diff --git a/AndroidCompat/src/main/java/android/webkit/WebChromeClient.java b/AndroidCompat/src/main/java/android/webkit/WebChromeClient.java new file mode 100644 index 00000000..4efc6be1 --- /dev/null +++ b/AndroidCompat/src/main/java/android/webkit/WebChromeClient.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Message; +import android.view.View; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +public class WebChromeClient { + + public void onProgressChanged(WebView view, int newProgress) {} + + public void onReceivedTitle(WebView view, String title) {} + + public void onReceivedIcon(WebView view, Bitmap icon) {} + + public void onReceivedTouchIconUrl(WebView view, String url, + boolean precomposed) {} + + public interface CustomViewCallback { + public void onCustomViewHidden(); + } + + public void onShowCustomView(View view, CustomViewCallback callback) {}; + + @Deprecated + public void onShowCustomView(View view, int requestedOrientation, + CustomViewCallback callback) {}; + + public void onHideCustomView() {} + + public boolean onCreateWindow(WebView view, boolean isDialog, + boolean isUserGesture, Message resultMsg) { + return false; + } + + public void onRequestFocus(WebView view) {} + + public void onCloseWindow(WebView window) {} + + public boolean onJsAlert(WebView view, String url, String message, + JsResult result) { + return false; + } + + public boolean onJsConfirm(WebView view, String url, String message, + JsResult result) { + return false; + } + + public boolean onJsPrompt(WebView view, String url, String message, + String defaultValue, JsPromptResult result) { + return false; + } + + public boolean onJsBeforeUnload(WebView view, String url, String message, + JsResult result) { + return false; + } + + @Deprecated + public void onExceededDatabaseQuota(String url, String databaseIdentifier, + long quota, long estimatedDatabaseSize, long totalQuota, + WebStorage.QuotaUpdater quotaUpdater) { + // This default implementation passes the current quota back to WebCore. + // WebCore will interpret this that new quota was declined. + quotaUpdater.updateQuota(quota); + } + + @Deprecated + public void onReachedMaxAppCacheSize(long requiredStorage, long quota, + WebStorage.QuotaUpdater quotaUpdater) { + quotaUpdater.updateQuota(quota); + } + + public void onGeolocationPermissionsShowPrompt(String origin, + GeolocationPermissions.Callback callback) {} + + public void onGeolocationPermissionsHidePrompt() {} + + public void onPermissionRequest(PermissionRequest request) { + request.deny(); + } + + public void onPermissionRequestCanceled(PermissionRequest request) {} + + // This method was only called when using the JSC javascript engine. V8 became + // the default JS engine with Froyo and support for building with JSC was + // removed in b/5495373. V8 does not have a mechanism for making a callback such + // as this. + @Deprecated + public boolean onJsTimeout() { + return true; + } + + @Deprecated + public void onConsoleMessage(String message, int lineNumber, String sourceID) { } + + public boolean onConsoleMessage(ConsoleMessage consoleMessage) { + // Call the old version of this function for backwards compatability. + onConsoleMessage(consoleMessage.message(), consoleMessage.lineNumber(), + consoleMessage.sourceId()); + return false; + } + + @Nullable + public Bitmap getDefaultVideoPoster() { + return null; + } + + @Nullable + public View getVideoLoadingProgressView() { + return null; + } + + /** Obtains a list of all visited history items, used for link coloring + */ + public void getVisitedHistory(ValueCallback callback) { + } + + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, + FileChooserParams fileChooserParams) { + return false; + } + + public static abstract class FileChooserParams { + @SystemApi + public static final long ENABLE_FILE_SYSTEM_ACCESS = 364980165L; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface Mode {} + + /** Open single file. Requires that the file exists before allowing the user to pick it. */ + public static final int MODE_OPEN = 0; + /** Like Open but allows multiple files to be selected. */ + public static final int MODE_OPEN_MULTIPLE = 1; + /** Like Open but allows a folder to be selected. */ + public static final int MODE_OPEN_FOLDER = 2; + /** Allows picking a nonexistent file and saving it. */ + public static final int MODE_SAVE = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionMode {} + + /** File or directory should be opened for reading only. */ + public static final int PERMISSION_MODE_READ = 0; + /** File or directory should be opened for read and write. */ + public static final int PERMISSION_MODE_READ_WRITE = 1; + + @Nullable + public static Uri[] parseResult(int resultCode, Intent data) { + throw new RuntimeException("Stub!"); + } + + @Mode + public abstract int getMode(); + + public abstract String[] getAcceptTypes(); + + public abstract boolean isCaptureEnabled(); + + @Nullable + public abstract CharSequence getTitle(); + + @Nullable + public abstract String getFilenameHint(); + + @PermissionMode + public int getPermissionMode() { + return PERMISSION_MODE_READ; + } + + public abstract Intent createIntent(); + } + + @SystemApi + @Deprecated + public void openFileChooser(ValueCallback uploadFile, String acceptType, String capture) { + uploadFile.onReceiveValue(null); + } +} diff --git a/AndroidCompat/src/main/java/android/webkit/WebSettings.java b/AndroidCompat/src/main/java/android/webkit/WebSettings.java new file mode 100644 index 00000000..54c622e5 --- /dev/null +++ b/AndroidCompat/src/main/java/android/webkit/WebSettings.java @@ -0,0 +1,1757 @@ +/* + * Copyright (C) 2007 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.webkit; + +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.Context; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Manages settings state for a WebView. When a WebView is first created, it + * obtains a set of default settings. These default settings will be returned + * from any getter call. A {@code WebSettings} object obtained from + * {@link WebView#getSettings()} is tied to the life of the WebView. If a WebView has + * been destroyed, any method call on {@code WebSettings} will throw an + * {@link IllegalStateException}. + */ +// This is an abstract base class: concrete WebViewProviders must +// create a class derived from this, and return an instance of it in the +// WebViewProvider.getWebSettingsProvider() method implementation. +public abstract class WebSettings { + /** + * Enum for controlling the layout of html. + *
    + *
  • {@code NORMAL} means no rendering changes. This is the recommended choice for maximum + * compatibility across different platforms and Android versions.
  • + *
  • {@code SINGLE_COLUMN} moves all content into one column that is the width of the + * view.
  • + *
  • {@code NARROW_COLUMNS} makes all columns no wider than the screen if possible. Only use + * this for API levels prior to {@link android.os.Build.VERSION_CODES#KITKAT}.
  • + *
  • {@code TEXT_AUTOSIZING} boosts font size of paragraphs based on heuristics to make + * the text readable when viewing a wide-viewport layout in the overview mode. + * It is recommended to enable zoom support {@link #setSupportZoom} when + * using this mode. Supported from API level + * {@link android.os.Build.VERSION_CODES#KITKAT}
  • + *
+ */ + // XXX: These must match LayoutAlgorithm in Settings.h in WebCore. + public enum LayoutAlgorithm { + NORMAL, + /** + * @deprecated This algorithm is now obsolete. + */ + @Deprecated + SINGLE_COLUMN, + /** + * @deprecated This algorithm is now obsolete. + */ + @Deprecated + NARROW_COLUMNS, + TEXT_AUTOSIZING + } + + /** + * Enum for specifying the text size. + *
    + *
  • SMALLEST is 50%
  • + *
  • SMALLER is 75%
  • + *
  • NORMAL is 100%
  • + *
  • LARGER is 150%
  • + *
  • LARGEST is 200%
  • + *
+ * + * @deprecated Use {@link WebSettings#setTextZoom(int)} and {@link WebSettings#getTextZoom()} instead. + */ + @Deprecated + public enum TextSize { + SMALLEST(50), + SMALLER(75), + NORMAL(100), + LARGER(150), + LARGEST(200); + TextSize(int size) { + value = size; + } + int value; + } + + /** + * Enum for specifying the WebView's desired density. + *
    + *
  • {@code FAR} makes 100% looking like in 240dpi
  • + *
  • {@code MEDIUM} makes 100% looking like in 160dpi
  • + *
  • {@code CLOSE} makes 100% looking like in 120dpi
  • + *
+ */ + public enum ZoomDensity { + FAR(150), // 240dpi + MEDIUM(100), // 160dpi + CLOSE(75); // 120dpi + ZoomDensity(int size) { + value = size; + } + + /** + * @hide Only for use by WebViewProvider implementations + */ + public int getValue() { + return value; + } + + int value; + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface CacheMode {} + + /** + * Enable web content to apply light or dark style according to the app's theme + * and WebView to attempt to darken web content by algorithmic darkening when + * appropriate. + * + * Refer to {@link #setAlgorithmicDarkeningAllowed} for detail. + * + * @hide + */ + @SystemApi + public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L; + + /** + * Enable User-Agent Reduction for webview. + * The OS, CPU, and Build information in the default User-Agent will be + * reduced to the static "Linux; Android 10; K" string. + * Minor/build/patch version information in the default User-Agent is + * reduced to "0.0.0". The rest of the default User-Agent remains unchanged. + * + * See https://developers.google.com/privacy-sandbox/protections/user-agent + * for details related to User-Agent Reduction. + * + * @hide + */ + @SystemApi + public static final long ENABLE_USER_AGENT_REDUCTION = 371034303L; + + /** + * Default cache usage mode. If the navigation type doesn't impose any + * specific behavior, use cached resources when they are available + * and not expired, otherwise load resources from the network. + * Use with {@link #setCacheMode}. + */ + public static final int LOAD_DEFAULT = -1; + + /** + * Normal cache usage mode. Use with {@link #setCacheMode}. + * + * @deprecated This value is obsolete, as from API level + * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and onwards it has the + * same effect as {@link #LOAD_DEFAULT}. + */ + @Deprecated + public static final int LOAD_NORMAL = 0; + + /** + * Use cached resources when they are available, even if they have expired. + * Otherwise load resources from the network. + * Use with {@link #setCacheMode}. + */ + public static final int LOAD_CACHE_ELSE_NETWORK = 1; + + /** + * Don't use the cache, load from the network. + * Use with {@link #setCacheMode}. + */ + public static final int LOAD_NO_CACHE = 2; + + /** + * Don't use the network, load from the cache. + * Use with {@link #setCacheMode}. + */ + public static final int LOAD_CACHE_ONLY = 3; + + public enum RenderPriority { + NORMAL, + HIGH, + LOW + } + + /** + * The plugin state effects how plugins are treated on a page. ON means + * that any object will be loaded even if a plugin does not exist to handle + * the content. ON_DEMAND means that if there is a plugin installed that + * can handle the content, a placeholder is shown until the user clicks on + * the placeholder. Once clicked, the plugin will be enabled on the page. + * OFF means that all plugins will be turned off and any fallback content + * will be used. + */ + public enum PluginState { + ON, + ON_DEMAND, + OFF + } + + /** + * In this mode, the WebView will allow a secure origin to load content from any other origin, + * even if that origin is insecure. This is the least secure mode of operation for the WebView, + * and where possible apps should not set this mode. + * + * @see #setMixedContentMode + */ + public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0; + + /** + * In this mode, the WebView will not allow a secure origin to load content from an insecure + * origin. This is the preferred and most secure mode of operation for the WebView and apps are + * strongly advised to use this mode. + * + * @see #setMixedContentMode + */ + public static final int MIXED_CONTENT_NEVER_ALLOW = 1; + + /** + * In this mode, the WebView will attempt to be compatible with the approach of a modern web + * browser with regard to mixed content. Some insecure content may be allowed to be loaded by + * a secure origin and other types of content will be blocked. The types of content are allowed + * or blocked may change release to release and are not explicitly defined. + * + * This mode is intended to be used by apps that are not in control of the content that they + * render but desire to operate in a reasonably secure environment. For highest security, apps + * are recommended to use {@link #MIXED_CONTENT_NEVER_ALLOW}. + * + * @see #setMixedContentMode + */ + public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface ForceDark {} + + /** + * Disable force dark, irrespective of the force dark mode of the WebView parent. In this mode, + * WebView content will always be rendered as-is, regardless of whether native views are being + * automatically darkened. + * + * @see #setForceDark + * @deprecated refer to {@link #setForceDark} + */ + public static final int FORCE_DARK_OFF = 0; + + /** + * Enable force dark dependent on the state of the WebView parent view. If the WebView parent + * view is being automatically force darkened + * (see: {@link android.view.View#setForceDarkAllowed}), then WebView content will be rendered + * so as to emulate a dark theme. WebViews that are not attached to the view hierarchy will not + * be inverted. + * + * @see #setForceDark + * @deprecated refer to {@link #setForceDark} + */ + public static final int FORCE_DARK_AUTO = 1; + + /** + * Unconditionally enable force dark. In this mode WebView content will always be rendered so + * as to emulate a dark theme. + * + * @see #setForceDark + * @deprecated refer to {@link #setForceDark} + */ + public static final int FORCE_DARK_ON = 2; + + /** + * Enables dumping the pages navigation cache to a text file. The default + * is {@code false}. + * + * @deprecated This method is now obsolete. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + @Deprecated + public abstract void setNavDump(boolean enabled); + + /** + * Gets whether dumping the navigation cache is enabled. + * + * @return whether dumping the navigation cache is enabled + * @see #setNavDump + * @deprecated This method is now obsolete. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + @Deprecated + public abstract boolean getNavDump(); + + /** + * Sets whether the WebView should support zooming using its on-screen zoom + * controls and gestures. The particular zoom mechanisms that should be used + * can be set with {@link #setBuiltInZoomControls}. This setting does not + * affect zooming performed using the {@link WebView#zoomIn()} and + * {@link WebView#zoomOut()} methods. The default is {@code true}. + * + * @param support whether the WebView should support zoom + */ + public abstract void setSupportZoom(boolean support); + + /** + * Gets whether the WebView supports zoom. + * + * @return {@code true} if the WebView supports zoom + * @see #setSupportZoom + */ + public abstract boolean supportZoom(); + + /** + * Sets whether the WebView requires a user gesture to play media. + * The default is {@code true}. + * + * @param require whether the WebView requires a user gesture to play media + */ + public abstract void setMediaPlaybackRequiresUserGesture(boolean require); + + /** + * Gets whether the WebView requires a user gesture to play media. + * + * @return {@code true} if the WebView requires a user gesture to play media + * @see #setMediaPlaybackRequiresUserGesture + */ + public abstract boolean getMediaPlaybackRequiresUserGesture(); + + /** + * Sets whether the WebView should use its built-in zoom mechanisms. The + * built-in zoom mechanisms comprise on-screen zoom controls, which are + * displayed over the WebView's content, and the use of a pinch gesture to + * control zooming. Whether or not these on-screen controls are displayed + * can be set with {@link #setDisplayZoomControls}. The default is {@code false}. + *

+ * The built-in mechanisms are the only currently supported zoom + * mechanisms, so it is recommended that this setting is always enabled. + * However, on-screen zoom controls are deprecated in Android (see + * {@link android.widget.ZoomButtonsController}) so it's recommended to + * disable {@link #setDisplayZoomControls}. + * + * @param enabled whether the WebView should use its built-in zoom mechanisms + */ + // This method was intended to select between the built-in zoom mechanisms + // and the separate zoom controls. The latter were obtained using + // {@link WebView#getZoomControls}, which is now hidden. + public abstract void setBuiltInZoomControls(boolean enabled); + + /** + * Gets whether the zoom mechanisms built into WebView are being used. + * + * @return {@code true} if the zoom mechanisms built into WebView are being used + * @see #setBuiltInZoomControls + */ + public abstract boolean getBuiltInZoomControls(); + + /** + * Sets whether the WebView should display on-screen zoom controls when + * using the built-in zoom mechanisms. See {@link #setBuiltInZoomControls}. + * The default is {@code true}. However, on-screen zoom controls are deprecated + * in Android (see {@link android.widget.ZoomButtonsController}) so it's + * recommended to set this to {@code false}. + * + * @param enabled whether the WebView should display on-screen zoom controls + */ + public abstract void setDisplayZoomControls(boolean enabled); + + /** + * Gets whether the WebView displays on-screen zoom controls when using + * the built-in zoom mechanisms. + * + * @return {@code true} if the WebView displays on-screen zoom controls when using + * the built-in zoom mechanisms + * @see #setDisplayZoomControls + */ + public abstract boolean getDisplayZoomControls(); + + /** + * Enables or disables file access within WebView. + * Note that this enables or disables file system access only. Assets and resources + * are still accessible using file:///android_asset and file:///android_res. + *

+ * Note: Apps should not open {@code file://} URLs from any external source in + * WebView, don't enable this if your app accepts arbitrary URLs from external sources. + * It's recommended to always use + * + * androidx.webkit.WebViewAssetLoader to access files including assets and resources over + * {@code http(s)://} schemes, instead of {@code file://} URLs. To prevent possible security + * issues targeting {@link android.os.Build.VERSION_CODES#Q} and earlier, you should explicitly + * set this value to {@code false}. + *

+ * The default value is {@code true} for apps targeting + * {@link android.os.Build.VERSION_CODES#Q} and below, and {@code false} when targeting + * {@link android.os.Build.VERSION_CODES#R} and above. + */ + public abstract void setAllowFileAccess(boolean allow); + + /** + * Gets whether this WebView supports file access. + * + * @see #setAllowFileAccess + */ + public abstract boolean getAllowFileAccess(); + + /** + * Enables or disables content URL access within WebView. Content URL + * access allows WebView to load content from a content provider installed + * in the system. The default is enabled. + */ + public abstract void setAllowContentAccess(boolean allow); + + /** + * Gets whether this WebView supports content URL access. + * + * @see #setAllowContentAccess + */ + public abstract boolean getAllowContentAccess(); + + /** + * Sets whether the WebView loads pages in overview mode, that is, + * zooms out the content to fit on screen by width. This setting is + * taken into account when the content width is greater than the width + * of the WebView control, for example, when {@link #getUseWideViewPort} + * is enabled. The default is {@code false}. + */ + public abstract void setLoadWithOverviewMode(boolean overview); + + /** + * Gets whether this WebView loads pages in overview mode. + * + * @return whether this WebView loads pages in overview mode + * @see #setLoadWithOverviewMode + */ + public abstract boolean getLoadWithOverviewMode(); + + /** + * Sets whether the WebView will enable smooth transition while panning or + * zooming or while the window hosting the WebView does not have focus. + * If it is {@code true}, WebView will choose a solution to maximize the performance. + * e.g. the WebView's content may not be updated during the transition. + * If it is false, WebView will keep its fidelity. The default value is {@code false}. + * + * @deprecated This method is now obsolete, and will become a no-op in future. + */ + @Deprecated + public abstract void setEnableSmoothTransition(boolean enable); + + /** + * Gets whether the WebView enables smooth transition while panning or + * zooming. + * + * @see #setEnableSmoothTransition + * + * @deprecated This method is now obsolete, and will become a no-op in future. + */ + @Deprecated + public abstract boolean enableSmoothTransition(); + + /** + * Sets whether the WebView uses its background for over scroll background. + * If {@code true}, it will use the WebView's background. If {@code false}, it will use an + * internal pattern. Default is {@code true}. + * + * @deprecated This method is now obsolete. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + @Deprecated + public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean view); + + /** + * Gets whether this WebView uses WebView's background instead of + * internal pattern for over scroll background. + * + * @see #setUseWebViewBackgroundForOverscrollBackground + * @deprecated This method is now obsolete. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + @Deprecated + public abstract boolean getUseWebViewBackgroundForOverscrollBackground(); + + /** + * Sets whether the WebView should save form data. In {@link android.os.Build.VERSION_CODES#O}, + * the platform has implemented a fully functional Autofill feature to store form data. + * Therefore, the Webview form data save feature is disabled. + * + *

Note that the feature will continue to be supported on older versions of + * Android as before. + * + * @see #getSaveFormData + * @deprecated In Android O and afterwards, this function does not have any effect. Form data + * will be saved to platform's autofill service if applicable. + */ + @Deprecated + public abstract void setSaveFormData(boolean save); + + /** + * Gets whether the WebView saves form data. In {@link android.os.Build.VERSION_CODES#O}, the + * platform has implemented a fully functional Autofill feature to store form data. Therefore, + * the Webview form data save feature is disabled. + * + *

Note that the feature will continue to be supported on older versions of + * Android as before. + * + * @return whether the WebView saves form data + * @see #setSaveFormData + * @deprecated In Android O and afterwards, this function does not have any effect. Form data + * will be filled from the platform's autofill service if applicable. + */ + @Deprecated + public abstract boolean getSaveFormData(); + + /** + * Sets whether the WebView should save passwords. The default is {@code true}. + * @deprecated Saving passwords in WebView will not be supported in future versions. + */ + @Deprecated + public abstract void setSavePassword(boolean save); + + /** + * Gets whether the WebView saves passwords. + * + * @return whether the WebView saves passwords + * @see #setSavePassword + * @deprecated Saving passwords in WebView will not be supported in future versions. + */ + @Deprecated + public abstract boolean getSavePassword(); + + /** + * Sets the text zoom of the page in percent. The default is 100. + * + * @param textZoom the text zoom in percent + */ + public abstract void setTextZoom(int textZoom); + + /** + * Gets the text zoom of the page in percent. + * + * @return the text zoom of the page in percent + * @see #setTextZoom + */ + public abstract int getTextZoom(); + + /** + * Sets policy for third party cookies. + * Developers should access this via {@link CookieManager#setShouldAcceptThirdPartyCookies}. + * @hide Internal API. + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + public abstract void setAcceptThirdPartyCookies(boolean accept); + + /** + * Gets policy for third party cookies. + * Developers should access this via {@link CookieManager#getShouldAcceptThirdPartyCookies}. + * @hide Internal API + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + public abstract boolean getAcceptThirdPartyCookies(); + + /** + * Sets the text size of the page. The default is {@link TextSize#NORMAL}. + * + * @param t the text size as a {@link TextSize} value + * @deprecated Use {@link #setTextZoom} instead. + */ + @Deprecated + public synchronized void setTextSize(TextSize t) { + setTextZoom(t.value); + } + + /** + * Gets the text size of the page. If the text size was previously specified + * in percent using {@link #setTextZoom}, this will return the closest + * matching {@link TextSize}. + * + * @return the text size as a {@link TextSize} value + * @see #setTextSize + * @deprecated Use {@link #getTextZoom} instead. + */ + @Deprecated + public synchronized TextSize getTextSize() { + TextSize closestSize = null; + int smallestDelta = Integer.MAX_VALUE; + int textSize = getTextZoom(); + for (TextSize size : TextSize.values()) { + int delta = Math.abs(textSize - size.value); + if (delta == 0) { + return size; + } + if (delta < smallestDelta) { + smallestDelta = delta; + closestSize = size; + } + } + return closestSize != null ? closestSize : TextSize.NORMAL; + } + + /** + * Sets the default zoom density of the page. This must be called from the UI + * thread. The default is {@link ZoomDensity#MEDIUM}. + * + * This setting is not recommended for use in new applications. If the WebView + * is utilized to display mobile-oriented pages, the desired effect can be achieved by + * adjusting 'width' and 'initial-scale' attributes of page's 'meta viewport' + * tag. For pages lacking the tag, {@link android.webkit.WebView#setInitialScale} + * and {@link #setUseWideViewPort} can be used. + * + * @param zoom the zoom density + * @deprecated This method is no longer supported, see the function documentation for + * recommended alternatives. + */ + @Deprecated + public abstract void setDefaultZoom(ZoomDensity zoom); + + /** + * Gets the default zoom density of the page. This should be called from + * the UI thread. + * + * This setting is not recommended for use in new applications. + * + * @return the zoom density + * @see #setDefaultZoom + * @deprecated Will only return the default value. + */ + @Deprecated + public abstract ZoomDensity getDefaultZoom(); + + /** + * Enables using light touches to make a selection and activate mouseovers. + * @deprecated From {@link android.os.Build.VERSION_CODES#JELLY_BEAN} this + * setting is obsolete and has no effect. + */ + @Deprecated + public abstract void setLightTouchEnabled(boolean enabled); + + /** + * Gets whether light touches are enabled. + * @see #setLightTouchEnabled + * @deprecated This setting is obsolete. + */ + @Deprecated + public abstract boolean getLightTouchEnabled(); + + /** + * Controlled a rendering optimization that is no longer present. Setting + * it now has no effect. + * + * @deprecated This setting now has no effect. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public void setUseDoubleTree(boolean use) { + // Specified to do nothing, so no need for derived classes to override. + } + + /** + * Controlled a rendering optimization that is no longer present. Setting + * it now has no effect. + * + * @deprecated This setting now has no effect. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public boolean getUseDoubleTree() { + // Returns false unconditionally, so no need for derived classes to override. + return false; + } + + /** + * Sets the user-agent string using an integer code. + *

    + *
  • 0 means the WebView should use an Android user-agent string
  • + *
  • 1 means the WebView should use a desktop user-agent string
  • + *
+ * Other values are ignored. The default is an Android user-agent string, + * i.e. code value 0. + * + * @param ua the integer code for the user-agent string + * @deprecated Please use {@link #setUserAgentString} instead. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + @Deprecated + public abstract void setUserAgent(int ua); + + /** + * Gets the user-agent as an integer code. + *
    + *
  • -1 means the WebView is using a custom user-agent string set with + * {@link #setUserAgentString}
  • + *
  • 0 means the WebView should use an Android user-agent string
  • + *
  • 1 means the WebView should use a desktop user-agent string
  • + *
+ * + * @return the integer code for the user-agent string + * @see #setUserAgent + * @deprecated Please use {@link #getUserAgentString} instead. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + @Deprecated + public abstract int getUserAgent(); + + /** + * Sets whether the WebView should enable support for the "viewport" + * HTML meta tag or should use a wide viewport. + * When the value of the setting is {@code false}, the layout width is always set to the + * width of the WebView control in device-independent (CSS) pixels. + * When the value is {@code true} and the page contains the viewport meta tag, the value + * of the width specified in the tag is used. If the page does not contain the tag or + * does not provide a width, then a wide viewport will be used. + * + * @param use whether to enable support for the viewport meta tag + */ + public abstract void setUseWideViewPort(boolean use); + + /** + * Gets whether the WebView supports the "viewport" + * HTML meta tag or will use a wide viewport. + * + * @return {@code true} if the WebView supports the viewport meta tag + * @see #setUseWideViewPort + */ + public abstract boolean getUseWideViewPort(); + + /** + * Sets whether the WebView should support multiple windows. + * + *

If set to {@code true}, the {@link WebChromeClient#onCreateWindow} + * callback must be implemented by the application to handle the + * creation of new windows. + * + *

The default is {@code false}. When multiple window support is disabled, + * requests to open new windows (either from the {@code window.open()} + * JavaScript API or from links with {@code target="_blank"}) will instead + * be treated as top-level navigations, replacing the current page in the + * same WebView. + * + * @param support whether to support multiple windows + */ + public abstract void setSupportMultipleWindows(boolean support); + + /** + * Gets whether the WebView supports multiple windows. + * + * @return {@code true} if the WebView supports multiple windows + * @see #setSupportMultipleWindows + */ + public abstract boolean supportMultipleWindows(); + + /** + * Sets the underlying layout algorithm. This will cause a re-layout of the + * WebView. The default is {@link LayoutAlgorithm#NARROW_COLUMNS}. + * + * @param l the layout algorithm to use, as a {@link LayoutAlgorithm} value + */ + public abstract void setLayoutAlgorithm(LayoutAlgorithm l); + + /** + * Gets the current layout algorithm. + * + * @return the layout algorithm in use, as a {@link LayoutAlgorithm} value + * @see #setLayoutAlgorithm + */ + public abstract LayoutAlgorithm getLayoutAlgorithm(); + + /** + * Sets the standard font family name. The default is "sans-serif". + * + * @param font a font family name + */ + public abstract void setStandardFontFamily(String font); + + /** + * Gets the standard font family name. + * + * @return the standard font family name as a string + * @see #setStandardFontFamily + */ + public abstract String getStandardFontFamily(); + + /** + * Sets the fixed font family name. The default is "monospace". + * + * @param font a font family name + */ + public abstract void setFixedFontFamily(String font); + + /** + * Gets the fixed font family name. + * + * @return the fixed font family name as a string + * @see #setFixedFontFamily + */ + public abstract String getFixedFontFamily(); + + /** + * Sets the sans-serif font family name. The default is "sans-serif". + * + * @param font a font family name + */ + public abstract void setSansSerifFontFamily(String font); + + /** + * Gets the sans-serif font family name. + * + * @return the sans-serif font family name as a string + * @see #setSansSerifFontFamily + */ + public abstract String getSansSerifFontFamily(); + + /** + * Sets the serif font family name. The default is "sans-serif". + * + * @param font a font family name + */ + public abstract void setSerifFontFamily(String font); + + /** + * Gets the serif font family name. The default is "serif". + * + * @return the serif font family name as a string + * @see #setSerifFontFamily + */ + public abstract String getSerifFontFamily(); + + /** + * Sets the cursive font family name. The default is "cursive". + * + * @param font a font family name + */ + public abstract void setCursiveFontFamily(String font); + + /** + * Gets the cursive font family name. + * + * @return the cursive font family name as a string + * @see #setCursiveFontFamily + */ + public abstract String getCursiveFontFamily(); + + /** + * Sets the fantasy font family name. The default is "fantasy". + * + * @param font a font family name + */ + public abstract void setFantasyFontFamily(String font); + + /** + * Gets the fantasy font family name. + * + * @return the fantasy font family name as a string + * @see #setFantasyFontFamily + */ + public abstract String getFantasyFontFamily(); + + /** + * Sets the minimum font size. The default is 8. + * + * @param size a non-negative integer between 1 and 72. Any number outside + * the specified range will be pinned. + */ + public abstract void setMinimumFontSize(int size); + + /** + * Gets the minimum font size. + * + * @return a non-negative integer between 1 and 72 + * @see #setMinimumFontSize + */ + public abstract int getMinimumFontSize(); + + /** + * Sets the minimum logical font size. The default is 8. + * + * @param size a non-negative integer between 1 and 72. Any number outside + * the specified range will be pinned. + */ + public abstract void setMinimumLogicalFontSize(int size); + + /** + * Gets the minimum logical font size. + * + * @return a non-negative integer between 1 and 72 + * @see #setMinimumLogicalFontSize + */ + public abstract int getMinimumLogicalFontSize(); + + /** + * Sets the default font size. The default is 16. + * + * @param size a non-negative integer between 1 and 72. Any number outside + * the specified range will be pinned. + */ + public abstract void setDefaultFontSize(int size); + + /** + * Gets the default font size. + * + * @return a non-negative integer between 1 and 72 + * @see #setDefaultFontSize + */ + public abstract int getDefaultFontSize(); + + /** + * Sets the default fixed font size. The default is 16. + * + * @param size a non-negative integer between 1 and 72. Any number outside + * the specified range will be pinned. + */ + public abstract void setDefaultFixedFontSize(int size); + + /** + * Gets the default fixed font size. + * + * @return a non-negative integer between 1 and 72 + * @see #setDefaultFixedFontSize + */ + public abstract int getDefaultFixedFontSize(); + + /** + * Sets whether the WebView should load image resources. Note that this method + * controls loading of all images, including those embedded using the data + * URI scheme. Use {@link #setBlockNetworkImage} to control loading only + * of images specified using network URI schemes. Note that if the value of this + * setting is changed from {@code false} to {@code true}, all images resources referenced + * by content currently displayed by the WebView are loaded automatically. + * The default is {@code true}. + * + * @param flag whether the WebView should load image resources + */ + public abstract void setLoadsImagesAutomatically(boolean flag); + + /** + * Gets whether the WebView loads image resources. This includes + * images embedded using the data URI scheme. + * + * @return {@code true} if the WebView loads image resources + * @see #setLoadsImagesAutomatically + */ + public abstract boolean getLoadsImagesAutomatically(); + + /** + * Sets whether the WebView should not load image resources from the + * network (resources accessed via http and https URI schemes). Note + * that this method has no effect unless + * {@link #getLoadsImagesAutomatically} returns {@code true}. Also note that + * disabling all network loads using {@link #setBlockNetworkLoads} + * will also prevent network images from loading, even if this flag is set + * to false. When the value of this setting is changed from {@code true} to {@code false}, + * network images resources referenced by content currently displayed by + * the WebView are fetched automatically. The default is {@code false}. + * + * @param flag whether the WebView should not load image resources from the + * network + * @see #setBlockNetworkLoads + */ + public abstract void setBlockNetworkImage(boolean flag); + + /** + * Gets whether the WebView does not load image resources from the network. + * + * @return {@code true} if the WebView does not load image resources from the network + * @see #setBlockNetworkImage + */ + public abstract boolean getBlockNetworkImage(); + + /** + * Sets whether the WebView should not load resources from the network. + * Use {@link #setBlockNetworkImage} to only avoid loading + * image resources. Note that if the value of this setting is + * changed from {@code true} to {@code false}, network resources referenced by content + * currently displayed by the WebView are not fetched until + * {@link android.webkit.WebView#reload} is called. + * If the application does not have the + * {@link android.Manifest.permission#INTERNET} permission, attempts to set + * a value of {@code false} will cause a {@link java.lang.SecurityException} + * to be thrown. The default value is {@code false} if the application has the + * {@link android.Manifest.permission#INTERNET} permission, otherwise it is + * {@code true}. + * + * @param flag {@code true} means block network loads by the WebView + * @see android.webkit.WebView#reload + */ + public abstract void setBlockNetworkLoads(boolean flag); + + /** + * Gets whether the WebView does not load any resources from the network. + * + * @return {@code true} if the WebView does not load any resources from the network + * @see #setBlockNetworkLoads + */ + public abstract boolean getBlockNetworkLoads(); + + /** + * Tells the WebView to enable JavaScript execution. + * The default is {@code false}. + * + * @param flag {@code true} if the WebView should execute JavaScript + */ + public abstract void setJavaScriptEnabled(boolean flag); + + /** + * Sets whether cross-origin requests in the context of a file scheme URL should be allowed to + * access content from any origin. This includes access to content from other file + * scheme URLs or web contexts. Note that some access such as image HTML elements doesn't + * follow same-origin rules and isn't affected by this setting. + *

+ * Don't enable this setting if you open files that may be created or altered by + * external sources. Enabling this setting allows malicious scripts loaded in a {@code file://} + * context to launch cross-site scripting attacks, either accessing arbitrary local files + * including WebView cookies, app private data or even credentials used on arbitrary web sites. + *

+ * Loading content via {@code file://} URLs is generally discouraged. See the note in + * {@link #setAllowFileAccess}. + *

+ * The default value is {@code true} for apps targeting + * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, and {@code false} + * when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and above. To prevent + * possible violation of same domain policy when targeting + * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, you should + * explicitly set this value to {@code false}. + * + * @param flag whether JavaScript running in the context of a file scheme URL should be allowed + * to access content from any origin + * @deprecated This setting is not secure, please use + * + * androidx.webkit.WebViewAssetLoader to load file content securely. + */ + @Deprecated + public abstract void setAllowUniversalAccessFromFileURLs(boolean flag); + + /** + * Sets whether cross-origin requests in the context of a file scheme URL should be allowed to + * access content from other file scheme URLs. Note that some accesses such as image HTML + * elements don't follow same-origin rules and aren't affected by this setting. + *

+ * Don't enable this setting if you open files that may be created or altered by + * external sources. Enabling this setting allows malicious scripts loaded in a {@code file://} + * context to access arbitrary local files including WebView cookies and app private data. + *

+ * Loading content via {@code file://} URLs is generally discouraged. See the note in + * {@link #setAllowFileAccess}. + *

+ * Note that the value of this setting is ignored if the value of + * {@link #getAllowUniversalAccessFromFileURLs} is {@code true}. The default value is + * {@code true} for apps targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} + * and below, and {@code false} when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} + * and above. To prevent possible violation of same domain policy when targeting + * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, you should + * explicitly set this value to {@code false}. + * + * @param flag whether JavaScript running in the context of a file scheme + * URL should be allowed to access content from other file + * scheme URLs + * @deprecated This setting is not secure, please use + * + * androidx.webkit.WebViewAssetLoader to load file content securely. + */ + @Deprecated + public abstract void setAllowFileAccessFromFileURLs(boolean flag); + + /** + * Sets whether the WebView should enable plugins. The default is {@code false}. + * + * @param flag {@code true} if plugins should be enabled + * @deprecated This method has been deprecated in favor of + * {@link #setPluginState} + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + @Deprecated + public abstract void setPluginsEnabled(boolean flag); + + /** + * Tells the WebView to enable, disable, or have plugins on demand. On + * demand mode means that if a plugin exists that can handle the embedded + * content, a placeholder icon will be shown instead of the plugin. When + * the placeholder is clicked, the plugin will be enabled. The default is + * {@link PluginState#OFF}. + * + * @param state a PluginState value + * @deprecated Plugins are not supported in API level + * {@link android.os.Build.VERSION_CODES#KITKAT} or later; + * enabling plugins is a no-op. + */ + @Deprecated + public abstract void setPluginState(PluginState state); + + /** + * Sets a custom path to plugins used by the WebView. This method is + * obsolete since each plugin is now loaded from its own package. + * + * @param pluginsPath a String path to the directory containing plugins + * @deprecated This method is no longer used as plugins are loaded from + * their own APK via the system's package manager. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} + */ + @Deprecated + public void setPluginsPath(String pluginsPath) { + // Specified to do nothing, so no need for derived classes to override. + } + + /** + * Sets the path to where database storage API databases should be saved. + * In order for the database storage API to function correctly, this method + * must be called with a path to which the application can write. This + * method should only be called once: repeated calls are ignored. + * + * @param databasePath a path to the directory where databases should be + * saved. + * @deprecated Database paths are managed by the implementation and calling this method + * will have no effect. + */ + @Deprecated + public abstract void setDatabasePath(String databasePath); + + /** + * Sets the path where the Geolocation databases should be saved. In order + * for Geolocation permissions and cached positions to be persisted, this + * method must be called with a path to which the application can write. + * + * @param databasePath a path to the directory where databases should be + * saved. + * @deprecated Geolocation database are managed by the implementation and calling this method + * will have no effect. + */ + @Deprecated + public abstract void setGeolocationDatabasePath(String databasePath); + + /** + * Sets whether the Application Caches API should be enabled. The default + * is {@code false}. Note that in order for the Application Caches API to be + * enabled, a valid database path must also be supplied to + * {@link #setAppCachePath}. + * + * @param flag {@code true} if the WebView should enable Application Caches + * @deprecated The Application Cache API is deprecated and this method will + * become a no-op on all Android versions once support is + * removed in Chromium. Consider using Service Workers instead. + * See https://web.dev/appcache-removal/ for more information. + * @removed The Application Cache API is no longer supported and this method + * is a no-op on WebView 95 and later. Consider using Service Workers + * instead. See https://web.dev/appcache-removal/ for more information. + */ + @Deprecated + public void setAppCacheEnabled(boolean flag) {} + + /** + * Sets the path to the Application Caches files. In order for the + * Application Caches API to be enabled, this method must be called with a + * path to which the application can write. This method should only be + * called once: repeated calls are ignored. + * + * @param appCachePath a String path to the directory containing + * Application Caches files. + * @see #setAppCacheEnabled + * @deprecated The Application Cache API is deprecated and this method will + * become a no-op on all Android versions once support is + * removed in Chromium. Consider using Service Workers instead. + * See https://web.dev/appcache-removal/ for more information. + * @removed The Application Cache API is no longer supported and this method + * is a no-op on WebView 95 and later. Consider using Service Workers + * instead. See https://web.dev/appcache-removal/ for more information. + */ + @Deprecated + public void setAppCachePath(String appCachePath) {} + + /** + * Sets the maximum size for the Application Cache content. The passed size + * will be rounded to the nearest value that the database can support, so + * this should be viewed as a guide, not a hard limit. Setting the + * size to a value less than current database size does not cause the + * database to be trimmed. The default size is {@link Long#MAX_VALUE}. + * It is recommended to leave the maximum size set to the default value. + * + * @param appCacheMaxSize the maximum size in bytes + * @deprecated Quota is managed automatically; this method is a no-op. + * @removed Quota is managed automatically; this method is a no-op. + */ + @Deprecated + public void setAppCacheMaxSize(long appCacheMaxSize) {} + + /** + * Sets whether the database storage API is enabled. The default value is + * false. See also {@link #setDatabasePath} for how to correctly set up the + * database storage API. + * + * This setting is global in effect, across all WebView instances in a process. + * Note you should only modify this setting prior to making any WebView + * page load within a given process, as the WebView implementation may ignore + * changes to this setting after that point. + * + * @param flag {@code true} if the WebView should use the database storage API + * @deprecated WebSQL is deprecated and this method will become a no-op on all + * Android versions once support is removed in Chromium. See + * https://developer.chrome.com/blog/deprecating-web-sql for more information. + */ + @Deprecated + public abstract void setDatabaseEnabled(boolean flag); + + /** + * Sets whether the DOM storage API is enabled. The default value is {@code false}. + * + * @param flag {@code true} if the WebView should use the DOM storage API + */ + public abstract void setDomStorageEnabled(boolean flag); + + /** + * Gets whether the DOM Storage APIs are enabled. + * + * @return {@code true} if the DOM Storage APIs are enabled + * @see #setDomStorageEnabled + */ + public abstract boolean getDomStorageEnabled(); + + /** + * Gets the path to where database storage API databases are saved. + * + * @return the String path to the database storage API databases + * @see #setDatabasePath + * @deprecated Database paths are managed by the implementation this method is obsolete. + */ + @Deprecated + public abstract String getDatabasePath(); + + /** + * Gets whether the database storage API is enabled. + * + * @return {@code true} if the database storage API is enabled + * @see #setDatabaseEnabled + * @deprecated WebSQL is deprecated and this method will become a no-op on all + * Android versions once support is removed in Chromium. See + * https://developer.chrome.com/blog/deprecating-web-sql for more information. + */ + @Deprecated + public abstract boolean getDatabaseEnabled(); + + /** + * Sets whether Geolocation is enabled. The default is {@code true}. + *

+ * Please note that in order for the Geolocation API to be usable + * by a page in the WebView, the following requirements must be met: + *

    + *
  • an application must have permission to access the device location, + * see {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}; + *
  • an application must provide an implementation of the + * {@link WebChromeClient#onGeolocationPermissionsShowPrompt} callback + * to receive notifications that a page is requesting access to location + * via the JavaScript Geolocation API. + *
+ *

+ * + * @param flag whether Geolocation should be enabled + */ + public abstract void setGeolocationEnabled(boolean flag); + + /** + * Gets whether JavaScript is enabled. + * + * @return {@code true} if JavaScript is enabled + * @see #setJavaScriptEnabled + */ + public abstract boolean getJavaScriptEnabled(); + + /** + * Gets whether JavaScript running in the context of a file scheme URL can + * access content from any origin. This includes access to content from + * other file scheme URLs. + * + * @return whether JavaScript running in the context of a file scheme URL + * can access content from any origin + * @see #setAllowUniversalAccessFromFileURLs + */ + public abstract boolean getAllowUniversalAccessFromFileURLs(); + + /** + * Gets whether JavaScript running in the context of a file scheme URL can + * access content from other file scheme URLs. + * + * @return whether JavaScript running in the context of a file scheme URL + * can access content from other file scheme URLs + * @see #setAllowFileAccessFromFileURLs + */ + public abstract boolean getAllowFileAccessFromFileURLs(); + + /** + * Gets whether plugins are enabled. + * + * @return {@code true} if plugins are enabled + * @see #setPluginsEnabled + * @deprecated This method has been replaced by {@link #getPluginState} + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + @Deprecated + public abstract boolean getPluginsEnabled(); + + /** + * Gets the current state regarding whether plugins are enabled. + * + * @return the plugin state as a {@link PluginState} value + * @see #setPluginState + * @deprecated Plugins are not supported in API level + * {@link android.os.Build.VERSION_CODES#KITKAT} or later; + * enabling plugins is a no-op. + */ + @Deprecated + public abstract PluginState getPluginState(); + + /** + * Gets the directory that contains the plugin libraries. This method is + * obsolete since each plugin is now loaded from its own package. + * + * @return an empty string + * @deprecated This method is no longer used as plugins are loaded from + * their own APK via the system's package manager. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} + */ + @Deprecated + public String getPluginsPath() { + // Unconditionally returns empty string, so no need for derived classes to override. + return ""; + } + + /** + * Allows JavaScript to open windows without a user gesture. This applies to + * the JavaScript function {@code window.open()}. The default is + * {@code false}: attempts without a user gesture will fail and do nothing. + *

+ * This is not affected by the {@link #setSupportMultipleWindows} setting; + * the user gesture requirement is enforced even if multiple windows are + * disabled. + * + * @param flag {@code true} if JavaScript can open windows without a user + * gesture. + */ + public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean flag); + + /** + * Gets whether JavaScript can open windows without a user gesture. + * + * @return {@code true} if JavaScript can open windows without a user + * gesture using {@code window.open()} + * @see #setJavaScriptCanOpenWindowsAutomatically + */ + public abstract boolean getJavaScriptCanOpenWindowsAutomatically(); + + /** + * Sets the default text encoding name to use when decoding html pages. + * The default is "UTF-8". + * + * @param encoding the text encoding name + */ + public abstract void setDefaultTextEncodingName(String encoding); + + /** + * Gets the default text encoding name. + * + * @return the default text encoding name as a string + * @see #setDefaultTextEncodingName + */ + public abstract String getDefaultTextEncodingName(); + + /** + * Sets the WebView's user-agent string. If the string is {@code null} or empty, + * the system default value will be used. + * + *

If the user-agent is overridden in this way, the values of the User-Agent Client Hints + * headers and {@code navigator.userAgentData} for this WebView could be changed. + *

See androidx.webkit.WebSettingsCompat + * #setUserAgentMetadata(WebSettings,UserAgentMetadata) for details. + * + *

Note that starting from {@link android.os.Build.VERSION_CODES#KITKAT} Android + * version, changing the user-agent while loading a web page causes WebView + * to initiate loading once again. + * + * @param ua new user-agent string + */ + public abstract void setUserAgentString(@Nullable String ua); + + /** + * Gets the WebView's user-agent string. + * + * @return the WebView's user-agent string + * @see #setUserAgentString + */ + public abstract String getUserAgentString(); + + /** + * Returns the default User-Agent used by a WebView. + * An instance of WebView could use a different User-Agent if a call + * is made to {@link WebSettings#setUserAgentString(String)}. + * + * @param context a Context object used to access application assets + */ + public static String getDefaultUserAgent(Context context) { + throw new RuntimeException("Stub!"); + } + + /** + * Tells the WebView whether it needs to set a node to have focus when + * {@link WebView#requestFocus(int, android.graphics.Rect)} is called. The + * default value is {@code true}. + * + * @param flag whether the WebView needs to set a node + */ + public abstract void setNeedInitialFocus(boolean flag); + + /** + * Sets the priority of the Render thread. Unlike the other settings, this + * one only needs to be called once per process. The default value is + * {@link RenderPriority#NORMAL}. + * + * @param priority the priority + * @deprecated It is not recommended to adjust thread priorities, and this will + * not be supported in future versions. + */ + @Deprecated + public abstract void setRenderPriority(RenderPriority priority); + + /** + * Overrides the way the cache is used. The way the cache is used is based + * on the navigation type. For a normal page load, the cache is checked + * and content is re-validated as needed. When navigating back, content is + * not revalidated, instead the content is just retrieved from the cache. + * This method allows the client to override this behavior by specifying + * one of {@link #LOAD_DEFAULT}, + * {@link #LOAD_CACHE_ELSE_NETWORK}, {@link #LOAD_NO_CACHE} or + * {@link #LOAD_CACHE_ONLY}. The default value is {@link #LOAD_DEFAULT}. + * + * @param mode the mode to use + */ + public abstract void setCacheMode(@CacheMode int mode); + + /** + * Gets the current setting for overriding the cache mode. + * + * @return the current setting for overriding the cache mode + * @see #setCacheMode + */ + @CacheMode + public abstract int getCacheMode(); + + /** + * Configures the WebView's behavior when a secure origin attempts to load a resource from an + * insecure origin. + * + * By default, apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or below default + * to {@link #MIXED_CONTENT_ALWAYS_ALLOW}. Apps targeting + * {@link android.os.Build.VERSION_CODES#LOLLIPOP} default to {@link #MIXED_CONTENT_NEVER_ALLOW}. + * + * The preferred and most secure mode of operation for the WebView is + * {@link #MIXED_CONTENT_NEVER_ALLOW} and use of {@link #MIXED_CONTENT_ALWAYS_ALLOW} is + * strongly discouraged. + * + * @param mode The mixed content mode to use. One of {@link #MIXED_CONTENT_NEVER_ALLOW}, + * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. + */ + public abstract void setMixedContentMode(int mode); + + /** + * Gets the current behavior of the WebView with regard to loading insecure content from a + * secure origin. + * @return The current setting, one of {@link #MIXED_CONTENT_NEVER_ALLOW}, + * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. + */ + public abstract int getMixedContentMode(); + + /** + * Sets whether to use a video overlay for embedded encrypted video. + * In API levels prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, encrypted video can + * only be rendered directly on a secure video surface, so it had been a hard problem to play + * encrypted video in HTML. When this flag is on, WebView can play encrypted video (MSE/EME) + * by using a video overlay (aka hole-punching) for videos embedded using HTML <video> + * tag.
+ * Caution: This setting is intended for use only in a narrow set of circumstances and apps + * should only enable it if they require playback of encrypted video content. It will impose + * the following limitations on the WebView: + *

    + *
  • Only one video overlay can be played at a time. + *
  • Changes made to position or dimensions of a video element may be propagated to the + * corresponding video overlay with a noticeable delay. + *
  • The video overlay is not visible to web APIs and as such may not interact with + * script or styling. For example, CSS styles applied to the <video> tag may be ignored. + *
+ * This is not an exhaustive set of constraints and it may vary with new versions of the + * WebView. + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean flag); + + /** + * Gets whether a video overlay will be used for embedded encrypted video. + * + * @return {@code true} if WebView uses a video overlay for embedded encrypted video. + * @see #setVideoOverlayForEmbeddedEncryptedVideoEnabled + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + public abstract boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled(); + + /** + * Sets whether this WebView should raster tiles when it is + * offscreen but attached to a window. Turning this on can avoid + * rendering artifacts when animating an offscreen WebView on-screen. + * Offscreen WebViews in this mode use more memory. The default value is + * false.
+ * Please follow these guidelines to limit memory usage: + *
    + *
  • WebView size should be not be larger than the device screen size. + *
  • Limit use of this mode to a small number of WebViews. Use it for + * visible WebViews and WebViews about to be animated to visible. + *
+ */ + public abstract void setOffscreenPreRaster(boolean enabled); + + /** + * Gets whether this WebView should raster tiles when it is + * offscreen but attached to a window. + * @return {@code true} if this WebView will raster tiles when it is + * offscreen but attached to a window. + */ + public abstract boolean getOffscreenPreRaster(); + + + /** + * Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to + * protect against malware and phishing attacks by verifying the links. + * + *

+ * Safe Browsing can be disabled for all WebViews using a manifest tag (read general Safe Browsing info). The + * manifest tag has a lower precedence than this API. + * + *

+ * Safe Browsing is enabled by default for devices which support it. + * + * @param enabled Whether Safe Browsing is enabled. + */ + public abstract void setSafeBrowsingEnabled(boolean enabled); + + /** + * Gets whether Safe Browsing is enabled. + * See {@link #setSafeBrowsingEnabled}. + * + * @return {@code true} if Safe Browsing is enabled and {@code false} otherwise. + */ + public abstract boolean getSafeBrowsingEnabled(); + + + /** + * Set the force dark mode for this WebView. + * + * @param forceDark the force dark mode to set. + * @see #getForceDark + * @deprecated The "force dark" model previously implemented by WebView was complex + * and didn't interoperate well with current Web standards for + * {@code prefers-color-scheme} and {@code color-scheme}. In apps with + * {@code targetSdkVersion} ≥ {@link android.os.Build.VERSION_CODES#TIRAMISU} + * this API is a no-op and WebView will always use the dark style defined by web content + * authors if the app's theme is dark. To customize the behavior, refer to + * {@link #setAlgorithmicDarkeningAllowed}. + */ + public void setForceDark(@ForceDark int forceDark) { + // Stub implementation to satisfy Roboelectrc shadows that don't override this yet. + } + + /** + * Get the force dark mode for this WebView. + * The default force dark mode is {@link #FORCE_DARK_AUTO}. + * + * @return the currently set force dark mode. + * @see #setForceDark + * @deprecated refer to {@link #setForceDark}. + */ + public @ForceDark int getForceDark() { + // Stub implementation to satisfy Roboelectrc shadows that don't override this yet. + return FORCE_DARK_AUTO; + } + + /** + * Control whether algorithmic darkening is allowed. + * + *

+ * Note: This API and the behaviour described only apply to apps with + * {@code targetSdkVersion} ≥ {@link android.os.Build.VERSION_CODES#TIRAMISU}. + * + *

+ * WebView always sets the media query {@code prefers-color-scheme} according to the app's + * theme attribute {@link android.R.styleable#Theme_isLightTheme isLightTheme}, i.e. + * {@code prefers-color-scheme} is {@code light} if isLightTheme is true or not specified, + * otherwise it is {@code dark}. This means that the web content's light or dark style will + * be applied automatically to match the app's theme if the content supports it. + * + *

+ * Algorithmic darkening is disallowed by default. + *

+ * If the app's theme is dark and it allows algorithmic darkening, WebView will attempt to + * darken web content using an algorithm, if the content doesn't define its own dark styles + * and doesn't explicitly disable darkening. + * + *

+ * If Android is applying Force Dark to WebView then WebView will ignore the value of + * this setting and behave as if it were set to true. + * + *

+ * The deprecated {@link #setForceDark} and related API are no-ops in apps with + * {@code targetSdkVersion} ≥ {@link android.os.Build.VERSION_CODES#TIRAMISU}, + * but they still apply to apps with + * {@code targetSdkVersion} < {@link android.os.Build.VERSION_CODES#TIRAMISU}. + * + *

+ * The below table summarizes how APIs work with different apps. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AppWeb content which uses {@code prefers-color-scheme}Web content which does not use {@code prefers-color-scheme}
App with {@code isLightTheme} True or not setRenders with the light theme defined by the content author.Renders with the default styling defined by the content author.
App with Android forceDark in effectRenders with the dark theme defined by the content author.Renders with the styling modified to dark colors by an algorithm + * if allowed by the content author.
App with {@code isLightTheme} False, + * {@code targetSdkVersion} < {@link android.os.Build.VERSION_CODES#TIRAMISU}, + * and has {@code FORCE_DARK_AUTO}Renders with the dark theme defined by the content author.Renders with the default styling defined by the content author.
App with {@code isLightTheme} False, + * {@code targetSdkVersion} ≥ {@link android.os.Build.VERSION_CODES#TIRAMISU}, + * and {@code setAlgorithmicDarkening(false)}Renders with the dark theme defined by the content author.Renders with the default styling defined by the content author.
App with {@code isLightTheme} False, + * {@code targetSdkVersion} ≥ {@link android.os.Build.VERSION_CODES#TIRAMISU}, + * and {@code setAlgorithmicDarkening(true)}Renders with the dark theme defined by the content author.Renders with the styling modified to dark colors by an algorithm if allowed + * by the content author.
+ *

+ * + * @param allow allow algorithmic darkening or not. + */ + public void setAlgorithmicDarkeningAllowed(boolean allow) { + // Stub implementation to satisfy Roboelectrc shadows that don't override this yet. + } + + /** + * Get if algorithmic darkening is allowed or not for this WebView. + * The default is false. + * + * @return if the algorithmic darkening is allowed or not. + * @see #setAlgorithmicDarkeningAllowed + */ + public boolean isAlgorithmicDarkeningAllowed() { + // Stub implementation to satisfy Roboelectrc shadows that don't override this yet. + return false; + } + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.PARAMETER, ElementType.METHOD}) + private @interface MenuItemFlags {} + + /** + * Disables the action mode menu items according to {@code menuItems} flag. + * @param menuItems an integer field flag for the menu items to be disabled. + */ + public abstract void setDisabledActionModeMenuItems(@MenuItemFlags int menuItems); + + /** + * Gets the action mode menu items that are disabled, expressed in an integer field flag. + * The default value is {@link #MENU_ITEM_NONE} + * + * @return all the disabled menu item flags combined with bitwise OR. + */ + public abstract @MenuItemFlags int getDisabledActionModeMenuItems(); + + /** + * No menu items should be disabled. + * + * @see #setDisabledActionModeMenuItems + */ + public static final int MENU_ITEM_NONE = 0; + + /** + * Disable menu item "Share". + * + * @see #setDisabledActionModeMenuItems + */ + public static final int MENU_ITEM_SHARE = 1 << 0; + + /** + * Disable menu item "Web Search". + * + * @see #setDisabledActionModeMenuItems + */ + public static final int MENU_ITEM_WEB_SEARCH = 1 << 1; + + /** + * Disable all the action mode menu items for text processing. + * By default WebView searches for activities that are able to handle + * {@link android.content.Intent#ACTION_PROCESS_TEXT} and show them in the + * action mode menu. If this flag is set via {@link + * #setDisabledActionModeMenuItems}, these menu items will be disabled. + * + * @see #setDisabledActionModeMenuItems + */ + public static final int MENU_ITEM_PROCESS_TEXT = 1 << 2; +} diff --git a/AndroidCompat/src/main/java/android/webkit/WebView.java b/AndroidCompat/src/main/java/android/webkit/WebView.java new file mode 100644 index 00000000..f82a39cb --- /dev/null +++ b/AndroidCompat/src/main/java/android/webkit/WebView.java @@ -0,0 +1,1368 @@ +/* + * 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.webkit; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.Widget; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Picture; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.net.http.SslCertificate; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.StrictMode; +import android.print.PrintDocumentAdapter; +import android.util.AttributeSet; +import android.util.Log; +import android.util.LongSparseArray; +import android.util.SparseArray; +import android.view.DragEvent; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.View; +import android.view.ViewDebug; +import android.view.ViewGroup; +import android.view.ViewStructure; +import android.view.ViewTreeObserver; +import android.view.WindowInsets; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.textclassifier.TextClassifier; +import android.widget.AbsoluteLayout; + +import java.io.BufferedWriter; +import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +import xyz.nulldev.androidcompat.CallableArgument; + +// Implementation notes. +// The WebView is a thin API class that delegates its public API to a backend WebViewProvider +// class instance. WebView extends {@link AbsoluteLayout} for backward compatibility reasons. +// Methods are delegated to the provider implementation: all public API methods introduced in this +// file are fully delegated, whereas public and protected methods from the View base classes are +// only delegated where a specific need exists for them to do so. +@Widget +public class WebView extends AbsoluteLayout + implements ViewTreeObserver.OnGlobalFocusChangeListener, + ViewGroup.OnHierarchyChangeListener /*, ViewDebug.HierarchyHandler */ { + + private static final String LOGTAG = "WebView"; + + // Throwing an exception for incorrect thread usage if the + // build target is JB MR2 or newer. Defaults to false, and is + // set in the WebView constructor. + private static volatile boolean sEnforceThreadChecking = false; + private static CallableArgument mProviderFactory; + + public static void setProviderFactory(CallableArgument factory) { + mProviderFactory = factory; + } + + public class WebViewTransport { + private WebView mWebview; + + public synchronized void setWebView(@Nullable WebView webview) { + mWebview = webview; + } + + @Nullable + public synchronized WebView getWebView() { + return mWebview; + } + } + + public static final String SCHEME_GEO = "geo:0,0?q="; + + public interface FindListener { + public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting); + } + + public static abstract class VisualStateCallback { + public abstract void onComplete(long requestId); + } + + @Deprecated + public interface PictureListener { + @Deprecated + void onNewPicture(WebView view, @Nullable Picture picture); + } + + public static class HitTestResult { + public static final int EDIT_TEXT_TYPE = 9; + + private int mType; + private String mExtra; + + @SystemApi + public HitTestResult() { + } + + @SystemApi + public void setType(int type) { + mType = type; + } + + @SystemApi + public void setExtra(String extra) { + mExtra = extra; + } + + public int getType() { + return mType; + } + + @Nullable + public String getExtra() { + return mExtra; + } + } + + public WebView(@NonNull Context context) { + this(context, null); + } + + public WebView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + this(context, attrs, defStyleAttr, defStyleRes, null, false); + } + + @Deprecated + public WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, + boolean privateBrowsing) { + this(context, attrs, defStyleAttr, 0, null, privateBrowsing); + } + + protected WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, + @Nullable Map javaScriptInterfaces, boolean privateBrowsing) { + this(context, attrs, defStyleAttr, 0, javaScriptInterfaces, privateBrowsing); + } + + @SuppressWarnings("deprecation") // for super() call into deprecated base class constructor. + protected WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes, @Nullable Map javaScriptInterfaces, + boolean privateBrowsing) { + super(context, attrs, defStyleAttr, defStyleRes); + + if (context == null) { + throw new IllegalArgumentException("Invalid context argument"); + } + if (mWebViewThread == null) { + throw new RuntimeException( + "WebView cannot be initialized on a thread that has no Looper."); + } + sEnforceThreadChecking = true; + checkThread(); + + ensureProviderCreated(); + mProvider.init(javaScriptInterfaces, privateBrowsing); + } + + @Deprecated + public void setHorizontalScrollbarOverlay(boolean overlay) { + } + + @Deprecated + public void setVerticalScrollbarOverlay(boolean overlay) { + } + + @Deprecated + public boolean overlayHorizontalScrollbar() { + // The old implementation defaulted to true, so return true for consistency + return true; + } + + @Deprecated + public boolean overlayVerticalScrollbar() { + // The old implementation defaulted to false, so return false for consistency + return false; + } + + @Deprecated + public int getVisibleTitleHeight() { + checkThread(); + return mProvider.getVisibleTitleHeight(); + } + + @Nullable + public SslCertificate getCertificate() { + checkThread(); + return mProvider.getCertificate(); + } + + @Deprecated + public void setCertificate(SslCertificate certificate) { + checkThread(); + mProvider.setCertificate(certificate); + } + + //------------------------------------------------------------------------- + // Methods called by activity + //------------------------------------------------------------------------- + + @Deprecated + public void savePassword(String host, String username, String password) { + checkThread(); + mProvider.savePassword(host, username, password); + } + + @Deprecated + public void setHttpAuthUsernamePassword(String host, String realm, + String username, String password) { + checkThread(); + mProvider.setHttpAuthUsernamePassword(host, realm, username, password); + } + + @Deprecated + @Nullable + public String[] getHttpAuthUsernamePassword(String host, String realm) { + checkThread(); + return mProvider.getHttpAuthUsernamePassword(host, realm); + } + + public void destroy() { + checkThread(); + mProvider.destroy(); + } + + @Deprecated + public static void enablePlatformNotifications() { + // noop + } + + @Deprecated + public static void disablePlatformNotifications() { + // noop + } + + public static void freeMemoryForTests() { + throw new RuntimeException("Stub!"); + } + + public void setNetworkAvailable(boolean networkUp) { + checkThread(); + mProvider.setNetworkAvailable(networkUp); + } + + @Nullable + public WebBackForwardList saveState(@NonNull Bundle outState) { + checkThread(); + return mProvider.saveState(outState); + } + + @Deprecated + public boolean savePicture(Bundle b, final File dest) { + checkThread(); + return mProvider.savePicture(b, dest); + } + + @Deprecated + public boolean restorePicture(Bundle b, File src) { + checkThread(); + return mProvider.restorePicture(b, src); + } + + @Nullable + public WebBackForwardList restoreState(@NonNull Bundle inState) { + checkThread(); + return mProvider.restoreState(inState); + } + + public void loadUrl(@NonNull String url, @NonNull Map additionalHttpHeaders) { + checkThread(); + mProvider.loadUrl(url, additionalHttpHeaders); + } + + public void loadUrl(@NonNull String url) { + checkThread(); + mProvider.loadUrl(url); + } + + public void postUrl(@NonNull String url, @NonNull byte[] postData) { + checkThread(); + if (URLUtil.isNetworkUrl(url)) { + mProvider.postUrl(url, postData); + } else { + mProvider.loadUrl(url); + } + } + + public void loadData(@NonNull String data, @Nullable String mimeType, + @Nullable String encoding) { + checkThread(); + mProvider.loadData(data, mimeType, encoding); + } + + public void loadDataWithBaseURL(@Nullable String baseUrl, @NonNull String data, + @Nullable String mimeType, @Nullable String encoding, @Nullable String historyUrl) { + checkThread(); + mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); + } + + public void evaluateJavascript(@NonNull String script, @Nullable ValueCallback + resultCallback) { + checkThread(); + mProvider.evaluateJavaScript(script, resultCallback); + } + + public void saveWebArchive(@NonNull String filename) { + checkThread(); + mProvider.saveWebArchive(filename); + } + + public void saveWebArchive(@NonNull String basename, boolean autoname, + @Nullable ValueCallback callback) { + checkThread(); + mProvider.saveWebArchive(basename, autoname, callback); + } + + public void stopLoading() { + checkThread(); + mProvider.stopLoading(); + } + + public void reload() { + checkThread(); + mProvider.reload(); + } + + public boolean canGoBack() { + checkThread(); + return mProvider.canGoBack(); + } + + public void goBack() { + checkThread(); + mProvider.goBack(); + } + + public boolean canGoForward() { + checkThread(); + return mProvider.canGoForward(); + } + + public void goForward() { + checkThread(); + mProvider.goForward(); + } + + public boolean canGoBackOrForward(int steps) { + checkThread(); + return mProvider.canGoBackOrForward(steps); + } + + public void goBackOrForward(int steps) { + checkThread(); + mProvider.goBackOrForward(steps); + } + + public boolean isPrivateBrowsingEnabled() { + checkThread(); + return mProvider.isPrivateBrowsingEnabled(); + } + + public boolean pageUp(boolean top) { + checkThread(); + return mProvider.pageUp(top); + } + + public boolean pageDown(boolean bottom) { + checkThread(); + return mProvider.pageDown(bottom); + } + + public void postVisualStateCallback(long requestId, @NonNull VisualStateCallback callback) { + checkThread(); + mProvider.insertVisualStateCallback(requestId, callback); + } + + @Deprecated + public void clearView() { + checkThread(); + mProvider.clearView(); + } + + @Deprecated + public Picture capturePicture() { + checkThread(); + return mProvider.capturePicture(); + } + + @Deprecated + public PrintDocumentAdapter createPrintDocumentAdapter() { + checkThread(); + return mProvider.createPrintDocumentAdapter("default"); + } + + @NonNull + public PrintDocumentAdapter createPrintDocumentAdapter(@NonNull String documentName) { + checkThread(); + return mProvider.createPrintDocumentAdapter(documentName); + } + + @Deprecated + @ViewDebug.ExportedProperty(category = "webview") + public float getScale() { + checkThread(); + return mProvider.getScale(); + } + + public void setInitialScale(int scaleInPercent) { + checkThread(); + mProvider.setInitialScale(scaleInPercent); + } + + public void invokeZoomPicker() { + checkThread(); + mProvider.invokeZoomPicker(); + } + + @NonNull + public HitTestResult getHitTestResult() { + checkThread(); + return mProvider.getHitTestResult(); + } + + public void requestFocusNodeHref(@Nullable Message hrefMsg) { + checkThread(); + mProvider.requestFocusNodeHref(hrefMsg); + } + + public void requestImageRef(@NonNull Message msg) { + checkThread(); + mProvider.requestImageRef(msg); + } + + @ViewDebug.ExportedProperty(category = "webview") + @Nullable + public String getUrl() { + checkThread(); + return mProvider.getUrl(); + } + + @ViewDebug.ExportedProperty(category = "webview") + @Nullable + public String getOriginalUrl() { + checkThread(); + return mProvider.getOriginalUrl(); + } + + @ViewDebug.ExportedProperty(category = "webview") + @Nullable + public String getTitle() { + checkThread(); + return mProvider.getTitle(); + } + + @Nullable + public Bitmap getFavicon() { + checkThread(); + return mProvider.getFavicon(); + } + + public String getTouchIconUrl() { + return mProvider.getTouchIconUrl(); + } + + public int getProgress() { + checkThread(); + return mProvider.getProgress(); + } + + @ViewDebug.ExportedProperty(category = "webview") + public int getContentHeight() { + checkThread(); + return mProvider.getContentHeight(); + } + + @ViewDebug.ExportedProperty(category = "webview") + public int getContentWidth() { + return mProvider.getContentWidth(); + } + + public void pauseTimers() { + checkThread(); + mProvider.pauseTimers(); + } + + public void resumeTimers() { + checkThread(); + mProvider.resumeTimers(); + } + + public void onPause() { + checkThread(); + mProvider.onPause(); + } + + public void onResume() { + checkThread(); + mProvider.onResume(); + } + + public boolean isPaused() { + return mProvider.isPaused(); + } + + @Deprecated + public void freeMemory() { + checkThread(); + mProvider.freeMemory(); + } + + public void clearCache(boolean includeDiskFiles) { + checkThread(); + mProvider.clearCache(includeDiskFiles); + } + + public void clearFormData() { + checkThread(); + mProvider.clearFormData(); + } + + public void clearHistory() { + checkThread(); + mProvider.clearHistory(); + } + + public void clearSslPreferences() { + checkThread(); + mProvider.clearSslPreferences(); + } + + public static void clearClientCertPreferences(@Nullable Runnable onCleared) { + throw new RuntimeException("Stub!"); + } + + @Deprecated + public static void startSafeBrowsing(@NonNull Context context, + @Nullable ValueCallback callback) { + throw new RuntimeException("Stub!"); + } + + public static void setSafeBrowsingWhitelist(@NonNull List hosts, + @Nullable ValueCallback callback) { + throw new RuntimeException("Stub!"); + } + + @NonNull + public static Uri getSafeBrowsingPrivacyPolicyUrl() { + throw new RuntimeException("Stub!"); + } + + @NonNull + public WebBackForwardList copyBackForwardList() { + checkThread(); + return mProvider.copyBackForwardList(); + + } + + public void setFindListener(@Nullable FindListener listener) { + checkThread(); + setupFindListenerIfNeeded(); + mFindListener.mUserFindListener = listener; + } + + public void findNext(boolean forward) { + checkThread(); + mProvider.findNext(forward); + } + + @Deprecated + public int findAll(String find) { + checkThread(); + StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync"); + return mProvider.findAll(find); + } + + public void findAllAsync(@NonNull String find) { + checkThread(); + mProvider.findAllAsync(find); + } + + @Deprecated + public boolean showFindDialog(@Nullable String text, boolean showIme) { + checkThread(); + return mProvider.showFindDialog(text, showIme); + } + + @Nullable + @Deprecated + public static String findAddress(String addr) { + throw new RuntimeException("Stub!"); + } + + public static void enableSlowWholeDocumentDraw() { + throw new RuntimeException("Stub!"); + } + + public void clearMatches() { + checkThread(); + mProvider.clearMatches(); + } + + public void documentHasImages(@NonNull Message response) { + checkThread(); + mProvider.documentHasImages(response); + } + + public void setWebViewClient(@NonNull WebViewClient client) { + checkThread(); + mProvider.setWebViewClient(client); + } + + @NonNull + public WebViewClient getWebViewClient() { + checkThread(); + return mProvider.getWebViewClient(); + } + + + @Nullable + public WebViewRenderProcess getWebViewRenderProcess() { + checkThread(); + return mProvider.getWebViewRenderProcess(); + } + + public void setWebViewRenderProcessClient( + @NonNull Executor executor, + @NonNull WebViewRenderProcessClient webViewRenderProcessClient) { + checkThread(); + mProvider.setWebViewRenderProcessClient( + executor, webViewRenderProcessClient); + } + + public void setWebViewRenderProcessClient( + @Nullable WebViewRenderProcessClient webViewRenderProcessClient) { + checkThread(); + mProvider.setWebViewRenderProcessClient(null, webViewRenderProcessClient); + } + + @Nullable + public WebViewRenderProcessClient getWebViewRenderProcessClient() { + checkThread(); + return mProvider.getWebViewRenderProcessClient(); + } + + public void setDownloadListener(@Nullable DownloadListener listener) { + checkThread(); + mProvider.setDownloadListener(listener); + } + + public void setWebChromeClient(@Nullable WebChromeClient client) { + checkThread(); + mProvider.setWebChromeClient(client); + } + + @Nullable + public WebChromeClient getWebChromeClient() { + checkThread(); + return mProvider.getWebChromeClient(); + } + + @Deprecated + public void setPictureListener(PictureListener listener) { + checkThread(); + mProvider.setPictureListener(listener); + } + + public void addJavascriptInterface(@NonNull Object object, @NonNull String name) { + checkThread(); + mProvider.addJavascriptInterface(object, name); + } + + public void removeJavascriptInterface(@NonNull String name) { + checkThread(); + mProvider.removeJavascriptInterface(name); + } + + @NonNull + public WebMessagePort[] createWebMessageChannel() { + checkThread(); + return mProvider.createWebMessageChannel(); + } + + public void postWebMessage(@NonNull WebMessage message, @NonNull Uri targetOrigin) { + checkThread(); + mProvider.postMessageToMainFrame(message, targetOrigin); + } + + @NonNull + public WebSettings getSettings() { + checkThread(); + return mProvider.getSettings(); + } + + public static void setWebContentsDebuggingEnabled(boolean enabled) { + throw new RuntimeException("Stub!"); + } + + public static void setDataDirectorySuffix(@NonNull String suffix) { + throw new RuntimeException("Stub!"); + } + + public static void disableWebView() { + } + + + @Deprecated + public void refreshPlugins(boolean reloadOpenPages) { + checkThread(); + } + + @Deprecated + public void emulateShiftHeld() { + checkThread(); + } + + @Override + // Cannot add @hide as this can always be accessed via the interface. + @Deprecated + public void onChildViewAdded(View parent, View child) {} + + @Override + // Cannot add @hide as this can always be accessed via the interface. + @Deprecated + public void onChildViewRemoved(View p, View child) {} + + @Override + // Cannot add @hide as this can always be accessed via the interface. + @Deprecated + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + } + + @Deprecated + public void setMapTrackballToArrowKeys(boolean setMap) { + checkThread(); + mProvider.setMapTrackballToArrowKeys(setMap); + } + + + public void flingScroll(int vx, int vy) { + checkThread(); + mProvider.flingScroll(vx, vy); + } + + @Deprecated + public View getZoomControls() { + checkThread(); + return mProvider.getZoomControls(); + } + + @Deprecated + public boolean canZoomIn() { + checkThread(); + return mProvider.canZoomIn(); + } + + @Deprecated + public boolean canZoomOut() { + checkThread(); + return mProvider.canZoomOut(); + } + + public void zoomBy(float zoomFactor) { + checkThread(); + if (zoomFactor < 0.01) + throw new IllegalArgumentException("zoomFactor must be greater than 0.01."); + if (zoomFactor > 100.0) + throw new IllegalArgumentException("zoomFactor must be less than 100."); + mProvider.zoomBy(zoomFactor); + } + + public boolean zoomIn() { + checkThread(); + return mProvider.zoomIn(); + } + + public boolean zoomOut() { + checkThread(); + return mProvider.zoomOut(); + } + + @Deprecated + public void debugDump() { + checkThread(); + } + + public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) { + mProvider.dumpViewHierarchyWithProperties(out, level); + } + + public View findHierarchyView(String className, int hashCode) { + return mProvider.findHierarchyView(className, hashCode); + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface RendererPriority {} + + public static final int RENDERER_PRIORITY_IMPORTANT = 2; + + public void setRendererPriorityPolicy( + @RendererPriority int rendererRequestedPriority, + boolean waivedWhenNotVisible) { + mProvider.setRendererPriorityPolicy(rendererRequestedPriority, waivedWhenNotVisible); + } + + @RendererPriority + public int getRendererRequestedPriority() { + return mProvider.getRendererRequestedPriority(); + } + + public boolean getRendererPriorityWaivedWhenNotVisible() { + return mProvider.getRendererPriorityWaivedWhenNotVisible(); + } + + public void setTextClassifier(@Nullable TextClassifier textClassifier) { + mProvider.setTextClassifier(textClassifier); + } + + @NonNull + public TextClassifier getTextClassifier() { + return mProvider.getTextClassifier(); + } + + @NonNull + public static ClassLoader getWebViewClassLoader() { + throw new RuntimeException("Stub!"); + } + + @NonNull + public Looper getWebViewLooper() { + return mWebViewThread; + } + + //------------------------------------------------------------------------- + // Interface for WebView providers + //------------------------------------------------------------------------- + + @SystemApi + public WebViewProvider getWebViewProvider() { + return mProvider; + } + + //------------------------------------------------------------------------- + // Package-private internal stuff + //------------------------------------------------------------------------- + + // Only used by android.webkit.FindActionModeCallback. + void setFindDialogFindListener(FindListener listener) { + checkThread(); + setupFindListenerIfNeeded(); + mFindListener.mFindDialogFindListener = listener; + } + + // Only used by android.webkit.FindActionModeCallback. + void notifyFindDialogDismissed() { + checkThread(); + mProvider.notifyFindDialogDismissed(); + } + + //------------------------------------------------------------------------- + // Private internal stuff + //------------------------------------------------------------------------- + + private WebViewProvider mProvider; + + private class FindListenerDistributor implements FindListener { + private FindListener mFindDialogFindListener; + private FindListener mUserFindListener; + + @Override + public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting) { + if (mFindDialogFindListener != null) { + mFindDialogFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, + isDoneCounting); + } + + if (mUserFindListener != null) { + mUserFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, + isDoneCounting); + } + } + } + private FindListenerDistributor mFindListener; + + private void setupFindListenerIfNeeded() { + if (mFindListener == null) { + mFindListener = new FindListenerDistributor(); + mProvider.setFindListener(mFindListener); + } + } + + private void ensureProviderCreated() { + checkThread(); + if (mProvider == null) { + if (mProviderFactory == null) + throw new IllegalStateException("No factory registered"); + // As this can get called during the base class constructor chain, pass the minimum + // number of dependencies here; the rest are deferred to init(). + mProvider = mProviderFactory.call(this); + } + } + + private final Looper mWebViewThread = Looper.myLooper(); + + private void checkThread() { + // Ignore mWebViewThread == null because this can be called during in the super class + // constructor, before this class's own constructor has even started. + if (mWebViewThread != null && Looper.myLooper() != mWebViewThread) { + Throwable throwable = new Throwable( + "A WebView method was called on thread '" + + Thread.currentThread().getName() + "'. " + + "All WebView methods must be called on the same thread. " + + "(Expected Looper " + mWebViewThread + " called on " + Looper.myLooper() + + ", FYI main Looper is " + Looper.getMainLooper() + ")"); + Log.w(LOGTAG, Log.getStackTraceString(throwable)); + + if (sEnforceThreadChecking) { + throw new RuntimeException(throwable); + } + } + } + + //------------------------------------------------------------------------- + // Override View methods + //------------------------------------------------------------------------- + + // TODO: Add a test that enumerates all methods in ViewDelegte & ScrollDelegate, and ensures + // there's a corresponding override (or better, caller) for each of them in here. + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mProvider.getViewDelegate().onAttachedToWindow(); + } + + // /** @hide */ + // protected void onDetachedFromWindowInternal() { + // mProvider.getViewDelegate().onDetachedFromWindow(); + // super.onDetachedFromWindowInternal(); + // } + + /** @hide */ + public void onMovedToDisplay(int displayId, Configuration config) { + mProvider.getViewDelegate().onMovedToDisplay(displayId, config); + } + + @Override + public void setLayoutParams(ViewGroup.LayoutParams params) { + mProvider.getViewDelegate().setLayoutParams(params); + } + + @Override + public void setOverScrollMode(int mode) { + super.setOverScrollMode(mode); + // This method may be called in the constructor chain, before the WebView provider is + // created. + ensureProviderCreated(); + mProvider.getViewDelegate().setOverScrollMode(mode); + } + + @Override + public void setScrollBarStyle(int style) { + mProvider.getViewDelegate().setScrollBarStyle(style); + super.setScrollBarStyle(style); + } + + @Override + protected int computeHorizontalScrollRange() { + return mProvider.getScrollDelegate().computeHorizontalScrollRange(); + } + + @Override + protected int computeHorizontalScrollOffset() { + return mProvider.getScrollDelegate().computeHorizontalScrollOffset(); + } + + @Override + protected int computeVerticalScrollRange() { + return mProvider.getScrollDelegate().computeVerticalScrollRange(); + } + + @Override + protected int computeVerticalScrollOffset() { + return mProvider.getScrollDelegate().computeVerticalScrollOffset(); + } + + @Override + protected int computeVerticalScrollExtent() { + return mProvider.getScrollDelegate().computeVerticalScrollExtent(); + } + + @Override + public void computeScroll() { + mProvider.getScrollDelegate().computeScroll(); + } + + @Override + public boolean onHoverEvent(MotionEvent event) { + return mProvider.getViewDelegate().onHoverEvent(event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return mProvider.getViewDelegate().onTouchEvent(event); + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + return mProvider.getViewDelegate().onGenericMotionEvent(event); + } + + @Override + public boolean onTrackballEvent(MotionEvent event) { + return mProvider.getViewDelegate().onTrackballEvent(event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return mProvider.getViewDelegate().onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + return mProvider.getViewDelegate().onKeyUp(keyCode, event); + } + + @Override + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { + return mProvider.getViewDelegate().onKeyMultiple(keyCode, repeatCount, event); + } + + /* + TODO: These are not currently implemented in WebViewClassic, but it seems inconsistent not + to be delegating them too. + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + return mProvider.getViewDelegate().onKeyPreIme(keyCode, event); + } + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + return mProvider.getViewDelegate().onKeyLongPress(keyCode, event); + } + @Override + public boolean onKeyShortcut(int keyCode, KeyEvent event) { + return mProvider.getViewDelegate().onKeyShortcut(keyCode, event); + } + */ + + @Override + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + AccessibilityNodeProvider provider = + mProvider.getViewDelegate().getAccessibilityNodeProvider(); + return provider == null ? super.getAccessibilityNodeProvider() : provider; + } + + @Deprecated + @Override + public boolean shouldDelayChildPressedState() { + return mProvider.getViewDelegate().shouldDelayChildPressedState(); + } + + @Override + public CharSequence getAccessibilityClassName() { + return WebView.class.getName(); + } + + @Override + public void onProvideVirtualStructure(ViewStructure structure) { + mProvider.getViewDelegate().onProvideVirtualStructure(structure); + } + + @Override + public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { + mProvider.getViewDelegate().onProvideAutofillVirtualStructure(structure, flags); + } + + @Override + public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) { + mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags); + } + + @Override + public void autofill(SparseArrayvalues) { + mProvider.getViewDelegate().autofill(values); + } + + @Override + public boolean isVisibleToUserForAutofill(int virtualId) { + return mProvider.getViewDelegate().isVisibleToUserForAutofill(virtualId); + } + + // @Nullable + // public void onCreateVirtualViewTranslationRequests(@NonNull long[] virtualIds, + // @NonNull int[] supportedFormats, + // @NonNull Consumer requestsCollector) { + // mProvider.getViewDelegate().onCreateVirtualViewTranslationRequests(virtualIds, + // supportedFormats, requestsCollector); + // } + + // public void dispatchCreateViewTranslationRequest(@NonNull Map viewIds, + // @NonNull int[] supportedFormats, + // @Nullable TranslationCapability capability, + // @NonNull List requests) { + // super.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability, requests); + // mProvider.getViewDelegate().dispatchCreateViewTranslationRequest(viewIds, supportedFormats, + // capability, requests); + // } + + // public void onVirtualViewTranslationResponses( + // @NonNull LongSparseArray response) { + // mProvider.getViewDelegate().onVirtualViewTranslationResponses(response); + // } + + // /** @hide */ + // public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + // super.onInitializeAccessibilityNodeInfoInternal(info); + // mProvider.getViewDelegate().onInitializeAccessibilityNodeInfo(info); + // } + + // /** @hide */ + // public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { + // super.onInitializeAccessibilityEventInternal(event); + // mProvider.getViewDelegate().onInitializeAccessibilityEvent(event); + // } + + /** @hide */ + public boolean performAccessibilityActionInternal(int action, Bundle arguments) { + return mProvider.getViewDelegate().performAccessibilityAction(action, arguments); + } + + /** @hide */ + protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar, + int l, int t, int r, int b) { + mProvider.getViewDelegate().onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b); + } + + @Override + protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { + mProvider.getViewDelegate().onOverScrolled(scrollX, scrollY, clampedX, clampedY); + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + mProvider.getViewDelegate().onWindowVisibilityChanged(visibility); + } + + @Override + protected void onDraw(Canvas canvas) { + mProvider.getViewDelegate().onDraw(canvas); + } + + @Override + public boolean performLongClick() { + return mProvider.getViewDelegate().performLongClick(); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + mProvider.getViewDelegate().onConfigurationChanged(newConfig); + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + return mProvider.getViewDelegate().onCreateInputConnection(outAttrs); + } + + @Override + public boolean onDragEvent(DragEvent event) { + return mProvider.getViewDelegate().onDragEvent(event); + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + // This method may be called in the constructor chain, before the WebView provider is + // created. + ensureProviderCreated(); + mProvider.getViewDelegate().onVisibilityChanged(changedView, visibility); + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + mProvider.getViewDelegate().onWindowFocusChanged(hasWindowFocus); + super.onWindowFocusChanged(hasWindowFocus); + } + + @Override + protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { + mProvider.getViewDelegate().onFocusChanged(focused, direction, previouslyFocusedRect); + super.onFocusChanged(focused, direction, previouslyFocusedRect); + } + + /** @hide */ + protected boolean setFrame(int left, int top, int right, int bottom) { + return mProvider.getViewDelegate().setFrame(left, top, right, bottom); + } + + @Override + protected void onSizeChanged(int w, int h, int ow, int oh) { + super.onSizeChanged(w, h, ow, oh); + mProvider.getViewDelegate().onSizeChanged(w, h, ow, oh); + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + mProvider.getViewDelegate().onScrollChanged(l, t, oldl, oldt); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + return mProvider.getViewDelegate().dispatchKeyEvent(event); + } + + @Override + public boolean requestFocus(int direction, Rect previouslyFocusedRect) { + return mProvider.getViewDelegate().requestFocus(direction, previouslyFocusedRect); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mProvider.getViewDelegate().onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) { + return mProvider.getViewDelegate().requestChildRectangleOnScreen(child, rect, immediate); + } + + @Override + public void setBackgroundColor(int color) { + mProvider.getViewDelegate().setBackgroundColor(color); + } + + @Override + public void setLayerType(int layerType, Paint paint) { + // ignore + } + + @Override + protected void dispatchDraw(Canvas canvas) { + mProvider.getViewDelegate().preDispatchDraw(canvas); + super.dispatchDraw(canvas); + } + + @Override + public void onStartTemporaryDetach() { + super.onStartTemporaryDetach(); + mProvider.getViewDelegate().onStartTemporaryDetach(); + } + + @Override + public void onFinishTemporaryDetach() { + super.onFinishTemporaryDetach(); + mProvider.getViewDelegate().onFinishTemporaryDetach(); + } + + @Override + public Handler getHandler() { + return mProvider.getViewDelegate().getHandler(super.getHandler()); + } + + @Override + public View findFocus() { + return mProvider.getViewDelegate().findFocus(super.findFocus()); + } + + @Nullable + public static PackageInfo getCurrentWebViewPackage() { + throw new RuntimeException("Stub!"); + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + mProvider.getViewDelegate().onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean onCheckIsTextEditor() { + return mProvider.getViewDelegate().onCheckIsTextEditor(); + } + + // /** @hide */ + // protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + // super.encodeProperties(encoder); + + // checkThread(); + // encoder.addProperty("webview:contentHeight", mProvider.getContentHeight()); + // encoder.addProperty("webview:contentWidth", mProvider.getContentWidth()); + // encoder.addProperty("webview:scale", mProvider.getScale()); + // encoder.addProperty("webview:title", mProvider.getTitle()); + // encoder.addProperty("webview:url", mProvider.getUrl()); + // encoder.addProperty("webview:originalUrl", mProvider.getOriginalUrl()); + // } + + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + WindowInsets result = mProvider.getViewDelegate().onApplyWindowInsets(insets); + if (result == null) return super.onApplyWindowInsets(insets); + return result; + } + + @Override + @Nullable + public PointerIcon onResolvePointerIcon(@NonNull MotionEvent event, int pointerIndex) { + PointerIcon icon = + mProvider.getViewDelegate().onResolvePointerIcon(event, pointerIndex); + if (icon != null) { + return icon; + } + return super.onResolvePointerIcon(event, pointerIndex); + } +} diff --git a/AndroidCompat/src/main/java/android/webkit/WebViewClient.java b/AndroidCompat/src/main/java/android/webkit/WebViewClient.java new file mode 100644 index 00000000..5bc1172b --- /dev/null +++ b/AndroidCompat/src/main/java/android/webkit/WebViewClient.java @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.annotation.Nullable; +import android.graphics.Bitmap; +import android.net.http.SslError; +import android.os.Message; +import android.view.InputEvent; +import android.view.KeyEvent; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +public class WebViewClient { + + /** + * Give the host application a chance to take control when a URL is about to be loaded in the + * current WebView. If a WebViewClient is not provided, by default WebView will ask Activity + * Manager to choose the proper handler for the URL. If a WebViewClient is provided, returning + * {@code true} causes the current WebView to abort loading the URL, while returning + * {@code false} causes the WebView to continue loading the URL as usual. + * + *

Note: Do not call {@link WebView#loadUrl(String)} with the same + * URL and then return {@code true}. This unnecessarily cancels the current load and starts a + * new load with the same URL. The correct way to continue loading a given URL is to simply + * return {@code false}, without calling {@link WebView#loadUrl(String)}. + * + *

Note: This method is not called for POST requests. + * + *

Note: This method may be called for subframes and with non-HTTP(S) + * schemes; calling {@link WebView#loadUrl(String)} with such a URL will fail. + * + * @param view The WebView that is initiating the callback. + * @param url The URL to be loaded. + * @return {@code true} to cancel the current load, otherwise return {@code false}. + * @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest) + * shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead. + */ + @Deprecated + public boolean shouldOverrideUrlLoading(WebView view, String url) { + return false; + } + + /** + * Give the host application a chance to take control when a URL is about to be loaded in the + * current WebView. If a WebViewClient is not provided, by default WebView will ask Activity + * Manager to choose the proper handler for the URL. If a WebViewClient is provided, returning + * {@code true} causes the current WebView to abort loading the URL, while returning + * {@code false} causes the WebView to continue loading the URL as usual. + * + *

This callback is not called for all page navigations. In particular, this is not called + * for navigations which the app initiated with {@code loadUrl()}: this callback would not serve + * a purpose in this case, because the app already knows about the navigation. This callback + * lets the app know about navigations initiated by the web page (such as navigations initiated + * by JavaScript code), by the user (such as when the user taps on a link), or by an HTTP + * redirect (ex. if {@code loadUrl("foo.com")} redirects to {@code "bar.com"} because of HTTP + * 301). + * + *

Note: Do not call {@link WebView#loadUrl(String)} with the request's + * URL and then return {@code true}. This unnecessarily cancels the current load and starts a + * new load with the same URL. The correct way to continue loading a given URL is to simply + * return {@code false}, without calling {@link WebView#loadUrl(String)}. + * + *

Note: This method is not called for POST requests. + * + *

Note: This method may be called for subframes and with non-HTTP(S) + * schemes; calling {@link WebView#loadUrl(String)} with such a URL will fail. + * + * @param view The WebView that is initiating the callback. + * @param request Object containing the details of the request. + * @return {@code true} to cancel the current load, otherwise return {@code false}. + */ + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + return shouldOverrideUrlLoading(view, request.getUrl().toString()); + } + + /** + * Notify the host application that a page has started loading. This method + * is called once for each main frame load so a page with iframes or + * framesets will call onPageStarted one time for the main frame. This also + * means that onPageStarted will not be called when the contents of an + * embedded frame changes, i.e. clicking a link whose target is an iframe, + * it will also not be called for fragment navigations (navigations to + * #fragment_id). + * + * @param view The WebView that is initiating the callback. + * @param url The url to be loaded. + * @param favicon The favicon for this page if it already exists in the + * database. + */ + public void onPageStarted(WebView view, String url, Bitmap favicon) { + } + + /** + * Notify the host application that a page has finished loading. This method + * is called only for main frame. Receiving an {@code onPageFinished()} callback does not + * guarantee that the next frame drawn by WebView will reflect the state of the DOM at this + * point. In order to be notified that the current DOM state is ready to be rendered, request a + * visual state callback with {@link WebView#postVisualStateCallback} and wait for the supplied + * callback to be triggered. + * + * @param view The WebView that is initiating the callback. + * @param url The url of the page. + */ + public void onPageFinished(WebView view, String url) { + } + + /** + * Notify the host application that the WebView will load the resource + * specified by the given url. + * + * @param view The WebView that is initiating the callback. + * @param url The url of the resource the WebView will load. + */ + public void onLoadResource(WebView view, String url) { + } + + /** + * Notify the host application that {@link android.webkit.WebView} content left over from + * previous page navigations will no longer be drawn. + * + *

This callback can be used to determine the point at which it is safe to make a recycled + * {@link android.webkit.WebView} visible, ensuring that no stale content is shown. It is called + * at the earliest point at which it can be guaranteed that {@link WebView#onDraw} will no + * longer draw any content from previous navigations. The next draw will display either the + * {@link WebView#setBackgroundColor background color} of the {@link WebView}, or some of the + * contents of the newly loaded page. + * + *

This method is called when the body of the HTTP response has started loading, is reflected + * in the DOM, and will be visible in subsequent draws. This callback occurs early in the + * document loading process, and as such you should expect that linked resources (for example, + * CSS and images) may not be available. + * + *

For more fine-grained notification of visual state updates, see {@link + * WebView#postVisualStateCallback}. + * + *

Please note that all the conditions and recommendations applicable to + * {@link WebView#postVisualStateCallback} also apply to this API. + * + *

This callback is only called for main frame navigations. + * + * @param view The {@link android.webkit.WebView} for which the navigation occurred. + * @param url The URL corresponding to the page navigation that triggered this callback. + */ + public void onPageCommitVisible(WebView view, String url) { + } + + /** + * Notify the host application of a resource request and allow the + * application to return the data. If the return value is {@code null}, the WebView + * will continue to load the resource as usual. Otherwise, the return + * response and data will be used. + * + *

This callback is invoked for a variety of URL schemes (e.g., {@code http(s):}, {@code + * data:}, {@code file:}, etc.), not only those schemes which send requests over the network. + * This is not called for {@code javascript:} URLs, {@code blob:} URLs, or for assets accessed + * via {@code file:///android_asset/} or {@code file:///android_res/} URLs. + * + *

In the case of redirects, this is only called for the initial resource URL, not any + * subsequent redirect URLs. + * + *

Note: This method is called on a thread + * other than the UI thread so clients should exercise caution + * when accessing private data or the view system. + * + *

Note: When Safe Browsing is enabled, these URLs still undergo Safe + * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist} + * to skip Safe Browsing checks for that host or dismiss the warning in {@link + * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}. + * + * @param view The {@link android.webkit.WebView} that is requesting the + * resource. + * @param url The raw url of the resource. + * @return A {@link android.webkit.WebResourceResponse} containing the + * response information or {@code null} if the WebView should load the + * resource itself. + * @deprecated Use {@link #shouldInterceptRequest(WebView, WebResourceRequest) + * shouldInterceptRequest(WebView, WebResourceRequest)} instead. + */ + @Deprecated + @Nullable + public WebResourceResponse shouldInterceptRequest(WebView view, + String url) { + return null; + } + + /** + * Notify the host application of a resource request and allow the + * application to return the data. If the return value is {@code null}, the WebView + * will continue to load the resource as usual. Otherwise, the return + * response and data will be used. + * + *

This callback is invoked for a variety of URL schemes (e.g., {@code http(s):}, {@code + * data:}, {@code file:}, etc.), not only those schemes which send requests over the network. + * This is not called for {@code javascript:} URLs, {@code blob:} URLs, or for assets accessed + * via {@code file:///android_asset/} or {@code file:///android_res/} URLs. + * + *

In the case of redirects, this is only called for the initial resource URL, not any + * subsequent redirect URLs. + * + *

Note: This method is called on a thread + * other than the UI thread so clients should exercise caution + * when accessing private data or the view system. + * + *

Note: When Safe Browsing is enabled, these URLs still undergo Safe + * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist} + * to skip Safe Browsing checks for that host or dismiss the warning in {@link + * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}. + * + * @param view The {@link android.webkit.WebView} that is requesting the + * resource. + * @param request Object containing the details of the request. + * @return A {@link android.webkit.WebResourceResponse} containing the + * response information or {@code null} if the WebView should load the + * resource itself. + */ + @Nullable + public WebResourceResponse shouldInterceptRequest(WebView view, + WebResourceRequest request) { + return shouldInterceptRequest(view, request.getUrl().toString()); + } + + /** + * Notify the host application that there have been an excessive number of + * HTTP redirects. As the host application if it would like to continue + * trying to load the resource. The default behavior is to send the cancel + * message. + * + * @param view The WebView that is initiating the callback. + * @param cancelMsg The message to send if the host wants to cancel + * @param continueMsg The message to send if the host wants to continue + * @deprecated This method is no longer called. When the WebView encounters + * a redirect loop, it will cancel the load. + */ + @Deprecated + public void onTooManyRedirects(WebView view, Message cancelMsg, + Message continueMsg) { + cancelMsg.sendToTarget(); + } + + // These ints must match up to the hidden values in EventHandler. + /** Generic error */ + public static final int ERROR_UNKNOWN = -1; + /** Server or proxy hostname lookup failed */ + public static final int ERROR_HOST_LOOKUP = -2; + /** Unsupported authentication scheme (not basic or digest) */ + public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3; + /** User authentication failed on server */ + public static final int ERROR_AUTHENTICATION = -4; + /** User authentication failed on proxy */ + public static final int ERROR_PROXY_AUTHENTICATION = -5; + /** Failed to connect to the server */ + public static final int ERROR_CONNECT = -6; + /** Failed to read or write to the server */ + public static final int ERROR_IO = -7; + /** Connection timed out */ + public static final int ERROR_TIMEOUT = -8; + /** Too many redirects */ + public static final int ERROR_REDIRECT_LOOP = -9; + /** Unsupported URI scheme */ + public static final int ERROR_UNSUPPORTED_SCHEME = -10; + /** Failed to perform SSL handshake */ + public static final int ERROR_FAILED_SSL_HANDSHAKE = -11; + /** Malformed URL */ + public static final int ERROR_BAD_URL = -12; + /** Generic file error */ + public static final int ERROR_FILE = -13; + /** File not found */ + public static final int ERROR_FILE_NOT_FOUND = -14; + /** Too many requests during this load */ + public static final int ERROR_TOO_MANY_REQUESTS = -15; + /** Resource load was canceled by Safe Browsing */ + public static final int ERROR_UNSAFE_RESOURCE = -16; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + public @interface SafeBrowsingThreat {} + + /** The resource was blocked for an unknown reason. */ + public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0; + /** The resource was blocked because it contains malware. */ + public static final int SAFE_BROWSING_THREAT_MALWARE = 1; + /** The resource was blocked because it contains deceptive content. */ + public static final int SAFE_BROWSING_THREAT_PHISHING = 2; + /** The resource was blocked because it contains unwanted software. */ + public static final int SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE = 3; + /** + * The resource was blocked because it may trick the user into a billing agreement. + * + *

This constant is only used when targetSdkVersion is at least {@link + * android.os.Build.VERSION_CODES#Q}. Otherwise, {@link #SAFE_BROWSING_THREAT_UNKNOWN} is used + * instead. + */ + public static final int SAFE_BROWSING_THREAT_BILLING = 4; + + /** + * Report an error to the host application. These errors are unrecoverable + * (i.e. the main resource is unavailable). The {@code errorCode} parameter + * corresponds to one of the {@code ERROR_*} constants. + * @param view The WebView that is initiating the callback. + * @param errorCode The error code corresponding to an ERROR_* value. + * @param description A String describing the error. + * @param failingUrl The url that failed to load. + * @deprecated Use {@link #onReceivedError(WebView, WebResourceRequest, WebResourceError) + * onReceivedError(WebView, WebResourceRequest, WebResourceError)} instead. + */ + @Deprecated + public void onReceivedError(WebView view, int errorCode, + String description, String failingUrl) { + } + + /** + * Report web resource loading error to the host application. These errors usually indicate + * inability to connect to the server. Note that unlike the deprecated version of the callback, + * the new version will be called for any resource (iframe, image, etc.), not just for the main + * page. Thus, it is recommended to perform minimum required work in this callback. + * @param view The WebView that is initiating the callback. + * @param request The originating request. + * @param error Information about the error occurred. + */ + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + if (request.isForMainFrame()) { + onReceivedError(view, + error.getErrorCode(), error.getDescription().toString(), + request.getUrl().toString()); + } + } + + /** + * Notify the host application that an HTTP error has been received from the server while + * loading a resource. HTTP errors have status codes >= 400. This callback will be called + * for any resource (iframe, image, etc.), not just for the main page. Thus, it is recommended + * to perform minimum required work in this callback. Note that the content of the server + * response may not be provided within the {@code errorResponse} parameter. + * @param view The WebView that is initiating the callback. + * @param request The originating request. + * @param errorResponse Information about the error occurred. + */ + public void onReceivedHttpError( + WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { + } + + /** + * As the host application if the browser should resend data as the + * requested page was a result of a POST. The default is to not resend the + * data. + * + * @param view The WebView that is initiating the callback. + * @param dontResend The message to send if the browser should not resend + * @param resend The message to send if the browser should resend data + */ + public void onFormResubmission(WebView view, Message dontResend, + Message resend) { + dontResend.sendToTarget(); + } + + /** + * Notify the host application to update its visited links database. + * + * @param view The WebView that is initiating the callback. + * @param url The url being visited. + * @param isReload {@code true} if this url is being reloaded. + */ + public void doUpdateVisitedHistory(WebView view, String url, + boolean isReload) { + } + + /** + * Notifies the host application that an SSL error occurred while loading a + * resource. The host application must call either + * {@link SslErrorHandler#cancel()} or {@link SslErrorHandler#proceed()}. + * + *

Warning: Application overrides of this method + * can be used to display custom error pages or to silently log issues, but + * the host application should always call {@code SslErrorHandler#cancel()} + * and never proceed past errors. + * + *

Note: Do not prompt the user about SSL errors. + * Users are unlikely to be able to make an informed security decision, and + * {@code WebView} does not provide a UI for showing the details of the + * error in a meaningful way. + * + *

The decision to call {@code proceed()} or {@code cancel()} may be + * retained to facilitate responses to future SSL errors. The default + * behavior is to cancel the resource loading process. + * + *

This API is called only for recoverable SSL certificate errors. For + * non-recoverable errors (such as when the server fails the client), the + * {@code WebView} calls {@link #onReceivedError(WebView, + * WebResourceRequest, WebResourceError) onReceivedError(WebView, + * WebResourceRequest, WebResourceError)} with the + * {@link #ERROR_FAILED_SSL_HANDSHAKE} argument. + * + * @param view {@code WebView} that initiated the callback. + * @param handler {@link SslErrorHandler} that handles the user's response. + * @param error SSL error object. + */ + public void onReceivedSslError(WebView view, SslErrorHandler handler, + SslError error) { + handler.cancel(); + } + + /** + * Notify the host application to handle a SSL client certificate request. The host application + * is responsible for showing the UI if desired and providing the keys. There are three ways to + * respond: {@link ClientCertRequest#proceed}, {@link ClientCertRequest#cancel}, or {@link + * ClientCertRequest#ignore}. Webview stores the response in memory (for the life of the + * application) if {@link ClientCertRequest#proceed} or {@link ClientCertRequest#cancel} is + * called and does not call {@code onReceivedClientCertRequest()} again for the same host and + * port pair. Webview does not store the response if {@link ClientCertRequest#ignore} + * is called. Note that, multiple layers in chromium network stack might be + * caching the responses, so the behavior for ignore is only a best case + * effort. + * + * This method is called on the UI thread. During the callback, the + * connection is suspended. + * + * For most use cases, the application program should implement the + * {@link android.security.KeyChainAliasCallback} interface and pass it to + * {@link android.security.KeyChain#choosePrivateKeyAlias} to start an + * activity for the user to choose the proper alias. The keychain activity will + * provide the alias through the callback method in the implemented interface. Next + * the application should create an async task to call + * {@link android.security.KeyChain#getPrivateKey} to receive the key. + * + * An example implementation of client certificates can be seen at + * + * AOSP Browser + * + * The default behavior is to cancel, returning no client certificate. + * + * @param view The WebView that is initiating the callback + * @param request An instance of a {@link ClientCertRequest} + * + */ + public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) { + request.cancel(); + } + + /** + * Notifies the host application that the WebView received an HTTP + * authentication request. The host application can use the supplied + * {@link HttpAuthHandler} to set the WebView's response to the request. + * The default behavior is to cancel the request. + * + *

Note: The supplied HttpAuthHandler must be used on + * the UI thread. + * + * @param view the WebView that is initiating the callback + * @param handler the HttpAuthHandler used to set the WebView's response + * @param host the host requiring authentication + * @param realm the realm for which authentication is required + * @see WebView#getHttpAuthUsernamePassword + */ + public void onReceivedHttpAuthRequest(WebView view, + HttpAuthHandler handler, String host, String realm) { + handler.cancel(); + } + + /** + * Give the host application a chance to handle the key event synchronously. + * e.g. menu shortcut key events need to be filtered this way. If return + * true, WebView will not handle the key event. If return {@code false}, WebView + * will always handle the key event, so none of the super in the view chain + * will see the key event. The default behavior returns {@code false}. + * + * @param view The WebView that is initiating the callback. + * @param event The key event. + * @return {@code true} if the host application wants to handle the key event + * itself, otherwise return {@code false} + */ + public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) { + return false; + } + + /** + * Notify the host application that a key was not handled by the WebView. + * Except system keys, WebView always consumes the keys in the normal flow + * or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously + * from where the key is dispatched. It gives the host application a chance + * to handle the unhandled key events. + * + * @param view The WebView that is initiating the callback. + * @param event The key event. + */ + public void onUnhandledKeyEvent(WebView view, KeyEvent event) { + onUnhandledInputEventInternal(view, event); + } + + /** + * Notify the host application that a input event was not handled by the WebView. + * Except system keys, WebView always consumes input events in the normal flow + * or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously + * from where the event is dispatched. It gives the host application a chance + * to handle the unhandled input events. + * + * Note that if the event is a {@link android.view.MotionEvent}, then it's lifetime is only + * that of the function call. If the WebViewClient wishes to use the event beyond that, then it + * must create a copy of the event. + * + * It is the responsibility of overriders of this method to call + * {@link #onUnhandledKeyEvent(WebView, KeyEvent)} + * when appropriate if they wish to continue receiving events through it. + * + * @param view The WebView that is initiating the callback. + * @param event The input event. + * @removed + */ + public void onUnhandledInputEvent(WebView view, InputEvent event) { + if (event instanceof KeyEvent) { + onUnhandledKeyEvent(view, (KeyEvent) event); + return; + } + onUnhandledInputEventInternal(view, event); + } + + private void onUnhandledInputEventInternal(WebView view, InputEvent event) { + throw new RuntimeException("Stub!"); + } + + /** + * Notify the host application that the scale applied to the WebView has + * changed. + * + * @param view The WebView that is initiating the callback. + * @param oldScale The old scale factor + * @param newScale The new scale factor + */ + public void onScaleChanged(WebView view, float oldScale, float newScale) { + } + + /** + * Notify the host application that a request to automatically log in the + * user has been processed. + * @param view The WebView requesting the login. + * @param realm The account realm used to look up accounts. + * @param account An optional account. If not {@code null}, the account should be + * checked against accounts on the device. If it is a valid + * account, it should be used to log in the user. + * @param args Authenticator specific arguments used to log in the user. + */ + public void onReceivedLoginRequest(WebView view, String realm, + @Nullable String account, String args) { + } + + /** + * Notify host application that the given WebView's render process has exited. + * + * Multiple WebView instances may be associated with a single render process; + * onRenderProcessGone will be called for each WebView that was affected. + * The application's implementation of this callback should only attempt to + * clean up the specific WebView given as a parameter, and should not assume + * that other WebView instances are affected. + * + * The given WebView can't be used, and should be removed from the view hierarchy, + * all references to it should be cleaned up, e.g any references in the Activity + * or other classes saved using {@link android.view.View#findViewById} and similar calls, etc. + * + * To cause an render process crash for test purpose, the application can + * call {@code loadUrl("chrome://crash")} on the WebView. Note that multiple WebView + * instances may be affected if they share a render process, not just the + * specific WebView which loaded chrome://crash. + * + * @param view The WebView which needs to be cleaned up. + * @param detail the reason why it exited. + * @return {@code true} if the host application handled the situation that process has + * exited, otherwise, application will crash if render process crashed, + * or be killed if render process was killed by the system. + */ + public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { + return false; + } + + /** + * Notify the host application that a loading URL has been flagged by Safe Browsing. + * + * The application must invoke the callback to indicate the preferred response. The default + * behavior is to show an interstitial to the user, with the reporting checkbox visible. + * + * If the application needs to show its own custom interstitial UI, the callback can be invoked + * asynchronously with {@link SafeBrowsingResponse#backToSafety} or {@link + * SafeBrowsingResponse#proceed}, depending on user response. + * + * @param view The WebView that hit the malicious resource. + * @param request Object containing the details of the request. + * @param threatType The reason the resource was caught by Safe Browsing, corresponding to a + * {@code SAFE_BROWSING_THREAT_*} value. + * @param callback Applications must invoke one of the callback methods. + */ + public void onSafeBrowsingHit(WebView view, WebResourceRequest request, + @SafeBrowsingThreat int threatType, SafeBrowsingResponse callback) { + callback.showInterstitial(/* allowReporting */ true); + } +} diff --git a/AndroidCompat/src/main/java/android/webkit/WebViewProvider.java b/AndroidCompat/src/main/java/android/webkit/WebViewProvider.java new file mode 100644 index 00000000..7ef66e2c --- /dev/null +++ b/AndroidCompat/src/main/java/android/webkit/WebViewProvider.java @@ -0,0 +1,541 @@ +/* + * Copyright (C) 2012 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.webkit; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.content.Intent; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Picture; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.net.http.SslCertificate; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.print.PrintDocumentAdapter; +import android.util.LongSparseArray; +import android.util.SparseArray; +import android.view.DragEvent; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.view.WindowInsets; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.textclassifier.TextClassifier; +import android.webkit.WebView.HitTestResult; +import android.webkit.WebView.PictureListener; +import android.webkit.WebView.VisualStateCallback; + + +import java.io.BufferedWriter; +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * WebView backend provider interface: this interface is the abstract backend to a WebView + * instance; each WebView object is bound to exactly one WebViewProvider object which implements + * the runtime behavior of that WebView. + * + * All methods must behave as per their namesake in {@link WebView}, unless otherwise noted. + * + * @hide Not part of the public API; only required by system implementors. + */ +@SystemApi +public interface WebViewProvider { + //------------------------------------------------------------------------- + // Main interface for backend provider of the WebView class. + //------------------------------------------------------------------------- + /** + * Initialize this WebViewProvider instance. Called after the WebView has fully constructed. + * @param javaScriptInterfaces is a Map of interface names, as keys, and + * object implementing those interfaces, as values. + * @param privateBrowsing If {@code true} the web view will be initialized in private / + * incognito mode. + */ + public void init(Map javaScriptInterfaces, + boolean privateBrowsing); + + // Deprecated - should never be called + public void setHorizontalScrollbarOverlay(boolean overlay); + + // Deprecated - should never be called + public void setVerticalScrollbarOverlay(boolean overlay); + + // Deprecated - should never be called + public boolean overlayHorizontalScrollbar(); + + // Deprecated - should never be called + public boolean overlayVerticalScrollbar(); + + public int getVisibleTitleHeight(); + + public SslCertificate getCertificate(); + + public void setCertificate(SslCertificate certificate); + + public void savePassword(String host, String username, String password); + + public void setHttpAuthUsernamePassword(String host, String realm, + String username, String password); + + public String[] getHttpAuthUsernamePassword(String host, String realm); + + /** + * See {@link WebView#destroy()}. + * As well as releasing the internal state and resources held by the implementation, + * the provider should null all references it holds on the WebView proxy class, and ensure + * no further method calls are made to it. + */ + public void destroy(); + + public void setNetworkAvailable(boolean networkUp); + + public WebBackForwardList saveState(Bundle outState); + + public boolean savePicture(Bundle b, final File dest); + + public boolean restorePicture(Bundle b, File src); + + public WebBackForwardList restoreState(Bundle inState); + + public void loadUrl(String url, Map additionalHttpHeaders); + + public void loadUrl(String url); + + public void postUrl(String url, byte[] postData); + + public void loadData(String data, String mimeType, String encoding); + + public void loadDataWithBaseURL(String baseUrl, String data, + String mimeType, String encoding, String historyUrl); + + public void evaluateJavaScript(String script, ValueCallback resultCallback); + + public void saveWebArchive(String filename); + + public void saveWebArchive(String basename, boolean autoname, ValueCallback callback); + + public void stopLoading(); + + public void reload(); + + public boolean canGoBack(); + + public void goBack(); + + public boolean canGoForward(); + + public void goForward(); + + public boolean canGoBackOrForward(int steps); + + public void goBackOrForward(int steps); + + public boolean isPrivateBrowsingEnabled(); + + public boolean pageUp(boolean top); + + public boolean pageDown(boolean bottom); + + public void insertVisualStateCallback(long requestId, VisualStateCallback callback); + + public void clearView(); + + public Picture capturePicture(); + + public PrintDocumentAdapter createPrintDocumentAdapter(String documentName); + + public float getScale(); + + public void setInitialScale(int scaleInPercent); + + public void invokeZoomPicker(); + + public HitTestResult getHitTestResult(); + + public void requestFocusNodeHref(Message hrefMsg); + + public void requestImageRef(Message msg); + + public String getUrl(); + + public String getOriginalUrl(); + + public String getTitle(); + + public Bitmap getFavicon(); + + public String getTouchIconUrl(); + + public int getProgress(); + + public int getContentHeight(); + + public int getContentWidth(); + + public void pauseTimers(); + + public void resumeTimers(); + + public void onPause(); + + public void onResume(); + + public boolean isPaused(); + + public void freeMemory(); + + public void clearCache(boolean includeDiskFiles); + + public void clearFormData(); + + public void clearHistory(); + + public void clearSslPreferences(); + + public WebBackForwardList copyBackForwardList(); + + public void setFindListener(WebView.FindListener listener); + + public void findNext(boolean forward); + + public int findAll(String find); + + public void findAllAsync(String find); + + public boolean showFindDialog(String text, boolean showIme); + + public void clearMatches(); + + public void documentHasImages(Message response); + + public void setWebViewClient(WebViewClient client); + + public WebViewClient getWebViewClient(); + + @Nullable + public WebViewRenderProcess getWebViewRenderProcess(); + + public void setWebViewRenderProcessClient( + @Nullable Executor executor, + @Nullable WebViewRenderProcessClient client); + + @Nullable + public WebViewRenderProcessClient getWebViewRenderProcessClient(); + + public void setDownloadListener(DownloadListener listener); + + public void setWebChromeClient(WebChromeClient client); + + public WebChromeClient getWebChromeClient(); + + public void setPictureListener(PictureListener listener); + + public void addJavascriptInterface(Object obj, String interfaceName); + + public void removeJavascriptInterface(String interfaceName); + + public WebMessagePort[] createWebMessageChannel(); + + public void postMessageToMainFrame(WebMessage message, Uri targetOrigin); + + public WebSettings getSettings(); + + public void setMapTrackballToArrowKeys(boolean setMap); + + public void flingScroll(int vx, int vy); + + public View getZoomControls(); + + public boolean canZoomIn(); + + public boolean canZoomOut(); + + public boolean zoomBy(float zoomFactor); + + public boolean zoomIn(); + + public boolean zoomOut(); + + public void dumpViewHierarchyWithProperties(BufferedWriter out, int level); + + public View findHierarchyView(String className, int hashCode); + + public void setRendererPriorityPolicy(int rendererRequestedPriority, boolean waivedWhenNotVisible); + + public int getRendererRequestedPriority(); + + public boolean getRendererPriorityWaivedWhenNotVisible(); + + @SuppressWarnings("unused") + public default void setTextClassifier(@Nullable TextClassifier textClassifier) {} + + @NonNull + public default TextClassifier getTextClassifier() { return TextClassifier.NO_OP; } + + //------------------------------------------------------------------------- + // Provider internal methods + //------------------------------------------------------------------------- + + /** + * @return the ViewDelegate implementation. This provides the functionality to back all of + * the name-sake functions from the View and ViewGroup base classes of WebView. + */ + /* package */ ViewDelegate getViewDelegate(); + + /** + * @return a ScrollDelegate implementation. Normally this would be same object as is + * returned by getViewDelegate(). + */ + /* package */ ScrollDelegate getScrollDelegate(); + + /** + * Only used by FindActionModeCallback to inform providers that the find dialog has + * been dismissed. + */ + public void notifyFindDialogDismissed(); + + //------------------------------------------------------------------------- + // View / ViewGroup delegation methods + //------------------------------------------------------------------------- + + /** + * Provides mechanism for the name-sake methods declared in View and ViewGroup to be delegated + * into the WebViewProvider instance. + * NOTE: For many of these methods, the WebView will provide a super.Foo() call before or after + * making the call into the provider instance. This is done for convenience in the common case + * of maintaining backward compatibility. For remaining super class calls (e.g. where the + * provider may need to only conditionally make the call based on some internal state) see the + * {@link WebView.PrivateAccess} callback class. + */ + // TODO: See if the pattern of the super-class calls can be rationalized at all, and document + // the remainder on the methods below. + interface ViewDelegate { + public boolean shouldDelayChildPressedState(); + + public void onProvideVirtualStructure(android.view.ViewStructure structure); + + default void onProvideAutofillVirtualStructure( + @SuppressWarnings("unused") android.view.ViewStructure structure, + @SuppressWarnings("unused") int flags) { + } + + default void autofill(@SuppressWarnings("unused") SparseArray values) { + } + + default boolean isVisibleToUserForAutofill(@SuppressWarnings("unused") int virtualId) { + return true; // true is the default value returned by View.isVisibleToUserForAutofill() + } + + default void onProvideContentCaptureStructure( + @NonNull @SuppressWarnings("unused") android.view.ViewStructure structure, + @SuppressWarnings("unused") int flags) { + } + + // @SuppressLint("NullableCollection") + // default void onCreateVirtualViewTranslationRequests( + // @NonNull @SuppressWarnings("unused") long[] virtualIds, + // @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats, + // @NonNull @SuppressWarnings("unused") + // Consumer requestsCollector) { + // } + + // default void onVirtualViewTranslationResponses( + // @NonNull @SuppressWarnings("unused") + // LongSparseArray response) { + // } + + // default void dispatchCreateViewTranslationRequest( + // @NonNull @SuppressWarnings("unused") Map viewIds, + // @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats, + // @Nullable @SuppressWarnings("unused") TranslationCapability capability, + // @NonNull @SuppressWarnings("unused") List requests) { + + // } + + public AccessibilityNodeProvider getAccessibilityNodeProvider(); + + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info); + + public void onInitializeAccessibilityEvent(AccessibilityEvent event); + + public boolean performAccessibilityAction(int action, Bundle arguments); + + public void setOverScrollMode(int mode); + + public void setScrollBarStyle(int style); + + public void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar, int l, int t, + int r, int b); + + public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY); + + public void onWindowVisibilityChanged(int visibility); + + public void onDraw(Canvas canvas); + + public void setLayoutParams(LayoutParams layoutParams); + + public boolean performLongClick(); + + public void onConfigurationChanged(Configuration newConfig); + + public InputConnection onCreateInputConnection(EditorInfo outAttrs); + + public boolean onDragEvent(DragEvent event); + + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event); + + public boolean onKeyDown(int keyCode, KeyEvent event); + + public boolean onKeyUp(int keyCode, KeyEvent event); + + public void onAttachedToWindow(); + + public void onDetachedFromWindow(); + + public default void onMovedToDisplay(int displayId, Configuration config) {} + + public void onVisibilityChanged(View changedView, int visibility); + + public void onWindowFocusChanged(boolean hasWindowFocus); + + public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect); + + public boolean setFrame(int left, int top, int right, int bottom); + + public void onSizeChanged(int w, int h, int ow, int oh); + + public void onScrollChanged(int l, int t, int oldl, int oldt); + + public boolean dispatchKeyEvent(KeyEvent event); + + public boolean onTouchEvent(MotionEvent ev); + + public boolean onHoverEvent(MotionEvent event); + + public boolean onGenericMotionEvent(MotionEvent event); + + public boolean onTrackballEvent(MotionEvent ev); + + public boolean requestFocus(int direction, Rect previouslyFocusedRect); + + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec); + + public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate); + + public void setBackgroundColor(int color); + + public void setLayerType(int layerType, Paint paint); + + public void preDispatchDraw(Canvas canvas); + + public void onStartTemporaryDetach(); + + public void onFinishTemporaryDetach(); + + public void onActivityResult(int requestCode, int resultCode, Intent data); + + public Handler getHandler(Handler originalHandler); + + public View findFocus(View originalFocusedView); + + @SuppressWarnings("unused") + default boolean onCheckIsTextEditor() { + return false; + } + + /** + * @see View#onApplyWindowInsets(WindowInsets). + * + *

This is the entry point for the WebView implementation to override. It returns + * {@code null} when the WebView implementation hasn't implemented the WindowInsets support + * on S yet. In this case, the {@link View#onApplyWindowInsets()} super method will be + * called instead. + * + * @param insets Insets to apply + * @return The supplied insets with any applied insets consumed. + */ + @SuppressWarnings("unused") + @Nullable + default WindowInsets onApplyWindowInsets(@Nullable WindowInsets insets) { + return null; + } + + /** + * @hide Only used by WebView. + */ + @SuppressWarnings("unused") + @Nullable + default PointerIcon onResolvePointerIcon(@NonNull MotionEvent event, int pointerIndex) { + return null; + } + } + + interface ScrollDelegate { + // These methods are declared protected in the ViewGroup base class. This interface + // exists to promote them to public so they may be called by the WebView proxy class. + // TODO: Combine into ViewDelegate? + /** + * See {@link android.webkit.WebView#computeHorizontalScrollRange} + */ + public int computeHorizontalScrollRange(); + + /** + * See {@link android.webkit.WebView#computeHorizontalScrollOffset} + */ + public int computeHorizontalScrollOffset(); + + /** + * See {@link android.webkit.WebView#computeVerticalScrollRange} + */ + public int computeVerticalScrollRange(); + + /** + * See {@link android.webkit.WebView#computeVerticalScrollOffset} + */ + public int computeVerticalScrollOffset(); + + /** + * See {@link android.webkit.WebView#computeVerticalScrollExtent} + */ + public int computeVerticalScrollExtent(); + + /** + * See {@link android.webkit.WebView#computeScroll} + */ + public void computeScroll(); + } +} diff --git a/AndroidCompat/src/main/java/android/widget/AbsoluteLayout.java b/AndroidCompat/src/main/java/android/widget/AbsoluteLayout.java new file mode 100644 index 00000000..f4b8ef65 --- /dev/null +++ b/AndroidCompat/src/main/java/android/widget/AbsoluteLayout.java @@ -0,0 +1,108 @@ +/* + * 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.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RemoteViews.RemoteView; + + +@Deprecated +@RemoteView +public class AbsoluteLayout extends ViewGroup { + public AbsoluteLayout(Context context) { + this(context, null); + } + + public AbsoluteLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AbsoluteLayout(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public AbsoluteLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + throw new RuntimeException("Stub!"); + } + + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0); + } + + @Override + protected void onLayout(boolean changed, int l, int t, + int r, int b) { + throw new RuntimeException("Stub!"); + } + + @Override + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { + return new AbsoluteLayout.LayoutParams(getContext(), attrs); + } + + // Override to allow type-checking of LayoutParams. + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof AbsoluteLayout.LayoutParams; + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + @Override + public boolean shouldDelayChildPressedState() { + return false; + } + + public static class LayoutParams extends ViewGroup.LayoutParams { + public int x; + public int y; + + public LayoutParams(int width, int height, int x, int y) { + super(width, height); + this.x = x; + this.y = y; + } + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + throw new RuntimeException("Stub!"); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + + public String debug(String output) { + return output + "Absolute.LayoutParams={width=" + + String.valueOf(width) + ", height=" + String.valueOf(height) + + " x=" + x + " y=" + y + "}"; + } + } +} diff --git a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/AndroidCompatInitializer.kt b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/AndroidCompatInitializer.kt index 3d1e446d..f6b1d1b2 100644 --- a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/AndroidCompatInitializer.kt +++ b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/AndroidCompatInitializer.kt @@ -1,8 +1,10 @@ package xyz.nulldev.androidcompat +import android.webkit.WebView import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule import xyz.nulldev.androidcompat.config.FilesConfigModule import xyz.nulldev.androidcompat.config.SystemConfigModule +import xyz.nulldev.androidcompat.webkit.KcefWebViewProvider import xyz.nulldev.ts.config.GlobalConfigManager /** @@ -17,6 +19,8 @@ class AndroidCompatInitializer { SystemConfigModule.register(GlobalConfigManager.config), ) + WebView.setProviderFactory({ view: WebView -> KcefWebViewProvider(view) }) + // Set some properties extensions use System.setProperty( "http.agent", diff --git a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/CallableArgument.kt b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/CallableArgument.kt new file mode 100644 index 00000000..57eb1da8 --- /dev/null +++ b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/CallableArgument.kt @@ -0,0 +1,5 @@ +package xyz.nulldev.androidcompat + +fun interface CallableArgument { + fun call(arg: A): R +} diff --git a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebSettings.kt b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebSettings.kt new file mode 100644 index 00000000..4f0f88eb --- /dev/null +++ b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebSettings.kt @@ -0,0 +1,429 @@ +package xyz.nulldev.androidcompat.webkit + +import android.webkit.WebSettings + +class KcefWebSettings : WebSettings() { + // Boolean settings + private var navDumps = false + private var mediaPlaybackRequiresUserGesture = true + private var builtInZoomControls = false + private var displayZoomControls = true + private var allowFileAccess = true + private var allowContentAccess = true + private var pluginsEnabled = false + private var loadWithOverviewMode = false + private var enableSmoothTransition = false + private var useWebViewBackgroundForOverscrollBackground = false + private var saveFormData = true + private var savePassword = false + private var acceptThirdPartyCookies = true + private var lightTouchEnabled = false + private var useWideViewPort = true + private var supportMultipleWindows = false + private var loadsImagesAutomatically = true + private var blockNetworkImage = false + private var blockNetworkLoads = false + private var javaScriptEnabled = true + private var allowUniversalAccessFromFileURLs = false + private var allowFileAccessFromFileURLs = false + private var domStorageEnabled = true + private var geolocationEnabled = true + private var javaScriptCanOpenWindowsAutomatically = false + private var needInitialFocus = false + private var offscreenPreRaster = false + private var videoOverlayForEmbeddedEncryptedVideoEnabled = false + private var safeBrowsingEnabled = true + + // Integer settings + private var textZoom = 100 + private var minimumFontSize = 8 + private var minimumLogicalFontSize = 8 + private var defaultFontSize = 16 + private var defaultFixedFontSize = 13 + private var cacheMode = 0 + private var mixedContentMode = 0 + private var disabledActionModeMenuItems = 0 + + // String settings + private var databasePath: String? = null + private var geolocationDatabasePath: String? = null + private var appCachePath: String? = null + private var defaultTextEncodingName: String? = null + private var userAgentString: String? = null + private var standardFontFamily: String? = null + private var fixedFontFamily: String? = null + private var sansSerifFontFamily: String? = null + private var serifFontFamily: String? = null + private var cursiveFontFamily: String? = null + private var fantasyFontFamily: String? = null + + // Enum settings + private var defaultZoom: ZoomDensity? = null + private var layoutAlgorithm: LayoutAlgorithm? = null + private var pluginState: PluginState? = null + private var renderPriority: RenderPriority? = null + + // Long settings + private var appCacheMaxSize: Long = 0L + + // Implementations + + @SuppressWarnings("HiddenAbstractMethod") + @Deprecated("inherit") + override fun setNavDump(p0: Boolean) { + navDumps = p0 + } + + @Deprecated("inherit") + @SuppressWarnings("HiddenAbstractMethod") + override fun getNavDump(): Boolean = navDumps + + override fun setSupportZoom(p0: Boolean) { + mediaPlaybackRequiresUserGesture = p0 + } + + override fun supportZoom() = mediaPlaybackRequiresUserGesture + + override fun setMediaPlaybackRequiresUserGesture(p0: Boolean) { + mediaPlaybackRequiresUserGesture = p0 + } + + override fun getMediaPlaybackRequiresUserGesture() = mediaPlaybackRequiresUserGesture + + override fun setBuiltInZoomControls(p0: Boolean) { + builtInZoomControls = p0 + } + + override fun getBuiltInZoomControls() = builtInZoomControls + + override fun setDisplayZoomControls(p0: Boolean) { + displayZoomControls = p0 + } + + override fun getDisplayZoomControls() = displayZoomControls + + override fun setAllowFileAccess(p0: Boolean) { + allowFileAccess = p0 + } + + override fun getAllowFileAccess() = allowFileAccess + + override fun setAllowContentAccess(p0: Boolean) { + allowContentAccess = p0 + } + + override fun getAllowContentAccess() = allowContentAccess + + override fun setLoadWithOverviewMode(p0: Boolean) { + loadWithOverviewMode = p0 + } + + override fun getLoadWithOverviewMode() = loadWithOverviewMode + + @Deprecated("inherit") + override fun setPluginsEnabled(p0: Boolean) { + pluginsEnabled = p0 + } + + @Deprecated("inherit") + override fun getPluginsEnabled() = pluginsEnabled + + @Deprecated("inherit") + override fun setEnableSmoothTransition(p0: Boolean) { + enableSmoothTransition = p0 + } + + @Deprecated("inherit") + override fun enableSmoothTransition() = enableSmoothTransition + + @Deprecated("inherit") + override fun setUseWebViewBackgroundForOverscrollBackground(p0: Boolean) { + useWebViewBackgroundForOverscrollBackground = p0 + } + + @Deprecated("inherit") + override fun getUseWebViewBackgroundForOverscrollBackground() = useWebViewBackgroundForOverscrollBackground + + @Deprecated("inherit") + override fun setSaveFormData(p0: Boolean) { + saveFormData = p0 + } + + @Deprecated("inherit") + override fun getSaveFormData() = saveFormData + + @Deprecated("inherit") + override fun setSavePassword(p0: Boolean) { + savePassword = p0 + } + + @Deprecated("inherit") + override fun getSavePassword() = savePassword + + override fun setTextZoom(p0: Int) { + textZoom = p0 + } + + override fun getTextZoom() = textZoom + + @Deprecated("inherit") + override fun setDefaultZoom(p0: ZoomDensity?) { + defaultZoom = p0 + } + + override fun setAcceptThirdPartyCookies(p0: Boolean) { + acceptThirdPartyCookies = p0 + } + + override fun getAcceptThirdPartyCookies() = acceptThirdPartyCookies + + @Deprecated("inherit") + override fun getDefaultZoom() = defaultZoom + + @Deprecated("inherit") + override fun setLightTouchEnabled(p0: Boolean) { + lightTouchEnabled = p0 + } + + @Deprecated("inherit") + override fun getLightTouchEnabled() = lightTouchEnabled + + @Deprecated("inherit") + override fun setUserAgent(ua: Int) = throw RuntimeException("Stub!") + + @Deprecated("inherit") + override fun getUserAgent(): Int = throw RuntimeException("Stub!") + + override fun setUseWideViewPort(p0: Boolean) { + useWideViewPort = p0 + } + + override fun getUseWideViewPort() = useWideViewPort + + override fun setSupportMultipleWindows(p0: Boolean) { + supportMultipleWindows = p0 + } + + override fun supportMultipleWindows() = supportMultipleWindows + + override fun setLayoutAlgorithm(p0: LayoutAlgorithm?) { + layoutAlgorithm = p0 + } + + override fun getLayoutAlgorithm() = layoutAlgorithm + + override fun setStandardFontFamily(p0: String?) { + standardFontFamily = p0 + } + + override fun getStandardFontFamily() = standardFontFamily + + override fun setFixedFontFamily(p0: String?) { + fixedFontFamily = p0 + } + + override fun getFixedFontFamily() = fixedFontFamily + + override fun setSansSerifFontFamily(p0: String?) { + sansSerifFontFamily = p0 + } + + override fun getSansSerifFontFamily() = sansSerifFontFamily + + override fun setSerifFontFamily(p0: String?) { + serifFontFamily = p0 + } + + override fun getSerifFontFamily() = serifFontFamily + + override fun setCursiveFontFamily(p0: String?) { + cursiveFontFamily = p0 + } + + override fun getCursiveFontFamily() = cursiveFontFamily + + override fun setFantasyFontFamily(p0: String?) { + fantasyFontFamily = p0 + } + + override fun getFantasyFontFamily() = fantasyFontFamily + + override fun setMinimumFontSize(p0: Int) { + minimumFontSize = p0 + } + + override fun getMinimumFontSize() = minimumFontSize + + override fun setMinimumLogicalFontSize(p0: Int) { + minimumLogicalFontSize = p0 + } + + override fun getMinimumLogicalFontSize() = minimumLogicalFontSize + + override fun setDefaultFontSize(p0: Int) { + defaultFontSize = p0 + } + + override fun getDefaultFontSize() = defaultFontSize + + override fun setDefaultFixedFontSize(p0: Int) { + defaultFixedFontSize = p0 + } + + override fun getDefaultFixedFontSize() = defaultFixedFontSize + + override fun setLoadsImagesAutomatically(p0: Boolean) { + loadsImagesAutomatically = p0 + } + + override fun getLoadsImagesAutomatically() = loadsImagesAutomatically + + override fun setBlockNetworkImage(p0: Boolean) { + blockNetworkImage = p0 + } + + override fun getBlockNetworkImage() = blockNetworkImage + + override fun setBlockNetworkLoads(p0: Boolean) { + blockNetworkLoads = p0 + } + + override fun getBlockNetworkLoads() = blockNetworkLoads + + override fun setJavaScriptEnabled(p0: Boolean) { + javaScriptEnabled = p0 + } + + override fun getJavaScriptEnabled() = javaScriptEnabled + + @Deprecated("inherit") + override fun setAllowUniversalAccessFromFileURLs(p0: Boolean) { + allowUniversalAccessFromFileURLs = p0 + } + + override fun getAllowUniversalAccessFromFileURLs() = allowUniversalAccessFromFileURLs + + @Deprecated("inherit") + override fun setAllowFileAccessFromFileURLs(p0: Boolean) { + allowFileAccessFromFileURLs = p0 + } + + override fun getAllowFileAccessFromFileURLs() = allowFileAccessFromFileURLs + + @Deprecated("inherit") + override fun setPluginState(p0: PluginState?) { + pluginState = p0 + } + + @Deprecated("inherit") + override fun getPluginState() = pluginState + + @Deprecated("inherit") + override fun setDatabasePath(p0: String?) { + databasePath = p0 + } + + @Deprecated("inherit") + override fun getDatabasePath() = databasePath ?: "" + + @Deprecated("inherit") + override fun setGeolocationDatabasePath(p0: String?) { + geolocationDatabasePath = p0 + } + + @Deprecated("inherit") + override fun setAppCacheEnabled(p0: Boolean) {} + + @Deprecated("inherit") + override fun setAppCachePath(p0: String?) { + appCachePath = p0 + } + + @Deprecated("inherit") + override fun setAppCacheMaxSize(p0: Long) { + appCacheMaxSize = p0 + } + + @Deprecated("inherit") + override fun setDatabaseEnabled(p0: Boolean) {} + + @Deprecated("inherit") + override fun getDatabaseEnabled() = true + + override fun setDomStorageEnabled(p0: Boolean) { + domStorageEnabled = p0 + } + + override fun getDomStorageEnabled() = domStorageEnabled + + override fun setGeolocationEnabled(p0: Boolean) { + geolocationEnabled = p0 + } + + override fun setJavaScriptCanOpenWindowsAutomatically(p0: Boolean) { + javaScriptCanOpenWindowsAutomatically = p0 + } + + override fun getJavaScriptCanOpenWindowsAutomatically() = javaScriptCanOpenWindowsAutomatically + + override fun setDefaultTextEncodingName(p0: String?) { + defaultTextEncodingName = p0 + } + + override fun getDefaultTextEncodingName() = defaultTextEncodingName ?: "" + + override fun setUserAgentString(p0: String?) { + userAgentString = p0 + } + + override fun getUserAgentString() = userAgentString ?: defaultUserAgent() + + override fun setNeedInitialFocus(p0: Boolean) { + needInitialFocus = p0 + } + + @Deprecated("inherit") + override fun setRenderPriority(p0: RenderPriority?) { + renderPriority = p0 + } + + override fun setCacheMode(p0: Int) { + cacheMode = p0 + } + + override fun getCacheMode() = cacheMode + + override fun setMixedContentMode(p0: Int) { + mixedContentMode = p0 + } + + override fun getMixedContentMode() = mixedContentMode + + override fun setOffscreenPreRaster(p0: Boolean) { + offscreenPreRaster = p0 + } + + override fun getOffscreenPreRaster() = offscreenPreRaster + + override fun setVideoOverlayForEmbeddedEncryptedVideoEnabled(p0: Boolean) { + videoOverlayForEmbeddedEncryptedVideoEnabled = p0 + } + + override fun getVideoOverlayForEmbeddedEncryptedVideoEnabled() = videoOverlayForEmbeddedEncryptedVideoEnabled + + override fun setSafeBrowsingEnabled(p0: Boolean) { + safeBrowsingEnabled = p0 + } + + override fun getSafeBrowsingEnabled() = safeBrowsingEnabled + + override fun setDisabledActionModeMenuItems(p0: Int) { + disabledActionModeMenuItems = p0 + } + + override fun getDisabledActionModeMenuItems() = disabledActionModeMenuItems + + companion object { + fun defaultUserAgent() = System.getProperty("http.agent") + } +} diff --git a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebViewProvider.kt b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebViewProvider.kt new file mode 100644 index 00000000..8e3ccb95 --- /dev/null +++ b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebViewProvider.kt @@ -0,0 +1,1049 @@ +package xyz.nulldev.androidcompat.webkit + +import android.content.Intent +import android.content.res.Configuration +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Picture +import android.graphics.Rect +import android.graphics.drawable.Drawable +import android.net.Uri +import android.net.http.SslCertificate +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.print.PrintDocumentAdapter +import android.util.Log +import android.util.SparseArray +import android.view.DragEvent +import android.view.KeyEvent +import android.view.MotionEvent +import android.view.PointerIcon +import android.view.View +import android.view.ViewGroup.LayoutParams +import android.view.WindowInsets +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityNodeInfo +import android.view.accessibility.AccessibilityNodeProvider +import android.view.autofill.AutofillValue +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputConnection +import android.view.textclassifier.TextClassifier +import android.webkit.DownloadListener +import android.webkit.RenderProcessGoneDetail +import android.webkit.ValueCallback +import android.webkit.WebBackForwardList +import android.webkit.WebChromeClient +import android.webkit.WebMessage +import android.webkit.WebMessagePort +import android.webkit.WebResourceRequest +import android.webkit.WebResourceResponse +import android.webkit.WebSettings +import android.webkit.WebView +import android.webkit.WebView.HitTestResult +import android.webkit.WebView.PictureListener +import android.webkit.WebView.VisualStateCallback +import android.webkit.WebViewClient +import android.webkit.WebViewProvider +import android.webkit.WebViewProvider.ScrollDelegate +import android.webkit.WebViewProvider.ViewDelegate +import android.webkit.WebViewRenderProcess +import android.webkit.WebViewRenderProcessClient +import dev.datlag.kcef.KCEF +import dev.datlag.kcef.KCEFBrowser +import dev.datlag.kcef.KCEFClient +import dev.datlag.kcef.KCEFResourceRequestHandler +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import org.cef.CefSettings +import org.cef.browser.CefBrowser +import org.cef.browser.CefFrame +import org.cef.browser.CefMessageRouter +import org.cef.browser.CefRendering +import org.cef.browser.CefRequestContext +import org.cef.callback.CefCallback +import org.cef.callback.CefQueryCallback +import org.cef.handler.CefDisplayHandlerAdapter +import org.cef.handler.CefLoadHandler +import org.cef.handler.CefLoadHandlerAdapter +import org.cef.handler.CefMessageRouterHandlerAdapter +import org.cef.handler.CefRequestHandler +import org.cef.handler.CefRequestHandlerAdapter +import org.cef.handler.CefResourceHandler +import org.cef.handler.CefResourceHandlerAdapter +import org.cef.handler.CefResourceRequestHandler +import org.cef.handler.CefResourceRequestHandlerAdapter +import org.cef.misc.BoolRef +import org.cef.misc.IntRef +import org.cef.misc.StringRef +import org.cef.network.CefPostData +import org.cef.network.CefPostDataElement +import org.cef.network.CefRequest +import org.cef.network.CefResponse +import java.io.BufferedWriter +import java.io.File +import java.io.IOException +import java.util.concurrent.Executor +import kotlin.collections.Map +import kotlin.reflect.KFunction +import kotlin.reflect.full.declaredMemberFunctions + +class KcefWebViewProvider( + private val view: WebView, +) : WebViewProvider { + private val settings = KcefWebSettings() + private var viewClient = WebViewClient() + private var chromeClient = WebChromeClient() + private val mappings: MutableList = mutableListOf() + + private var kcefClient: KCEFClient? = null + private var browser: KCEFBrowser? = null + + private val handler = Handler(view.webViewLooper) + + companion object { + const val TAG = "KcefWebViewProvider" + const val QUERY_FN = "__\$_suwayomiQuery" + const val QUERY_CANCEL_FN = "__\$_suwayomiQueryCancel" + } + + private class CefWebResourceRequest( + val request: CefRequest?, + val frame: CefFrame?, + val redirect: Boolean, + ) : WebResourceRequest { + override fun getUrl(): Uri = Uri.parse(request?.url) + + override fun isForMainFrame(): Boolean = frame?.isMain ?: false + + override fun isRedirect(): Boolean = redirect + + override fun hasGesture(): Boolean = false + + override fun getMethod(): String = request?.method ?: "GET" + + override fun getRequestHeaders(): Map { + val headers = mutableMapOf() + request?.getHeaderMap(headers) + return headers + } + } + + private inner class DisplayHandler : CefDisplayHandlerAdapter() { + override fun onConsoleMessage( + browser: CefBrowser, + level: CefSettings.LogSeverity, + message: String, + source: String, + line: Int, + ): Boolean { + Log.v(TAG, "$source:$line[$level]: $message") + return true + } + + override fun onAddressChange( + browser: CefBrowser, + frame: CefFrame, + url: String, + ) { + Log.d(TAG, "Navigate to $url") + } + + override fun onStatusMessage( + browser: CefBrowser, + value: String, + ) { + Log.v(TAG, "Status update: $value") + } + } + + private inner class LoadHandler : CefLoadHandlerAdapter() { + override fun onLoadEnd( + browser: CefBrowser, + frame: CefFrame, + httpStatusCode: Int, + ) { + val url = frame.url ?: "" + Log.v(TAG, "Load end $url") + handler.post { + if (httpStatusCode == 404) { + viewClient.onReceivedError( + view, + WebViewClient.ERROR_FILE_NOT_FOUND, + "Not Found", + url, + ) + } + if (httpStatusCode == 429) { + viewClient.onReceivedError( + view, + WebViewClient.ERROR_TOO_MANY_REQUESTS, + "Too Many Requests", + url, + ) + } + if (httpStatusCode >= 400) { + // TODO: create request and response + // viewClient.onReceivedHttpError(_view, ...); + } + viewClient.onPageFinished(view, url) + chromeClient.onProgressChanged(view, 100) + } + } + + override fun onLoadError( + browser: CefBrowser, + frame: CefFrame, + errorCode: CefLoadHandler.ErrorCode, + errorText: String, + failedUrl: String, + ) { + Log.w(TAG, "Load error ($failedUrl) [$errorCode]: $errorText") + // TODO: translate correctly + handler.post { + viewClient.onReceivedError(view, WebViewClient.ERROR_UNKNOWN, errorText, url) + } + } + + override fun onLoadStart( + browser: CefBrowser, + frame: CefFrame, + transitionType: CefRequest.TransitionType, + ) { + Log.v(TAG, "Load start, pushing mappings") + mappings.forEach { + val js = + """ + window.${it.interfaceName} = window.${it.interfaceName} || {} + window.${it.interfaceName}.${it.functionName} = function() { + return new Promise((resolve, reject) => { + window.${QUERY_FN}({ + request: JSON.stringify({ + functionName: ${Json.encodeToString(it.functionName)}, + interfaceName: ${Json.encodeToString(it.interfaceName)}, + args: Array.from(arguments), + }), + persistent: false, + onSuccess: resolve, + onFailure: (_, err) => reject(err), + }) + }); + } + """ + browser.executeJavaScript(js, "SUWAYOMI ${it.toNice()}", 0) + } + + handler.post { viewClient.onPageStarted(view, frame.url, null) } + } + } + + private inner class MessageRouterHandler : CefMessageRouterHandlerAdapter() { + override fun onQuery( + browser: CefBrowser, + frame: CefFrame, + queryId: Long, + request: String, + persistent: Boolean, + callback: CefQueryCallback, + ): Boolean { + val invoke = + try { + Json.decodeFromString(request) + } catch (e: Exception) { + Log.w(TAG, "Invalid request received $e") + return false + } + // TODO: Use a map + mappings + .find { + it.functionName == invoke.functionName && + it.interfaceName == invoke.interfaceName + }?.let { + handler.post { + try { + Log.v(TAG, "Received request to invoke ${it.toNice()}") + // NOTE: first argument is + // implicitly this + val retval = it.fn.call(it.obj, *invoke.args) + callback.success(retval.toString()) + } catch (e: Exception) { + Log.w(TAG, "JS-invoke on ${it.toNice()} failed: $e") + callback.failure(0, e.message) + } + } + return true + } + return false + } + } + + private inner class WebResponseResourceHandler( + val webResponse: WebResourceResponse, + ) : CefResourceHandlerAdapter() { + private var resolvedData: ByteArray? = null + private var readOffset = 0 + + override fun processRequest( + request: CefRequest, + callback: CefCallback, + ): Boolean { + Log.v(TAG, "Handling request from client's response for ${request.url}") + handler.post { + try { + resolvedData = webResponse.data.readAllBytes() + } catch (e: IOException) { + } + callback.Continue() + } + return true + } + + override fun getResponseHeaders( + response: CefResponse, + responseLength: IntRef, + redirectUrl: StringRef, + ) { + webResponse.responseHeaders.forEach { response.setHeaderByName(it.key, it.value, true) } + responseLength.set(resolvedData?.size ?: 0) + } + + override fun readResponse( + dataOut: ByteArray, + bytesToRead: Int, + bytesRead: IntRef, + callback: CefCallback, + ): Boolean { + val data = resolvedData ?: return false + val bytesToTransfer = Math.min(bytesToRead, data.size - readOffset) + Log.v( + TAG, + "readResponse: $readOffset/${data.size}, reading $bytesToRead->$bytesToTransfer", + ) + data.copyInto(dataOut, startIndex = readOffset, endIndex = readOffset + bytesToTransfer) + bytesRead.set(bytesToTransfer) + readOffset += bytesToTransfer + return readOffset < data.size + } + } + + private inner class ResourceRequestHandler : CefResourceRequestHandlerAdapter() { + override fun onBeforeResourceLoad( + browser: CefBrowser?, + frame: CefFrame?, + request: CefRequest, + ): Boolean { + request.setHeaderByName("user-agent", settings.userAgentString, true) + + // TODO: we should be calling this on the handler, since CEF calls us on its IO thread + // thus if a client tried to use WebView#loadUrl as the docs suggest, this fails + val cancel = + viewClient.shouldOverrideUrlLoading( + view, + CefWebResourceRequest(request, frame, false), + ) + Log.v(TAG, "Resource ${request?.url}, result is cancel? $cancel") + + handler.post { viewClient.onLoadResource(view, frame?.url) } + + return cancel || settings.blockNetworkLoads + } + + override fun getResourceHandler( + browser: CefBrowser, + frame: CefFrame, + request: CefRequest, + ): CefResourceHandler? { + // TODO: we should be calling this on the handler, since CEF calls us on its IO thread + val response = + viewClient.shouldInterceptRequest( + view, + CefWebResourceRequest(request, frame, false), + ) + response ?: return null + return WebResponseResourceHandler(response) + } + } + + private inner class RequestHandler : CefRequestHandlerAdapter() { + override fun getResourceRequestHandler( + browser: CefBrowser, + frame: CefFrame, + request: CefRequest, + isNavigation: Boolean, + isDownload: Boolean, + requestInitiator: String, + disableDefaultHandling: BoolRef, + ): CefResourceRequestHandler? = ResourceRequestHandler() + + override fun onRenderProcessTerminated( + browser: CefBrowser, + status: CefRequestHandler.TerminationStatus, + ) { + handler.post { + viewClient.onRenderProcessGone( + view, + object : RenderProcessGoneDetail() { + override fun didCrash(): Boolean = status == CefRequestHandler.TerminationStatus.TS_PROCESS_CRASHED + + override fun rendererPriorityAtExit(): Int = -1 + }, + ) + } + } + } + + override fun init( + javaScriptInterfaces: Map?, + privateBrowsing: Boolean, + ) { + destroy() + kcefClient = + KCEF.newClientBlocking().apply { + addDisplayHandler(DisplayHandler()) + addLoadHandler(LoadHandler()) + addRequestHandler(RequestHandler()) + + val config = CefMessageRouter.CefMessageRouterConfig() + config.jsQueryFunction = QUERY_FN + config.jsCancelFunction = QUERY_CANCEL_FN + addMessageRouter(CefMessageRouter.create(config, MessageRouterHandler())) + } + } + + // Deprecated - should never be called + override fun setHorizontalScrollbarOverlay(overlay: Boolean): Unit = throw RuntimeException("Stub!") + + // Deprecated - should never be called + override fun setVerticalScrollbarOverlay(overlay: Boolean): Unit = throw RuntimeException("Stub!") + + // Deprecated - should never be called + override fun overlayHorizontalScrollbar(): Boolean = throw RuntimeException("Stub!") + + // Deprecated - should never be called + override fun overlayVerticalScrollbar(): Boolean = throw RuntimeException("Stub!") + + override fun getVisibleTitleHeight(): Int = throw RuntimeException("Stub!") + + override fun getCertificate(): SslCertificate = throw RuntimeException("Stub!") + + override fun setCertificate(certificate: SslCertificate): Unit = throw RuntimeException("Stub!") + + override fun savePassword( + host: String, + username: String, + password: String, + ): Unit = throw RuntimeException("Stub!") + + override fun setHttpAuthUsernamePassword( + host: String, + realm: String, + username: String, + password: String, + ): Unit = throw RuntimeException("Stub!") + + override fun getHttpAuthUsernamePassword( + host: String, + realm: String, + ): Array = throw RuntimeException("Stub!") + + override fun destroy() { + browser?.close(true) + browser?.dispose() + browser = null + kcefClient?.dispose() + kcefClient = null + } + + override fun setNetworkAvailable(networkUp: Boolean): Unit = throw RuntimeException("Stub!") + + override fun saveState(outState: Bundle): WebBackForwardList = throw RuntimeException("Stub!") + + override fun savePicture( + b: Bundle, + dest: File, + ): Boolean = throw RuntimeException("Stub!") + + override fun restorePicture( + b: Bundle, + src: File, + ): Boolean = throw RuntimeException("Stub!") + + override fun restoreState(inState: Bundle): WebBackForwardList = throw RuntimeException("Stub!") + + override fun loadUrl( + loadUrl: String, + additionalHttpHeaders: Map, + ) { + browser?.close(true) + browser?.dispose() + chromeClient.onProgressChanged(view, 0) + browser = + kcefClient!! + .createBrowser( + loadUrl, + CefRendering.OFFSCREEN, + context = createContext(additionalHttpHeaders), + ).apply { + // NOTE: Without this, we don't seem to be receiving any events + createImmediately() + } + Log.d(TAG, "Page loaded at URL $loadUrl") + } + + override fun loadUrl(url: String) { + loadUrl(url, mapOf()) + } + + override fun postUrl( + url: String, + postData: ByteArray, + ) { + browser?.close(true) + browser?.dispose() + chromeClient.onProgressChanged(view, 0) + browser = + kcefClient!! + .createBrowser( + url, + CefRendering.OFFSCREEN, + context = createContext(postData = postData), + ).apply { + // NOTE: Without this, we don't seem to be receiving any events + createImmediately() + } + Log.d(TAG, "Page posted at URL $url") + } + + override fun loadData( + data: String, + mimeType: String, + encoding: String, + ) { + loadDataWithBaseURL(null, data, mimeType, encoding, null) + } + + override fun loadDataWithBaseURL( + baseUrl: String?, + data: String, + mimeType: String, + encoding: String, + historyUrl: String?, + ) { + browser?.close(true) + browser?.dispose() + chromeClient.onProgressChanged(view, 0) + browser = + kcefClient!! + .createBrowserWithHtml( + data, + baseUrl ?: KCEFBrowser.BLANK_URI, + CefRendering.OFFSCREEN, + ).apply { + // NOTE: Without this, we don't seem to be receiving any events + createImmediately() + } + Log.d(TAG, "Page loaded from data at base URL $baseUrl") + } + + override fun evaluateJavaScript( + script: String, + resultCallback: ValueCallback, + ) { + browser!!.evaluateJavaScript( + script.removePrefix("javascript:"), + { + Log.v(TAG, "JS returned: $it") + it?.let { resultCallback.onReceiveValue(it) } + }, + ) + } + + override fun saveWebArchive(filename: String): Unit = throw RuntimeException("Stub!") + + override fun saveWebArchive( + basename: String, + autoname: Boolean, + callback: ValueCallback, + ): Unit = throw RuntimeException("Stub!") + + override fun stopLoading() { + browser!!.stopLoad() + } + + override fun reload() { + browser!!.reload() + } + + override fun canGoBack(): Boolean = browser!!.canGoBack() + + override fun goBack() { + browser!!.goBack() + } + + override fun canGoForward(): Boolean = browser!!.canGoForward() + + override fun goForward() { + browser!!.goForward() + } + + override fun canGoBackOrForward(steps: Int): Boolean = throw RuntimeException("Stub!") + + override fun goBackOrForward(steps: Int): Unit = throw RuntimeException("Stub!") + + override fun isPrivateBrowsingEnabled(): Boolean = throw RuntimeException("Stub!") + + override fun pageUp(top: Boolean): Boolean = throw RuntimeException("Stub!") + + override fun pageDown(bottom: Boolean): Boolean = throw RuntimeException("Stub!") + + override fun insertVisualStateCallback( + requestId: Long, + callback: VisualStateCallback, + ): Unit = throw RuntimeException("Stub!") + + override fun clearView(): Unit = throw RuntimeException("Stub!") + + override fun capturePicture(): Picture = throw RuntimeException("Stub!") + + override fun createPrintDocumentAdapter(documentName: String): PrintDocumentAdapter = throw RuntimeException("Stub!") + + override fun getScale(): Float = throw RuntimeException("Stub!") + + override fun setInitialScale(scaleInPercent: Int): Unit = throw RuntimeException("Stub!") + + override fun invokeZoomPicker(): Unit = throw RuntimeException("Stub!") + + override fun getHitTestResult(): HitTestResult = throw RuntimeException("Stub!") + + override fun requestFocusNodeHref(hrefMsg: Message): Unit = throw RuntimeException("Stub!") + + override fun requestImageRef(msg: Message): Unit = throw RuntimeException("Stub!") + + override fun getUrl(): String = browser!!.url + + override fun getOriginalUrl(): String = browser!!.url + + override fun getTitle(): String = throw RuntimeException("Stub!") + + override fun getFavicon(): Bitmap = throw RuntimeException("Stub!") + + override fun getTouchIconUrl(): String = throw RuntimeException("Stub!") + + override fun getProgress(): Int = throw RuntimeException("Stub!") + + override fun getContentHeight(): Int = throw RuntimeException("Stub!") + + override fun getContentWidth(): Int = throw RuntimeException("Stub!") + + override fun pauseTimers(): Unit = throw RuntimeException("Stub!") + + override fun resumeTimers(): Unit = throw RuntimeException("Stub!") + + override fun onPause(): Unit = throw RuntimeException("Stub!") + + override fun onResume(): Unit = throw RuntimeException("Stub!") + + override fun isPaused(): Boolean = throw RuntimeException("Stub!") + + override fun freeMemory(): Unit = throw RuntimeException("Stub!") + + override fun clearCache(includeDiskFiles: Boolean): Unit = throw RuntimeException("Stub!") + + override fun clearFormData(): Unit = throw RuntimeException("Stub!") + + override fun clearHistory(): Unit = throw RuntimeException("Stub!") + + override fun clearSslPreferences(): Unit = throw RuntimeException("Stub!") + + override fun copyBackForwardList(): WebBackForwardList = throw RuntimeException("Stub!") + + override fun setFindListener(listener: WebView.FindListener): Unit = throw RuntimeException("Stub!") + + override fun findNext(forward: Boolean): Unit = throw RuntimeException("Stub!") + + override fun findAll(find: String): Int = throw RuntimeException("Stub!") + + override fun findAllAsync(find: String): Unit = throw RuntimeException("Stub!") + + override fun showFindDialog( + text: String, + showIme: Boolean, + ): Boolean = throw RuntimeException("Stub!") + + override fun clearMatches(): Unit = throw RuntimeException("Stub!") + + override fun documentHasImages(response: Message): Unit = throw RuntimeException("Stub!") + + override fun setWebViewClient(client: WebViewClient) { + viewClient = client + } + + override fun getWebViewClient(): WebViewClient = viewClient + + override fun getWebViewRenderProcess(): WebViewRenderProcess? = throw RuntimeException("Stub!") + + override fun setWebViewRenderProcessClient( + executor: Executor?, + client: WebViewRenderProcessClient?, + ): Unit = throw RuntimeException("Stub!") + + override fun getWebViewRenderProcessClient(): WebViewRenderProcessClient? = throw RuntimeException("Stub!") + + override fun setDownloadListener(listener: DownloadListener): Unit = throw RuntimeException("Stub!") + + override fun setWebChromeClient(client: WebChromeClient) { + chromeClient = client + } + + override fun getWebChromeClient(): WebChromeClient = chromeClient + + override fun setPictureListener(listener: PictureListener): Unit = throw RuntimeException("Stub!") + + @Serializable + private data class FunctionCall( + val interfaceName: String, + val functionName: String, + val args: Array, + ) + + private data class FunctionMapping( + val interfaceName: String, + val functionName: String, + val obj: Any, + val fn: KFunction<*>, + ) { + fun toNice(): String = "$interfaceName.$functionName" + } + + override fun addJavascriptInterface( + obj: Any, + interfaceName: String, + ) { + val cls = obj::class + mappings.addAll( + cls.declaredMemberFunctions.map { + val map = FunctionMapping(interfaceName, it.name, obj, it) + Log.v(TAG, "Exposing: " + map.toNice()) + map + }, + ) + } + + override fun removeJavascriptInterface(interfaceName: String): Unit = throw RuntimeException("Stub!") + + override fun createWebMessageChannel(): Array = throw RuntimeException("Stub!") + + override fun postMessageToMainFrame( + message: WebMessage, + targetOrigin: Uri, + ): Unit = throw RuntimeException("Stub!") + + override fun getSettings(): WebSettings = settings + + override fun setMapTrackballToArrowKeys(setMap: Boolean): Unit = throw RuntimeException("Stub!") + + override fun flingScroll( + vx: Int, + vy: Int, + ): Unit = throw RuntimeException("Stub!") + + override fun getZoomControls(): View = throw RuntimeException("Stub!") + + override fun canZoomIn(): Boolean = throw RuntimeException("Stub!") + + override fun canZoomOut(): Boolean = throw RuntimeException("Stub!") + + override fun zoomBy(zoomFactor: Float): Boolean = throw RuntimeException("Stub!") + + override fun zoomIn(): Boolean = throw RuntimeException("Stub!") + + override fun zoomOut(): Boolean = throw RuntimeException("Stub!") + + override fun dumpViewHierarchyWithProperties( + out: BufferedWriter, + level: Int, + ): Unit = throw RuntimeException("Stub!") + + override fun findHierarchyView( + className: String, + hashCode: Int, + ): View = throw RuntimeException("Stub!") + + override fun setRendererPriorityPolicy( + rendererRequestedPriority: Int, + waivedWhenNotVisible: Boolean, + ): Unit = throw RuntimeException("Stub!") + + override fun getRendererRequestedPriority(): Int = throw RuntimeException("Stub!") + + override fun getRendererPriorityWaivedWhenNotVisible(): Boolean = throw RuntimeException("Stub!") + + @SuppressWarnings("unused") + override fun setTextClassifier(textClassifier: TextClassifier?) {} + + override fun getTextClassifier(): TextClassifier = TextClassifier.NO_OP + + // ------------------------------------------------------------------------- + // Provider internal methods + // ------------------------------------------------------------------------- + + override fun getViewDelegate(): ViewDelegate = throw RuntimeException("Stub!") + + override fun getScrollDelegate(): ScrollDelegate = throw RuntimeException("Stub!") + + override fun notifyFindDialogDismissed(): Unit = throw RuntimeException("Stub!") + + // ------------------------------------------------------------------------- + // View / ViewGroup delegation methods + // ------------------------------------------------------------------------- + + class KcefViewDelegate : ViewDelegate { + override fun shouldDelayChildPressedState(): Boolean = throw RuntimeException("Stub!") + + override fun onProvideVirtualStructure(structure: android.view.ViewStructure): Unit = throw RuntimeException("Stub!") + + override fun onProvideAutofillVirtualStructure( + @SuppressWarnings("unused") structure: android.view.ViewStructure, + @SuppressWarnings("unused") flags: Int, + ) {} + + override fun autofill( + @SuppressWarnings("unused") values: SparseArray, + ) {} + + override fun isVisibleToUserForAutofill( + @SuppressWarnings("unused") virtualId: Int, + ): Boolean { + return true // true is the default value returned by View.isVisibleToUserForAutofill() + } + + override fun onProvideContentCaptureStructure( + @SuppressWarnings("unused") structure: android.view.ViewStructure, + @SuppressWarnings("unused") flags: Int, + ) {} + + override fun getAccessibilityNodeProvider(): AccessibilityNodeProvider = throw RuntimeException("Stub!") + + override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo): Unit = throw RuntimeException("Stub!") + + override fun onInitializeAccessibilityEvent(event: AccessibilityEvent): Unit = throw RuntimeException("Stub!") + + override fun performAccessibilityAction( + action: Int, + arguments: Bundle, + ): Boolean = throw RuntimeException("Stub!") + + override fun setOverScrollMode(mode: Int): Unit = throw RuntimeException("Stub!") + + override fun setScrollBarStyle(style: Int): Unit = throw RuntimeException("Stub!") + + override fun onDrawVerticalScrollBar( + canvas: Canvas, + scrollBar: Drawable, + l: Int, + t: Int, + r: Int, + b: Int, + ): Unit = throw RuntimeException("Stub!") + + override fun onOverScrolled( + scrollX: Int, + scrollY: Int, + clampedX: Boolean, + clampedY: Boolean, + ): Unit = throw RuntimeException("Stub!") + + override fun onWindowVisibilityChanged(visibility: Int): Unit = throw RuntimeException("Stub!") + + override fun onDraw(canvas: Canvas): Unit = throw RuntimeException("Stub!") + + override fun setLayoutParams(layoutParams: LayoutParams): Unit = throw RuntimeException("Stub!") + + override fun performLongClick(): Boolean = throw RuntimeException("Stub!") + + override fun onConfigurationChanged(newConfig: Configuration): Unit = throw RuntimeException("Stub!") + + override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection = throw RuntimeException("Stub!") + + override fun onDragEvent(event: DragEvent): Boolean = throw RuntimeException("Stub!") + + override fun onKeyMultiple( + keyCode: Int, + repeatCount: Int, + event: KeyEvent, + ): Boolean = throw RuntimeException("Stub!") + + override fun onKeyDown( + keyCode: Int, + event: KeyEvent, + ): Boolean = throw RuntimeException("Stub!") + + override fun onKeyUp( + keyCode: Int, + event: KeyEvent, + ): Boolean = throw RuntimeException("Stub!") + + override fun onAttachedToWindow(): Unit = throw RuntimeException("Stub!") + + override fun onDetachedFromWindow(): Unit = throw RuntimeException("Stub!") + + override fun onMovedToDisplay( + displayId: Int, + config: Configuration, + ) {} + + override fun onVisibilityChanged( + changedView: View, + visibility: Int, + ): Unit = throw RuntimeException("Stub!") + + override fun onWindowFocusChanged(hasWindowFocus: Boolean): Unit = throw RuntimeException("Stub!") + + override fun onFocusChanged( + focused: Boolean, + direction: Int, + previouslyFocusedRect: Rect, + ): Unit = throw RuntimeException("Stub!") + + override fun setFrame( + left: Int, + top: Int, + right: Int, + bottom: Int, + ): Boolean = throw RuntimeException("Stub!") + + override fun onSizeChanged( + w: Int, + h: Int, + ow: Int, + oh: Int, + ): Unit = throw RuntimeException("Stub!") + + override fun onScrollChanged( + l: Int, + t: Int, + oldl: Int, + oldt: Int, + ): Unit = throw RuntimeException("Stub!") + + override fun dispatchKeyEvent(event: KeyEvent): Boolean = throw RuntimeException("Stub!") + + override fun onTouchEvent(ev: MotionEvent): Boolean = throw RuntimeException("Stub!") + + override fun onHoverEvent(event: MotionEvent): Boolean = throw RuntimeException("Stub!") + + override fun onGenericMotionEvent(event: MotionEvent): Boolean = throw RuntimeException("Stub!") + + override fun onTrackballEvent(ev: MotionEvent): Boolean = throw RuntimeException("Stub!") + + override fun requestFocus( + direction: Int, + previouslyFocusedRect: Rect, + ): Boolean = throw RuntimeException("Stub!") + + override fun onMeasure( + widthMeasureSpec: Int, + heightMeasureSpec: Int, + ): Unit = throw RuntimeException("Stub!") + + override fun requestChildRectangleOnScreen( + child: View, + rect: Rect, + immediate: Boolean, + ): Boolean = throw RuntimeException("Stub!") + + override fun setBackgroundColor(color: Int): Unit = throw RuntimeException("Stub!") + + override fun setLayerType( + layerType: Int, + paint: Paint, + ) { + // ignore + } + + override fun preDispatchDraw(canvas: Canvas): Unit = throw RuntimeException("Stub!") + + override fun onStartTemporaryDetach(): Unit = throw RuntimeException("Stub!") + + override fun onFinishTemporaryDetach(): Unit = throw RuntimeException("Stub!") + + override fun onActivityResult( + requestCode: Int, + resultCode: Int, + data: Intent, + ): Unit = throw RuntimeException("Stub!") + + override fun getHandler(originalHandler: Handler): Handler = throw RuntimeException("Stub!") + + override fun findFocus(originalFocusedView: View): View = throw RuntimeException("Stub!") + + @SuppressWarnings("unused") + override fun onCheckIsTextEditor(): Boolean = false + + @SuppressWarnings("unused") + override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets? = null + + @SuppressWarnings("unused") + override fun onResolvePointerIcon( + event: MotionEvent, + pointerIndex: Int, + ): PointerIcon? = null + } + + class KcefScrollDelegate : ScrollDelegate { + override fun computeHorizontalScrollRange(): Int = throw RuntimeException("Stub!") + + override fun computeHorizontalScrollOffset(): Int = throw RuntimeException("Stub!") + + override fun computeVerticalScrollRange(): Int = throw RuntimeException("Stub!") + + override fun computeVerticalScrollOffset(): Int = throw RuntimeException("Stub!") + + override fun computeVerticalScrollExtent(): Int = throw RuntimeException("Stub!") + + override fun computeScroll(): Unit = throw RuntimeException("Stub!") + } + + private fun createContext( + additionalHttpHeaders: Map? = null, + postData: ByteArray? = null, + ): CefRequestContext = + CefRequestContext.createContext { + browser, + frame, + request, + isNavigation, + isDownload, + requestInitiator, + disableDefaultHandling, + -> + KCEFResourceRequestHandler.globalHandler( + browser, + frame, + request.apply { + if (!additionalHttpHeaders.isNullOrEmpty()) { + additionalHttpHeaders.forEach { + setHeaderByName(it.key, it.value, true) + } + } + + if (postData != null) { + this.postData = + CefPostData.create().apply { + addElement( + CefPostDataElement.create().apply { + setToBytes(postData.size, postData) + }, + ) + } + } + }, + isNavigation, + isDownload, + requestInitiator, + disableDefaultHandling, + ) + } +} diff --git a/README.md b/README.md index 01cf2f59..2c16f615 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,25 @@ Download the latest `linux-x64`(x86_64) release from [the releases section](http `tar xvf` the downloaded file and double-click on one of the launcher scripts or run them using the terminal. +#### WebView support + +WebView support is implemented via [KCEF](https://github.com/DATL4G/KCEF). +This is optional, and is only necessary to support some extensions. + +To have a functional WebView, several dependencies are required; aside from X11 libraries necessary for rendering Chromium, some JNI bindings are necessary: gluegen and jogl (found in Ubuntu as `libgluegen2-jni` and `libjogl2-jni`). +Note that on some systems (e.g. Ubuntu), the JNI libraries are not automatically found, see below. + +A KCEF server is launched on startup, which loads the X11 libraries. +If those are missing, you should see "Could not load 'jcef' library". +If so, use `ldd ~/.local/share/Tachidesk/bin/kcef/libjcef.so | grep not` to figure out which libraries are not found on your system. + +The JNI bindings are only loaded when a browser is actually launched. +This is done by extensions that rely on WebView, not by Suwayomi itself. +If there is a problem loading the JNI libraries, you should see a message indicating the library and the search path. +This search path includes the current working directory, if you do not want to modify system directories. + +Refer to the [Dockerfile](https://github.com/Suwayomi/Suwayomi-Server-docker/blob/main/Dockerfile) for more details. + ## Other methods of getting Suwayomi ### Docker Check our Official Docker release [Suwayomi Container](https://github.com/orgs/Suwayomi/packages/container/package/tachidesk) for running Suwayomi Server in a docker container. Source code for our container is available at [docker-tachidesk](https://github.com/Suwayomi/docker-tachidesk), an example compose file can also be found there. By default, the server will be running on http://localhost:4567 open this url in your browser. diff --git a/build.gradle.kts b/build.gradle.kts index fabe5f12..932a3914 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,6 +23,7 @@ allprojects { google() maven("https://github.com/Suwayomi/Suwayomi-Server/raw/android-jar/") maven("https://jitpack.io") + maven("https://jogamp.org/deployment/maven") } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 604bfc27..dde6bb9e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -146,6 +146,9 @@ cron4j = "it.sauronsoftware.cron4j:cron4j:2.2.5" # cron-utils cronUtils = "com.cronutils:cron-utils:9.2.1" +# Webview +kcef = "dev.datlag:kcef:2024.04.20.4" + # lint - used for renovate to update ktlint version ktlint = { module = "com.pinterest.ktlint:ktlint-cli", version.ref = "ktlint" } @@ -194,7 +197,8 @@ shared = [ "dex2jar-translator", "dex2jar-tools", "apk-parser", - "jackson-annotations" + "jackson-annotations", + "kcef" ] sharedTest = [ @@ -244,4 +248,4 @@ twelvemonkeys = [ "twelvemonkeys-imageio-metadata", "twelvemonkeys-imageio-jpeg", "twelvemonkeys-imageio-webp", -] \ No newline at end of file +] diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt index 6dab5215..3a6c7255 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -16,6 +16,7 @@ import io.github.oshai.kotlinlogging.KotlinLogging import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -54,6 +55,7 @@ class NetworkHelper( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", ) + val userAgentFlow = userAgent.asStateFlow() fun defaultUserAgentProvider(): String = userAgent.value diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt index 6b4cc021..28bb7163 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt @@ -117,6 +117,14 @@ class ServerConfig( // extensions val extensionRepos: MutableStateFlow> by OverrideConfigValues(StringConfigAdapter) + // playwright webview + val playwrightBrowser: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) + val playwrightWsEndpoint: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) + val playwrightSandbox: MutableStateFlow by OverrideConfigValue(BooleanConfigAdapter) + + // webview + val webviewImpl: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) + // requests val maxSourcesInParallel: MutableStateFlow by OverrideConfigValue(IntConfigAdapter) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt index 81a8d0e7..dee78a99 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt @@ -7,8 +7,10 @@ package suwayomi.tachidesk.server * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import android.os.Looper import ch.qos.logback.classic.Level import com.typesafe.config.ConfigRenderOptions +import dev.datlag.kcef.KCEF import eu.kanade.tachiyomi.App import eu.kanade.tachiyomi.createAppModule import eu.kanade.tachiyomi.network.NetworkHelper @@ -16,9 +18,14 @@ import eu.kanade.tachiyomi.source.local.LocalSource import io.github.oshai.kotlinlogging.KotlinLogging import io.javalin.json.JavalinJackson import io.javalin.json.JsonMapper +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import org.bouncycastle.jce.provider.BouncyCastleProvider import org.koin.core.context.startKoin import org.koin.core.module.Module @@ -50,6 +57,10 @@ import java.net.Authenticator import java.net.PasswordAuthentication import java.security.Security import java.util.Locale +import kotlin.io.path.Path +import kotlin.io.path.createDirectories +import kotlin.io.path.div +import kotlin.math.roundToInt private val logger = KotlinLogging.logger {} @@ -70,6 +81,15 @@ class ApplicationDirs( val mangaDownloadsRoot get() = "$downloadsRoot/mangas" } +@Suppress("DEPRECATION") +class LooperThread : Thread() { + override fun run() { + logger.info { "Starting Android Main Loop" } + Looper.prepareMainLooper() + Looper.loop() + } +} + data class ProxySettings( val proxyEnabled: Boolean, val socksProxyVersion: Int, @@ -100,11 +120,15 @@ fun serverModule(applicationDirs: ApplicationDirs): Module = single { JavalinJackson() } } +@OptIn(DelicateCoroutinesApi::class) fun applicationSetup() { Thread.setDefaultUncaughtExceptionHandler { _, throwable -> KotlinLogging.logger { }.error(throwable) { "unhandled exception" } } + val mainLoop = LooperThread() + mainLoop.start() + // register Tachidesk's config which is dubbed "ServerConfig" GlobalConfigManager.registerModule( ServerConfig.register { GlobalConfigManager.config }, @@ -187,7 +211,12 @@ fun applicationSetup() { androidCompat.startApp(app) // Initialize NetworkHelper early - Injekt.get() + Injekt + .get() + .userAgentFlow + .onEach { + System.setProperty("http.agent", it) + }.launchIn(GlobalScope) // create or update conf file if doesn't exist try { @@ -312,4 +341,29 @@ fun applicationSetup() { // start DownloadManager and restore + resume downloads DownloadManager.restoreAndResumeDownloads() + + GlobalScope.launch { + val logger = KotlinLogging.logger("KCEF") + KCEF.init( + builder = { + progress { + var lastNum = -1 + onDownloading { + val num = it.roundToInt() + if (num > lastNum) { + lastNum = num + logger.info { "KCEF download progress: $num%" } + } + } + } + download { github() } + val kcefDir = Path(applicationDirs.dataRoot) / "bin/kcef" + kcefDir.createDirectories() + installDir(kcefDir.toFile()) + }, + onError = { + it?.printStackTrace() + }, + ) + } }