mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-10 06:42:07 +01:00
Implement WebView via Playwright (#1434)
* Implement Android's Looper Looper handles thread messaging. This is used by extensions when they want to enqueue actions e.g. for sleeping while WebView does someting * Stub WebView * Continue stubbing ViewGroup for WebView * Implement WebView via Playwright * Lint * Implement request interception Supports Yidan * Support WebChromeClient For Bokugen * Fix onPageStarted * Make Playwright configurable * Subscribe to config changes * Fix exposing of functions * Support data urls * Looper: Fix infinite sleep * Looper: Avoid killing the loop on exception Just log it and continue * Pump playwright's message queue periodically https://playwright.dev/java/docs/multithreading#pagewaitfortimeout-vs-threadsleep * Update server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Stub a KCef WebViewProvider * Initial Kcef Webview implementation Still buggy, on the second call it just seems to fall over * Format, restructure to create browser on load This is much more consistent, before we would sometimes see errors from about:blank, which block the actual page * Implement some small useful properties * Move inline objects to class * Handle requests in Kcef * Move Playwright implementation * Document Playwright settings, fix deprecated warnings * Inject default user agent from NetworkHelper * Move playwright to libs.versions.toml * Lint * Fix missing imports after lint * Update server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Fix default user agent set/get Use System.getProperty instead of SystemProperties.get * Configurable WebView provider implementation * Simplify Playwright settings init * Minor cleanup and improvements * Remove playwright WebView impl * Document WebView for Linux --------- Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
This commit is contained in:
1043
AndroidCompat/src/main/java/android/os/Handler.java
Normal file
1043
AndroidCompat/src/main/java/android/os/Handler.java
Normal file
File diff suppressed because it is too large
Load Diff
25
AndroidCompat/src/main/java/android/os/IMessenger.java
Normal file
25
AndroidCompat/src/main/java/android/os/IMessenger.java
Normal file
@@ -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);
|
||||
}
|
||||
575
AndroidCompat/src/main/java/android/os/Looper.java
Normal file
575
AndroidCompat/src/main/java/android/os/Looper.java
Normal file
@@ -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.
|
||||
*
|
||||
* <p>Most interaction with a message loop is through the
|
||||
* {@link Handler} class.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <pre>
|
||||
* 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();
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
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<Looper> sThreadLocal = new ThreadLocal<Looper>();
|
||||
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 <var>printer</var>
|
||||
* 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.
|
||||
* <p>
|
||||
* Causes the {@link #loop} method to terminate without processing any
|
||||
* more messages in the message queue.
|
||||
* </p><p>
|
||||
* 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.
|
||||
* </p><p class="note">
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @see #quitSafely
|
||||
*/
|
||||
public void quit() {
|
||||
mQueue.quit(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quits the looper safely.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p><p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* <p> 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);
|
||||
}
|
||||
}
|
||||
630
AndroidCompat/src/main/java/android/os/Message.java
Normal file
630
AndroidCompat/src/main/java/android/os/Message.java
Normal file
@@ -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.
|
||||
*
|
||||
* <p class="note">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.</p>
|
||||
*/
|
||||
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}.
|
||||
*
|
||||
* <p>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 <em>target</em> member on the Message returned.
|
||||
* @param h Handler to assign to the returned Message object's <em>target</em> 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 <em>target</em> 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 <em>target</em> and
|
||||
* <em>what</em> members on the Message.
|
||||
* @param h Value to assign to the <em>target</em> member.
|
||||
* @param what Value to assign to the <em>what</em> 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 <em>target</em>, <em>what</em>, and <em>obj</em>
|
||||
* members.
|
||||
* @param h The <em>target</em> value to set.
|
||||
* @param what The <em>what</em> value to set.
|
||||
* @param obj The <em>object</em> 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 <em>target</em>, <em>what</em>,
|
||||
* <em>arg1</em>, and <em>arg2</em> members.
|
||||
*
|
||||
* @param h The <em>target</em> value to set.
|
||||
* @param what The <em>what</em> value to set.
|
||||
* @param arg1 The <em>arg1</em> value to set.
|
||||
* @param arg2 The <em>arg2</em> 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 <em>target</em>, <em>what</em>,
|
||||
* <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
|
||||
*
|
||||
* @param h The <em>target</em> value to set.
|
||||
* @param what The <em>what</em> value to set.
|
||||
* @param arg1 The <em>arg1</em> value to set.
|
||||
* @param arg2 The <em>arg2</em> value to set.
|
||||
* @param obj The <em>obj</em> 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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 <em>target</em> {@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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p><p>
|
||||
* 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.
|
||||
* </p><p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @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<Message> CREATOR
|
||||
= new Parcelable.Creator<Message>() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
1700
AndroidCompat/src/main/java/android/os/MessageQueue.java
Normal file
1700
AndroidCompat/src/main/java/android/os/MessageQueue.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
*
|
||||
* <p>ThreadLocalWorkSource is automatically updated inside system server for incoming/outgoing
|
||||
* binder calls and messages posted to handler threads.
|
||||
*
|
||||
* <p>ThreadLocalWorkSource can also be set manually if needed to refine the WorkSource.
|
||||
*
|
||||
* <p>Example:
|
||||
* <ul>
|
||||
* <li>Bluetooth process calls {@link PowerManager#isInteractive()} API on behalf of app foo.
|
||||
* <li>ThreadLocalWorkSource will be automatically set to the UID of foo.
|
||||
* <li>Any code on the thread handling {@link PowerManagerService#isInteractive()} can call
|
||||
* {@link ThreadLocalWorkSource#getUid()} to blame any resource used to handle this call.
|
||||
* <li>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.
|
||||
* </ul>
|
||||
*
|
||||
* @hide Only for use within system server.
|
||||
*/
|
||||
public final class ThreadLocalWorkSource {
|
||||
public static final int UID_NONE = Message.UID_NONE;
|
||||
private static final ThreadLocal<int []> sWorkSourceUid =
|
||||
ThreadLocal.withInitial(() -> new int[] {UID_NONE});
|
||||
|
||||
/**
|
||||
* Returns the UID to blame for the code currently executed on this thread.
|
||||
*
|
||||
* <p>This UID is set automatically by common frameworks (e.g. Binder and Handler frameworks)
|
||||
* and automatically propagated inside system server.
|
||||
* <p>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.
|
||||
*
|
||||
* <p>Inside system server, this UID will be automatically propagated.
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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:
|
||||
*
|
||||
* <pre>
|
||||
* ThreadLocalWorkSource.setUid(Process.myUid());
|
||||
* </pre>
|
||||
*
|
||||
* @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() {
|
||||
}
|
||||
}
|
||||
@@ -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<T> {
|
||||
|
||||
private static final int INITIAL_ID = 1;
|
||||
|
||||
private final String name;
|
||||
private final boolean debug;
|
||||
private final HashMap<Long, T> nativeObjToIdMap = new HashMap<Long, T>();
|
||||
private final Map<Long, DebugInfo> idToDebugInfoMap;
|
||||
|
||||
private long nextId = INITIAL_ID;
|
||||
|
||||
public NativeObjRegistry(Class<T> theClass) {
|
||||
this(theClass, false);
|
||||
}
|
||||
|
||||
public NativeObjRegistry(Class<T> 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<Long, T> 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<Trace> unregistrationTraces = new ArrayList<>();
|
||||
|
||||
public DebugInfo(Trace trace) {
|
||||
registrationTrace = trace;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Trace extends Throwable {
|
||||
|
||||
private Trace() {}
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
*
|
||||
* <p>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<ShadowPausedMessageQueue> nativeQueueRegistry =
|
||||
new NativeObjRegistry<ShadowPausedMessageQueue>(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;
|
||||
}
|
||||
}
|
||||
|
||||
270
AndroidCompat/src/main/java/android/util/Slog.java
Normal file
270
AndroidCompat/src/main/java/android/util/Slog.java
Normal file
@@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
371
AndroidCompat/src/main/java/android/util/TimeUtils.java
Normal file
371
AndroidCompat/src/main/java/android/util/TimeUtils.java
Normal file
@@ -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.
|
||||
*
|
||||
* <p>What is notUsedAfter?</p>
|
||||
* 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.
|
||||
*
|
||||
* <p>Why hardcoded?</p>
|
||||
* 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(")");
|
||||
}
|
||||
}}
|
||||
@@ -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();
|
||||
}
|
||||
5748
AndroidCompat/src/main/java/android/view/View.java
Normal file
5748
AndroidCompat/src/main/java/android/view/View.java
Normal file
File diff suppressed because it is too large
Load Diff
1787
AndroidCompat/src/main/java/android/view/ViewGroup.java
Normal file
1787
AndroidCompat/src/main/java/android/view/ViewGroup.java
Normal file
File diff suppressed because it is too large
Load Diff
205
AndroidCompat/src/main/java/android/webkit/WebChromeClient.java
Normal file
205
AndroidCompat/src/main/java/android/webkit/WebChromeClient.java
Normal file
@@ -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<String[]> callback) {
|
||||
}
|
||||
|
||||
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> 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<Uri> uploadFile, String acceptType, String capture) {
|
||||
uploadFile.onReceiveValue(null);
|
||||
}
|
||||
}
|
||||
1757
AndroidCompat/src/main/java/android/webkit/WebSettings.java
Normal file
1757
AndroidCompat/src/main/java/android/webkit/WebSettings.java
Normal file
File diff suppressed because it is too large
Load Diff
1368
AndroidCompat/src/main/java/android/webkit/WebView.java
Normal file
1368
AndroidCompat/src/main/java/android/webkit/WebView.java
Normal file
File diff suppressed because it is too large
Load Diff
609
AndroidCompat/src/main/java/android/webkit/WebViewClient.java
Normal file
609
AndroidCompat/src/main/java/android/webkit/WebViewClient.java
Normal file
@@ -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.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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)}.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> This method is not called for POST requests.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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.
|
||||
*
|
||||
* <p>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).
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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)}.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> This method is not called for POST requests.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>For more fine-grained notification of visual state updates, see {@link
|
||||
* WebView#postVisualStateCallback}.
|
||||
*
|
||||
* <p>Please note that all the conditions and recommendations applicable to
|
||||
* {@link WebView#postVisualStateCallback} also apply to this API.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>In the case of redirects, this is only called for the initial resource URL, not any
|
||||
* subsequent redirect URLs.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>In the case of redirects, this is only called for the initial resource URL, not any
|
||||
* subsequent redirect URLs.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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.
|
||||
*
|
||||
* <p>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()}.
|
||||
*
|
||||
* <p class="warning"><b>Warning:</b> 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.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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
|
||||
* <A href="https://android.googlesource.com/platform/packages/apps/Browser/+/android-5.1.1_r1/src/com/android/browser/Tab.java">
|
||||
* AOSP Browser</a>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* <p class="note"><b>Note:</b> 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
|
||||
* <i>must</i> 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);
|
||||
}
|
||||
}
|
||||
541
AndroidCompat/src/main/java/android/webkit/WebViewProvider.java
Normal file
541
AndroidCompat/src/main/java/android/webkit/WebViewProvider.java
Normal file
@@ -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<String, Object> 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<String, String> 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<String> resultCallback);
|
||||
|
||||
public void saveWebArchive(String filename);
|
||||
|
||||
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> 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<AutofillValue> 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<ViewTranslationRequest> requestsCollector) {
|
||||
// }
|
||||
|
||||
// default void onVirtualViewTranslationResponses(
|
||||
// @NonNull @SuppressWarnings("unused")
|
||||
// LongSparseArray<ViewTranslationResponse> response) {
|
||||
// }
|
||||
|
||||
// default void dispatchCreateViewTranslationRequest(
|
||||
// @NonNull @SuppressWarnings("unused") Map<AutofillId, long[]> viewIds,
|
||||
// @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats,
|
||||
// @Nullable @SuppressWarnings("unused") TranslationCapability capability,
|
||||
// @NonNull @SuppressWarnings("unused") List<ViewTranslationRequest> 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).
|
||||
*
|
||||
* <p>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();
|
||||
}
|
||||
}
|
||||
108
AndroidCompat/src/main/java/android/widget/AbsoluteLayout.java
Normal file
108
AndroidCompat/src/main/java/android/widget/AbsoluteLayout.java
Normal file
@@ -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 + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package xyz.nulldev.androidcompat
|
||||
|
||||
fun interface CallableArgument<A, R> {
|
||||
fun call(arg: A): R
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
19
README.md
19
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.
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -117,6 +117,14 @@ class ServerConfig(
|
||||
// extensions
|
||||
val extensionRepos: MutableStateFlow<List<String>> by OverrideConfigValues(StringConfigAdapter)
|
||||
|
||||
// playwright webview
|
||||
val playwrightBrowser: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
|
||||
val playwrightWsEndpoint: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
|
||||
val playwrightSandbox: MutableStateFlow<Boolean> by OverrideConfigValue(BooleanConfigAdapter)
|
||||
|
||||
// webview
|
||||
val webviewImpl: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
|
||||
|
||||
// requests
|
||||
val maxSourcesInParallel: MutableStateFlow<Int> by OverrideConfigValue(IntConfigAdapter)
|
||||
|
||||
|
||||
@@ -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<JsonMapper> { 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<NetworkHelper>()
|
||||
Injekt
|
||||
.get<NetworkHelper>()
|
||||
.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()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user