From f5fc993484cff1540bb1c2826ce8081e3b4d0242 Mon Sep 17 00:00:00 2001 From: Julian Winkler Date: Tue, 12 Sep 2023 23:18:47 +0200 Subject: [PATCH] add APIs needed for non legacy NewPipe version --- .../android/animation/AnimatorInflater.java | 4 + .../android/animation/ObjectAnimator.java | 6 + .../android/animation/StateListAnimator.java | 6 + .../android/animation/ValueAnimator.java | 2 + src/api-impl/android/app/ActivityManager.java | 10 + src/api-impl/android/app/AppOpsManager.java | 4 + src/api-impl/android/app/Application.java | 5 +- src/api-impl/android/app/Notification.java | 8 + .../android/app/job/JobScheduler.java | 4 + src/api-impl/android/app/job/JobService.java | 4 + .../android/appwidget/AppWidgetManager.java | 4 + .../android/bluetooth/BluetoothManager.java | 4 + src/api-impl/android/content/Context.java | 5 + .../android/content/DialogInterface.java | 2 + src/api-impl/android/content/Intent.java | 3 +- .../android/content/RestrictionsManager.java | 4 + src/api-impl/android/content/UriMatcher.java | 8 + .../android/content/pm/LauncherApps.java | 4 + .../android/graphics/drawable/Drawable.java | 3 + .../graphics/drawable/RippleDrawable.java | 16 + .../graphics/drawable/ShapeDrawable.java | 19 + .../graphics/drawable/shapes/OvalShape.java | 4 + .../graphics/drawable/shapes/Shape.java | 4 + .../android/hardware/ConsumerIrManager.java | 4 + .../hardware/camera2/CameraManager.java | 4 + .../android/media/AudioAttributes.java | 4 + .../projection/MediaProjectionManager.java | 4 + .../media/session/MediaSessionManager.java | 4 + .../android/media/tv/TvInputManager.java | 4 + .../android/net/ConnectivityManager.java | 3 + src/api-impl/android/net/Uri.java | 4 + .../net/http/X509TrustManagerExtensions.java | 8 + src/api-impl/android/net/nsd/NsdManager.java | 4 + src/api-impl/android/os/BaseBundle.java | 96 ++ src/api-impl/android/os/BatteryManager.java | 4 + src/api-impl/android/os/Bundle.java | 90 +- src/api-impl/android/os/Parcelable.java | 2 + src/api-impl/android/os/UserManager.java | 4 + src/api-impl/android/print/PrintManager.java | 4 + src/api-impl/android/provider/Settings.java | 13 + .../android/telecom/TelecomManager.java | 4 + src/api-impl/android/text/Editable.java | 2 + src/api-impl/android/text/InputFilter.java | 3 + src/api-impl/android/text/Selection.java | 151 ++ .../android/text/SpannableStringBuilder.java | 1418 +++++++++++++++++ src/api-impl/android/text/StaticLayout.java | 4 +- src/api-impl/android/text/TextUtils.java | 16 + src/api-impl/android/text/TextWatcher.java | 6 + .../android/text/method/KeyListener.java | 4 + .../text/method/NumberKeyListener.java | 4 + .../android/text/style/CharacterStyle.java | 4 + src/api-impl/android/view/Display.java | 5 + .../android/view/GestureDetector.java | 5 + src/api-impl/android/view/InputDevice.java | 4 + src/api-impl/android/view/Menu.java | 8 + src/api-impl/android/view/MenuItem.java | 2 + src/api-impl/android/view/View.java | 123 +- src/api-impl/android/view/ViewGroup.java | 13 + .../android/view/ViewOutlineProvider.java | 6 + .../android/view/ViewPropertyAnimator.java | 2 +- src/api-impl/android/view/WindowInsets.java | 18 + .../accessibility/AccessibilityNodeInfo.java | 12 + .../view/accessibility/CaptioningManager.java | 4 + src/api-impl/android/widget/AbsListView.java | 17 + src/api-impl/android/widget/AbsSpinner.java | 16 + src/api-impl/android/widget/AdapterView.java | 20 + src/api-impl/android/widget/EdgeEffect.java | 1 + src/api-impl/android/widget/EditText.java | 34 +- src/api-impl/android/widget/ListView.java | 3 +- src/api-impl/android/widget/SeekBar.java | 3 +- src/api-impl/android/widget/Spinner.java | 13 +- src/api-impl/android/widget/TextView.java | 16 + .../com/android/internal/util/ArrayUtils.java | 27 + .../internal/util/GrowingArrayUtils.java | 182 +++ .../conscrypt/OpenSSLSocketFactoryImpl.java | 6 + .../org/conscrypt/OpenSSLSocketImpl.java | 14 + .../org/conscrypt/SSLParametersImpl.java | 4 + src/api-impl/meson.build | 42 + 78 files changed, 2458 insertions(+), 147 deletions(-) create mode 100644 src/api-impl/android/animation/StateListAnimator.java create mode 100644 src/api-impl/android/app/AppOpsManager.java create mode 100644 src/api-impl/android/app/Notification.java create mode 100644 src/api-impl/android/app/job/JobScheduler.java create mode 100644 src/api-impl/android/app/job/JobService.java create mode 100644 src/api-impl/android/appwidget/AppWidgetManager.java create mode 100644 src/api-impl/android/bluetooth/BluetoothManager.java create mode 100644 src/api-impl/android/content/RestrictionsManager.java create mode 100644 src/api-impl/android/content/UriMatcher.java create mode 100644 src/api-impl/android/content/pm/LauncherApps.java create mode 100644 src/api-impl/android/graphics/drawable/RippleDrawable.java create mode 100644 src/api-impl/android/graphics/drawable/ShapeDrawable.java create mode 100644 src/api-impl/android/graphics/drawable/shapes/OvalShape.java create mode 100644 src/api-impl/android/graphics/drawable/shapes/Shape.java create mode 100644 src/api-impl/android/hardware/ConsumerIrManager.java create mode 100644 src/api-impl/android/hardware/camera2/CameraManager.java create mode 100644 src/api-impl/android/media/AudioAttributes.java create mode 100644 src/api-impl/android/media/projection/MediaProjectionManager.java create mode 100644 src/api-impl/android/media/session/MediaSessionManager.java create mode 100644 src/api-impl/android/media/tv/TvInputManager.java create mode 100644 src/api-impl/android/net/http/X509TrustManagerExtensions.java create mode 100644 src/api-impl/android/net/nsd/NsdManager.java create mode 100644 src/api-impl/android/os/BaseBundle.java create mode 100644 src/api-impl/android/os/BatteryManager.java create mode 100644 src/api-impl/android/os/UserManager.java create mode 100644 src/api-impl/android/print/PrintManager.java create mode 100644 src/api-impl/android/telecom/TelecomManager.java create mode 100644 src/api-impl/android/text/Selection.java create mode 100644 src/api-impl/android/text/SpannableStringBuilder.java create mode 100644 src/api-impl/android/text/method/KeyListener.java create mode 100644 src/api-impl/android/text/method/NumberKeyListener.java create mode 100644 src/api-impl/android/text/style/CharacterStyle.java create mode 100644 src/api-impl/android/view/ViewOutlineProvider.java create mode 100644 src/api-impl/android/view/WindowInsets.java create mode 100644 src/api-impl/android/view/accessibility/AccessibilityNodeInfo.java create mode 100644 src/api-impl/android/view/accessibility/CaptioningManager.java create mode 100644 src/api-impl/android/widget/AbsListView.java create mode 100644 src/api-impl/android/widget/AbsSpinner.java create mode 100644 src/api-impl/com/android/internal/util/GrowingArrayUtils.java create mode 100644 src/api-impl/com/android/org/conscrypt/OpenSSLSocketFactoryImpl.java create mode 100644 src/api-impl/com/android/org/conscrypt/OpenSSLSocketImpl.java create mode 100644 src/api-impl/com/android/org/conscrypt/SSLParametersImpl.java diff --git a/src/api-impl/android/animation/AnimatorInflater.java b/src/api-impl/android/animation/AnimatorInflater.java index efc60aa0..c10e4cfd 100644 --- a/src/api-impl/android/animation/AnimatorInflater.java +++ b/src/api-impl/android/animation/AnimatorInflater.java @@ -7,5 +7,9 @@ public class AnimatorInflater { public static Animator loadAnimator(Context context, int resId) { return new ObjectAnimator(); } + + public static StateListAnimator loadStateListAnimator(Context context, int resId) { + return new StateListAnimator(); + } } diff --git a/src/api-impl/android/animation/ObjectAnimator.java b/src/api-impl/android/animation/ObjectAnimator.java index 9c14fa2f..bb252a50 100644 --- a/src/api-impl/android/animation/ObjectAnimator.java +++ b/src/api-impl/android/animation/ObjectAnimator.java @@ -6,4 +6,10 @@ public class ObjectAnimator extends ValueAnimator { return null; } + public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { + return new ObjectAnimator(); + } + + public ObjectAnimator setDuration(long duration) {return this;} + } diff --git a/src/api-impl/android/animation/StateListAnimator.java b/src/api-impl/android/animation/StateListAnimator.java new file mode 100644 index 00000000..0825e58d --- /dev/null +++ b/src/api-impl/android/animation/StateListAnimator.java @@ -0,0 +1,6 @@ +package android.animation; + +public class StateListAnimator { + + public void addState(int[] specs, Animator animator) {} +} diff --git a/src/api-impl/android/animation/ValueAnimator.java b/src/api-impl/android/animation/ValueAnimator.java index 85f3e59e..2456e578 100644 --- a/src/api-impl/android/animation/ValueAnimator.java +++ b/src/api-impl/android/animation/ValueAnimator.java @@ -31,6 +31,8 @@ public class ValueAnimator extends Animator { public void setFloatValues(float[] values) {} public boolean isRunning() {return false;} public void setIntValues(int[] values) {} + public void setRepeatCount(int value) {} + public void setRepeatMode(int value) {} /** * Implementors of this interface can add themselves as update listeners diff --git a/src/api-impl/android/app/ActivityManager.java b/src/api-impl/android/app/ActivityManager.java index bc5a6c0d..78e37efd 100644 --- a/src/api-impl/android/app/ActivityManager.java +++ b/src/api-impl/android/app/ActivityManager.java @@ -1,5 +1,15 @@ package android.app; +import java.util.List; + public class ActivityManager { + public static class RunningAppProcessInfo{} + + public List getRunningAppProcesses() { + return null; + } + + public boolean isLowRamDevice() {return false;} + } diff --git a/src/api-impl/android/app/AppOpsManager.java b/src/api-impl/android/app/AppOpsManager.java new file mode 100644 index 00000000..a6775d2c --- /dev/null +++ b/src/api-impl/android/app/AppOpsManager.java @@ -0,0 +1,4 @@ +package android.app; + +public class AppOpsManager { +} diff --git a/src/api-impl/android/app/Application.java b/src/api-impl/android/app/Application.java index b3b1add5..d9b0b16d 100644 --- a/src/api-impl/android/app/Application.java +++ b/src/api-impl/android/app/Application.java @@ -9,9 +9,9 @@ import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; import java.io.InputStream; import java.io.IOException; -import android.content.Context; +import android.content.ContextWrapper; -public class Application extends Context { +public class Application extends ContextWrapper { private String app_icon_path = null; private String get_app_icon_path() { @@ -43,6 +43,7 @@ public class Application extends Context { } public Application() { + super(null); /* TODO: is this the right place to put this? */ InputStream inStream = ClassLoader.getSystemClassLoader().getResourceAsStream("AndroidManifest.xml"); try { diff --git a/src/api-impl/android/app/Notification.java b/src/api-impl/android/app/Notification.java new file mode 100644 index 00000000..3dcce1a4 --- /dev/null +++ b/src/api-impl/android/app/Notification.java @@ -0,0 +1,8 @@ +package android.app; + +import android.media.AudioAttributes; + +public class Notification { + + public static final AudioAttributes AUDIO_ATTRIBUTES_DEFAULT = new AudioAttributes(); +} diff --git a/src/api-impl/android/app/job/JobScheduler.java b/src/api-impl/android/app/job/JobScheduler.java new file mode 100644 index 00000000..4c292134 --- /dev/null +++ b/src/api-impl/android/app/job/JobScheduler.java @@ -0,0 +1,4 @@ +package android.app.job; + +public class JobScheduler { +} diff --git a/src/api-impl/android/app/job/JobService.java b/src/api-impl/android/app/job/JobService.java new file mode 100644 index 00000000..3fbfc461 --- /dev/null +++ b/src/api-impl/android/app/job/JobService.java @@ -0,0 +1,4 @@ +package android.app.job; + +public class JobService { +} diff --git a/src/api-impl/android/appwidget/AppWidgetManager.java b/src/api-impl/android/appwidget/AppWidgetManager.java new file mode 100644 index 00000000..8c013a1e --- /dev/null +++ b/src/api-impl/android/appwidget/AppWidgetManager.java @@ -0,0 +1,4 @@ +package android.appwidget; + +public class AppWidgetManager { +} diff --git a/src/api-impl/android/bluetooth/BluetoothManager.java b/src/api-impl/android/bluetooth/BluetoothManager.java new file mode 100644 index 00000000..3188cfbe --- /dev/null +++ b/src/api-impl/android/bluetooth/BluetoothManager.java @@ -0,0 +1,4 @@ +package android.bluetooth; + +public class BluetoothManager { +} diff --git a/src/api-impl/android/content/Context.java b/src/api-impl/android/content/Context.java index ccca87bc..f58e9c38 100644 --- a/src/api-impl/android/content/Context.java +++ b/src/api-impl/android/content/Context.java @@ -13,6 +13,7 @@ import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.hardware.input.InputManager; import android.hardware.SensorManager; import android.hardware.display.DisplayManager; @@ -374,6 +375,10 @@ public class Context extends Object { return getResources().getText(resId); } + public final Drawable getDrawable(int resId) { + return getResources().getDrawable(resId); + } + public boolean isRestricted() {return false;} public File getDatabasePath(String dbName) { diff --git a/src/api-impl/android/content/DialogInterface.java b/src/api-impl/android/content/DialogInterface.java index 4cda3250..0da47301 100644 --- a/src/api-impl/android/content/DialogInterface.java +++ b/src/api-impl/android/content/DialogInterface.java @@ -17,4 +17,6 @@ public interface DialogInterface { } public interface OnCancelListener { } + public interface OnMultiChoiceClickListener { + } } diff --git a/src/api-impl/android/content/Intent.java b/src/api-impl/android/content/Intent.java index fbbaf8e8..1a6ee4d1 100644 --- a/src/api-impl/android/content/Intent.java +++ b/src/api-impl/android/content/Intent.java @@ -201,7 +201,8 @@ public class Intent { } public Intent setAction(String action) { - return this; // FIXME + this.action = action; + return this; } public String getAction() { diff --git a/src/api-impl/android/content/RestrictionsManager.java b/src/api-impl/android/content/RestrictionsManager.java new file mode 100644 index 00000000..bdbdb82d --- /dev/null +++ b/src/api-impl/android/content/RestrictionsManager.java @@ -0,0 +1,4 @@ +package android.content; + +public class RestrictionsManager { +} diff --git a/src/api-impl/android/content/UriMatcher.java b/src/api-impl/android/content/UriMatcher.java new file mode 100644 index 00000000..85174ef8 --- /dev/null +++ b/src/api-impl/android/content/UriMatcher.java @@ -0,0 +1,8 @@ +package android.content; + +public class UriMatcher { + + public UriMatcher(int code) {} + + public void addURI(String authority, String path, int code) {} +} diff --git a/src/api-impl/android/content/pm/LauncherApps.java b/src/api-impl/android/content/pm/LauncherApps.java new file mode 100644 index 00000000..15c9b97c --- /dev/null +++ b/src/api-impl/android/content/pm/LauncherApps.java @@ -0,0 +1,4 @@ +package android.content.pm; + +public class LauncherApps { +} diff --git a/src/api-impl/android/graphics/drawable/Drawable.java b/src/api-impl/android/graphics/drawable/Drawable.java index ff5c4518..f730b51b 100644 --- a/src/api-impl/android/graphics/drawable/Drawable.java +++ b/src/api-impl/android/graphics/drawable/Drawable.java @@ -1,5 +1,6 @@ package android.graphics.drawable; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.ColorFilter; @@ -76,4 +77,6 @@ public abstract class Drawable { public int getIntrinsicWidth() {return 0;} public int getIntrinsicHeight() {return 0;} + + public void setTintList (ColorStateList tint) {} } diff --git a/src/api-impl/android/graphics/drawable/RippleDrawable.java b/src/api-impl/android/graphics/drawable/RippleDrawable.java new file mode 100644 index 00000000..f3fdd2d4 --- /dev/null +++ b/src/api-impl/android/graphics/drawable/RippleDrawable.java @@ -0,0 +1,16 @@ +package android.graphics.drawable; + +import android.content.res.ColorStateList; +import android.graphics.Canvas; + +public class RippleDrawable extends Drawable { + + public RippleDrawable(ColorStateList colorStateList, Drawable drawable, Drawable drawable2) {} + + @Override + public void draw(Canvas canvas) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'draw'"); + } + +} diff --git a/src/api-impl/android/graphics/drawable/ShapeDrawable.java b/src/api-impl/android/graphics/drawable/ShapeDrawable.java new file mode 100644 index 00000000..f0dbdc94 --- /dev/null +++ b/src/api-impl/android/graphics/drawable/ShapeDrawable.java @@ -0,0 +1,19 @@ +package android.graphics.drawable; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.shapes.Shape; + +public class ShapeDrawable extends Drawable { + + public ShapeDrawable(Shape shape) {} + + public Paint getPaint() {return new Paint();} + + @Override + public void draw(Canvas canvas) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'draw'"); + } + +} diff --git a/src/api-impl/android/graphics/drawable/shapes/OvalShape.java b/src/api-impl/android/graphics/drawable/shapes/OvalShape.java new file mode 100644 index 00000000..dbfcf0a9 --- /dev/null +++ b/src/api-impl/android/graphics/drawable/shapes/OvalShape.java @@ -0,0 +1,4 @@ +package android.graphics.drawable.shapes; + +public class OvalShape extends Shape { +} diff --git a/src/api-impl/android/graphics/drawable/shapes/Shape.java b/src/api-impl/android/graphics/drawable/shapes/Shape.java new file mode 100644 index 00000000..e8a607f2 --- /dev/null +++ b/src/api-impl/android/graphics/drawable/shapes/Shape.java @@ -0,0 +1,4 @@ +package android.graphics.drawable.shapes; + +public class Shape { +} diff --git a/src/api-impl/android/hardware/ConsumerIrManager.java b/src/api-impl/android/hardware/ConsumerIrManager.java new file mode 100644 index 00000000..243049bd --- /dev/null +++ b/src/api-impl/android/hardware/ConsumerIrManager.java @@ -0,0 +1,4 @@ +package android.hardware; + +public class ConsumerIrManager { +} diff --git a/src/api-impl/android/hardware/camera2/CameraManager.java b/src/api-impl/android/hardware/camera2/CameraManager.java new file mode 100644 index 00000000..fb050b57 --- /dev/null +++ b/src/api-impl/android/hardware/camera2/CameraManager.java @@ -0,0 +1,4 @@ +package android.hardware.camera2; + +public class CameraManager { +} diff --git a/src/api-impl/android/media/AudioAttributes.java b/src/api-impl/android/media/AudioAttributes.java new file mode 100644 index 00000000..0819d001 --- /dev/null +++ b/src/api-impl/android/media/AudioAttributes.java @@ -0,0 +1,4 @@ +package android.media; + +public class AudioAttributes { +} diff --git a/src/api-impl/android/media/projection/MediaProjectionManager.java b/src/api-impl/android/media/projection/MediaProjectionManager.java new file mode 100644 index 00000000..464cc266 --- /dev/null +++ b/src/api-impl/android/media/projection/MediaProjectionManager.java @@ -0,0 +1,4 @@ +package android.media.projection; + +public class MediaProjectionManager { +} diff --git a/src/api-impl/android/media/session/MediaSessionManager.java b/src/api-impl/android/media/session/MediaSessionManager.java new file mode 100644 index 00000000..3eb0d4bc --- /dev/null +++ b/src/api-impl/android/media/session/MediaSessionManager.java @@ -0,0 +1,4 @@ +package android.media.session; + +public class MediaSessionManager { +} diff --git a/src/api-impl/android/media/tv/TvInputManager.java b/src/api-impl/android/media/tv/TvInputManager.java new file mode 100644 index 00000000..70e1f58c --- /dev/null +++ b/src/api-impl/android/media/tv/TvInputManager.java @@ -0,0 +1,4 @@ +package android.media.tv; + +public class TvInputManager { +} diff --git a/src/api-impl/android/net/ConnectivityManager.java b/src/api-impl/android/net/ConnectivityManager.java index 00f89b6b..b3588f9c 100644 --- a/src/api-impl/android/net/ConnectivityManager.java +++ b/src/api-impl/android/net/ConnectivityManager.java @@ -1,6 +1,9 @@ package android.net; public class ConnectivityManager { + + public class NetworkCallback {} + public NetworkInfo getNetworkInfo(int networkType) { return null; // this means the network type is not supported, which should make properly coded apps cease any attempts to use network-related APIs } diff --git a/src/api-impl/android/net/Uri.java b/src/api-impl/android/net/Uri.java index 58e1e03e..baae422c 100644 --- a/src/api-impl/android/net/Uri.java +++ b/src/api-impl/android/net/Uri.java @@ -145,6 +145,10 @@ public class Uri { } } + public String getScheme() { + return uri.getScheme(); + } + @Override public String toString() { return String.valueOf(uri); diff --git a/src/api-impl/android/net/http/X509TrustManagerExtensions.java b/src/api-impl/android/net/http/X509TrustManagerExtensions.java new file mode 100644 index 00000000..7ed2573a --- /dev/null +++ b/src/api-impl/android/net/http/X509TrustManagerExtensions.java @@ -0,0 +1,8 @@ +package android.net.http; + +import javax.net.ssl.X509TrustManager; + +public class X509TrustManagerExtensions { + + public X509TrustManagerExtensions(X509TrustManager tm) {} +} diff --git a/src/api-impl/android/net/nsd/NsdManager.java b/src/api-impl/android/net/nsd/NsdManager.java new file mode 100644 index 00000000..5c90f393 --- /dev/null +++ b/src/api-impl/android/net/nsd/NsdManager.java @@ -0,0 +1,4 @@ +package android.net.nsd; + +public class NsdManager { +} diff --git a/src/api-impl/android/os/BaseBundle.java b/src/api-impl/android/os/BaseBundle.java new file mode 100644 index 00000000..1ac78f22 --- /dev/null +++ b/src/api-impl/android/os/BaseBundle.java @@ -0,0 +1,96 @@ +package android.os; + +import java.util.Set; + +import android.util.ArrayMap; +import android.util.Log; + +public class BaseBundle { + protected static final String TAG = "Bundle"; + + // Invariant - exactly one of mMap / mParcelledData will be null + // (except inside a call to unparcel) + + /* package */ ArrayMap mMap = null; + + // Log a message if the value was non-null but not of the expected type + void typeWarning(String key, Object value, String className, + Object defaultValue, ClassCastException e) { + StringBuilder sb = new StringBuilder(); + sb.append("Key "); + sb.append(key); + sb.append(" expected "); + sb.append(className); + sb.append(" but value was a "); + sb.append(value.getClass().getName()); + sb.append(". The default value "); + sb.append(defaultValue); + sb.append(" was returned."); + Log.w(TAG, sb.toString()); + Log.w(TAG, "Attempt to cast generated internal exception:", e); + } + + void typeWarning(String key, Object value, String className, + ClassCastException e) { + typeWarning(key, value, className, "", e); + } + + /** + * Returns true if the mapping of this Bundle is empty, false otherwise. + */ + public boolean isEmpty() { + return mMap.isEmpty(); + } + + /** + * Returns a Set containing the Strings used as keys in this Bundle. + * + * @return a Set of String keys + */ + public Set keySet() { + return mMap.keySet(); + } + + /** + * Returns true if the given key is contained in the mapping + * of this Bundle. + * + * @param key a String key + * @return true if the key is part of the mapping, false otherwise + */ + public boolean containsKey(String key) { + return mMap.containsKey(key); + } + + /** + * Returns the value associated with the given key, or null if + * no mapping of the desired type exists for the given key or a null + * value is explicitly associated with the key. + * + * @param key a String, or null + * @return a String value, or null + */ + public String getString(String key) { + final Object o = mMap.get(key); + try { + return (String)o; + } catch (ClassCastException e) { + typeWarning(key, o, "String", e); + return null; + } + } + + /** + * Returns the value associated with the given key, or defaultValue if + * no mapping of the desired type exists for the given key. + * + * @param key a String, or null + * @param defaultValue Value to return if key does not exist + * @return the String value associated with the given key, or defaultValue + * if no valid String object is currently mapped to that key. + */ + public String getString(String key, String defaultValue) { + final String s = getString(key); + return (s == null) ? defaultValue : s; + } +} diff --git a/src/api-impl/android/os/BatteryManager.java b/src/api-impl/android/os/BatteryManager.java new file mode 100644 index 00000000..764c64d5 --- /dev/null +++ b/src/api-impl/android/os/BatteryManager.java @@ -0,0 +1,4 @@ +package android.os; + +public class BatteryManager { +} diff --git a/src/api-impl/android/os/Bundle.java b/src/api-impl/android/os/Bundle.java index 6c04af98..fd7f038c 100644 --- a/src/api-impl/android/os/Bundle.java +++ b/src/api-impl/android/os/Bundle.java @@ -22,14 +22,12 @@ import android.util.SparseArray; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import java.util.Set; /** * A mapping from String values to various Parcelable types. * */ -public final class Bundle implements Cloneable { - private static final String TAG = "Bundle"; +public final class Bundle extends BaseBundle implements Cloneable { static final boolean DEBUG = false; public static final Bundle EMPTY; @@ -40,11 +38,6 @@ public final class Bundle implements Cloneable { EMPTY.mMap = ArrayMap.EMPTY; } - // Invariant - exactly one of mMap / mParcelledData will be null - // (except inside a call to unparcel) - - /* package */ ArrayMap mMap = null; - /* * If mParcelledData is non-null, then mMap will be null and the * data are stored as a Parcel containing a Bundle. When the data @@ -195,13 +188,6 @@ public final class Bundle implements Cloneable { return mMap.size(); } - /** - * Returns true if the mapping of this Bundle is empty, false otherwise. - */ - public boolean isEmpty() { - return mMap.isEmpty(); - } - /** * Removes all elements from the mapping of this Bundle. */ @@ -211,17 +197,6 @@ public final class Bundle implements Cloneable { mFdsKnown = true; } - /** - * Returns true if the given key is contained in the mapping - * of this Bundle. - * - * @param key a String key - * @return true if the key is part of the mapping, false otherwise - */ - public boolean containsKey(String key) { - return mMap.containsKey(key); - } - /** * Returns the entry with the given key as an object. * @@ -254,15 +229,6 @@ public final class Bundle implements Cloneable { mFdsKnown = mFdsKnown && map.mFdsKnown; } - /** - * Returns a Set containing the Strings used as keys in this Bundle. - * - * @return a Set of String keys - */ - public Set keySet() { - return mMap.keySet(); - } - /** * Reports whether the bundle contains any parcelled file descriptors. */ @@ -706,28 +672,6 @@ public final class Bundle implements Cloneable { return getBoolean(key, false); } - // Log a message if the value was non-null but not of the expected type - private void typeWarning(String key, Object value, String className, - Object defaultValue, ClassCastException e) { - StringBuilder sb = new StringBuilder(); - sb.append("Key "); - sb.append(key); - sb.append(" expected "); - sb.append(className); - sb.append(" but value was a "); - sb.append(value.getClass().getName()); - sb.append(". The default value "); - sb.append(defaultValue); - sb.append(" was returned."); - Log.w(TAG, sb.toString()); - Log.w(TAG, "Attempt to cast generated internal exception:", e); - } - - private void typeWarning(String key, Object value, String className, - ClassCastException e) { - typeWarning(key, value, className, "", e); - } - /** * Returns the value associated with the given key, or defaultValue if * no mapping of the desired type exists for the given key. @@ -978,38 +922,6 @@ public final class Bundle implements Cloneable { } } - /** - * Returns the value associated with the given key, or null if - * no mapping of the desired type exists for the given key or a null - * value is explicitly associated with the key. - * - * @param key a String, or null - * @return a String value, or null - */ - public String getString(String key) { - final Object o = mMap.get(key); - try { - return (String)o; - } catch (ClassCastException e) { - typeWarning(key, o, "String", e); - return null; - } - } - - /** - * Returns the value associated with the given key, or defaultValue if - * no mapping of the desired type exists for the given key. - * - * @param key a String, or null - * @param defaultValue Value to return if key does not exist - * @return the String value associated with the given key, or defaultValue - * if no valid String object is currently mapped to that key. - */ - public String getString(String key, String defaultValue) { - final String s = getString(key); - return (s == null) ? defaultValue : s; - } - /** * Returns the value associated with the given key, or null if * no mapping of the desired type exists for the given key or a null diff --git a/src/api-impl/android/os/Parcelable.java b/src/api-impl/android/os/Parcelable.java index 8a7c6440..7f72b4e9 100644 --- a/src/api-impl/android/os/Parcelable.java +++ b/src/api-impl/android/os/Parcelable.java @@ -2,4 +2,6 @@ package android.os; public interface Parcelable { public static interface Creator {} + + public static interface ClassLoaderCreator extends Creator {} } diff --git a/src/api-impl/android/os/UserManager.java b/src/api-impl/android/os/UserManager.java new file mode 100644 index 00000000..4be8a356 --- /dev/null +++ b/src/api-impl/android/os/UserManager.java @@ -0,0 +1,4 @@ +package android.os; + +public class UserManager { +} diff --git a/src/api-impl/android/print/PrintManager.java b/src/api-impl/android/print/PrintManager.java new file mode 100644 index 00000000..588f5671 --- /dev/null +++ b/src/api-impl/android/print/PrintManager.java @@ -0,0 +1,4 @@ +package android.print; + +public class PrintManager { +} diff --git a/src/api-impl/android/provider/Settings.java b/src/api-impl/android/provider/Settings.java index 9c34d568..20748806 100644 --- a/src/api-impl/android/provider/Settings.java +++ b/src/api-impl/android/provider/Settings.java @@ -28,6 +28,8 @@ public class Settings { public static final class System { public static final Uri CONTENT_URI = null; // Uri.parse("content://settings/system"); + public static final Uri DEFAULT_NOTIFICATION_URI = getUriFor("notification_sound"); + public static int getInt(ContentResolver cr, String key, int def) { int ret = getInt(cr, key); if (ret != -1) { @@ -54,5 +56,16 @@ public class Settings { } } + public static final class Global { + + public static int getInt(ContentResolver cr, String key, int def) { + switch (key) { + default: + java.lang.System.out.println("!!!! Settings$Global.getInt: unknown key: >" + key + "<"); + return def; + } + } + } + public static class SettingNotFoundException extends AndroidException {} } diff --git a/src/api-impl/android/telecom/TelecomManager.java b/src/api-impl/android/telecom/TelecomManager.java new file mode 100644 index 00000000..ed2bd168 --- /dev/null +++ b/src/api-impl/android/telecom/TelecomManager.java @@ -0,0 +1,4 @@ +package android.telecom; + +public class TelecomManager { +} diff --git a/src/api-impl/android/text/Editable.java b/src/api-impl/android/text/Editable.java index c4f4f8ad..8c55e143 100644 --- a/src/api-impl/android/text/Editable.java +++ b/src/api-impl/android/text/Editable.java @@ -2,4 +2,6 @@ package android.text; public interface Editable extends CharSequence { + public class Factory {} + } diff --git a/src/api-impl/android/text/InputFilter.java b/src/api-impl/android/text/InputFilter.java index df5f9cda..095ee322 100644 --- a/src/api-impl/android/text/InputFilter.java +++ b/src/api-impl/android/text/InputFilter.java @@ -1,6 +1,9 @@ package android.text; public interface InputFilter { + + public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend); + public static class LengthFilter extends Object implements InputFilter { public LengthFilter(int max) { } diff --git a/src/api-impl/android/text/Selection.java b/src/api-impl/android/text/Selection.java new file mode 100644 index 00000000..9ea05378 --- /dev/null +++ b/src/api-impl/android/text/Selection.java @@ -0,0 +1,151 @@ +/* + * 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.text; + +import java.text.BreakIterator; + +/** + * Utility class for manipulating cursors and selections in CharSequences. + * A cursor is a selection where the start and end are at the same offset. + */ +public class Selection { + private Selection() { /* cannot be instantiated */ } + + /* + * Retrieving the selection + */ + + /** + * Return the offset of the selection anchor or cursor, or -1 if + * there is no selection or cursor. + */ + public static final int getSelectionStart(CharSequence text) { + if (text instanceof Spanned) + return ((Spanned) text).getSpanStart(SELECTION_START); + else + return -1; + } + + /** + * Return the offset of the selection edge or cursor, or -1 if + * there is no selection or cursor. + */ + public static final int getSelectionEnd(CharSequence text) { + if (text instanceof Spanned) + return ((Spanned) text).getSpanStart(SELECTION_END); + else + return -1; + } + /* + * Setting the selection + */ + // private static int pin(int value, int min, int max) { + // return value < min ? 0 : (value > max ? max : value); + // } + + /** + * Set the selection anchor to start and the selection edge + * to stop. + */ + public static void setSelection(Spannable text, int start, int stop) { + // int len = text.length(); + // start = pin(start, 0, len); XXX remove unless we really need it + // stop = pin(stop, 0, len); + int ostart = getSelectionStart(text); + int oend = getSelectionEnd(text); + if (ostart != start || oend != stop) { + text.setSpan(SELECTION_START, start, start, + Spanned.SPAN_POINT_POINT|Spanned.SPAN_INTERMEDIATE); + text.setSpan(SELECTION_END, stop, stop, + Spanned.SPAN_POINT_POINT); + } + } + + /** + * Move the cursor to offset index. + */ + public static final void setSelection(Spannable text, int index) { + setSelection(text, index, index); + } + + /** + * Select the entire text. + */ + public static final void selectAll(Spannable text) { + setSelection(text, 0, text.length()); + } + + /** + * Move the selection edge to offset index. + */ + public static final void extendSelection(Spannable text, int index) { + if (text.getSpanStart(SELECTION_END) != index) + text.setSpan(SELECTION_END, index, index, Spanned.SPAN_POINT_POINT); + } + + /** + * Remove the selection or cursor, if any, from the text. + */ + public static final void removeSelection(Spannable text) { + text.removeSpan(SELECTION_START); + text.removeSpan(SELECTION_END); + } + + /** {@hide} */ + public static interface PositionIterator { + public static final int DONE = BreakIterator.DONE; + public int preceding(int position); + public int following(int position); + } + + /** {@hide} */ + public static boolean moveToPreceding( + Spannable text, PositionIterator iter, boolean extendSelection) { + final int offset = iter.preceding(getSelectionEnd(text)); + if (offset != PositionIterator.DONE) { + if (extendSelection) { + extendSelection(text, offset); + } else { + setSelection(text, offset); + } + } + return true; + } + + /** {@hide} */ + public static boolean moveToFollowing( + Spannable text, PositionIterator iter, boolean extendSelection) { + final int offset = iter.following(getSelectionEnd(text)); + if (offset != PositionIterator.DONE) { + if (extendSelection) { + extendSelection(text, offset); + } else { + setSelection(text, offset); + } + } + return true; + } + + private static final class START implements NoCopySpan { } + private static final class END implements NoCopySpan { } + + /* + * Public constants + */ + public static final Object SELECTION_START = new START(); + public static final Object SELECTION_END = new END(); +} \ No newline at end of file diff --git a/src/api-impl/android/text/SpannableStringBuilder.java b/src/api-impl/android/text/SpannableStringBuilder.java new file mode 100644 index 00000000..2341c55f --- /dev/null +++ b/src/api-impl/android/text/SpannableStringBuilder.java @@ -0,0 +1,1418 @@ +/* + * 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.text; + +import android.graphics.Paint; +import android.os.Build; +import android.util.Log; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; +import libcore.util.EmptyArray; +import java.lang.reflect.Array; +import java.util.IdentityHashMap; +/** + * This is the class for text whose content and markup can both be changed. + */ +public class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable, + Appendable { + + private final static String TAG = "SpannableStringBuilder"; + + /** + * Create a new SpannableStringBuilder with empty contents + */ + public SpannableStringBuilder() { + this(""); + } + + /** + * Create a new SpannableStringBuilder containing a copy of the + * specified text, including its spans if any. + */ + public SpannableStringBuilder(CharSequence text) { + this(text, 0, text.length()); + } + + /** + * Create a new SpannableStringBuilder containing a copy of the + * specified slice of the specified text, including its spans if any. + */ + public SpannableStringBuilder(CharSequence text, int start, int end) { + int srclen = end - start; + if (srclen < 0) throw new StringIndexOutOfBoundsException(); + mText = ArrayUtils.newUnpaddedCharArray(GrowingArrayUtils.growSize(srclen)); + mGapStart = srclen; + mGapLength = mText.length - srclen; + TextUtils.getChars(text, start, end, mText, 0); + mSpanCount = 0; + mSpanInsertCount = 0; + mSpans = EmptyArray.OBJECT; + mSpanStarts = EmptyArray.INT; + mSpanEnds = EmptyArray.INT; + mSpanFlags = EmptyArray.INT; + mSpanMax = EmptyArray.INT; + mSpanOrder = EmptyArray.INT; + if (text instanceof Spanned) { + Spanned sp = (Spanned) text; + Object[] spans = sp.getSpans(start, end, Object.class); + for (int i = 0; i < spans.length; i++) { + if (spans[i] instanceof NoCopySpan) { + continue; + } + int st = sp.getSpanStart(spans[i]) - start; + int en = sp.getSpanEnd(spans[i]) - start; + int fl = sp.getSpanFlags(spans[i]); + if (st < 0) + st = 0; + if (st > end - start) + st = end - start; + if (en < 0) + en = 0; + if (en > end - start) + en = end - start; + setSpan(false, spans[i], st, en, fl, false/*enforceParagraph*/); + } + restoreInvariants(); + } + } + public static SpannableStringBuilder valueOf(CharSequence source) { + if (source instanceof SpannableStringBuilder) { + return (SpannableStringBuilder) source; + } else { + return new SpannableStringBuilder(source); + } + } + + /** + * Return the char at the specified offset within the buffer. + */ + public char charAt(int where) { + int len = length(); + if (where < 0) { + throw new IndexOutOfBoundsException("charAt: " + where + " < 0"); + } else if (where >= len) { + throw new IndexOutOfBoundsException("charAt: " + where + " >= length " + len); + } + if (where >= mGapStart) + return mText[where + mGapLength]; + else + return mText[where]; + } + + /** + * Return the number of chars in the buffer. + */ + public int length() { + return mText.length - mGapLength; + } + + private void resizeFor(int size) { + final int oldLength = mText.length; + if (size + 1 <= oldLength) { + return; + } + char[] newText = ArrayUtils.newUnpaddedCharArray(GrowingArrayUtils.growSize(size)); + System.arraycopy(mText, 0, newText, 0, mGapStart); + final int newLength = newText.length; + final int delta = newLength - oldLength; + final int after = oldLength - (mGapStart + mGapLength); + System.arraycopy(mText, oldLength - after, newText, newLength - after, after); + mText = newText; + mGapLength += delta; + if (mGapLength < 1) + new Exception("mGapLength < 1").printStackTrace(); + if (mSpanCount != 0) { + for (int i = 0; i < mSpanCount; i++) { + if (mSpanStarts[i] > mGapStart) mSpanStarts[i] += delta; + if (mSpanEnds[i] > mGapStart) mSpanEnds[i] += delta; + } + calcMax(treeRoot()); + } + } + + private void moveGapTo(int where) { + if (where == mGapStart) + return; + boolean atEnd = (where == length()); + if (where < mGapStart) { + int overlap = mGapStart - where; + System.arraycopy(mText, where, mText, mGapStart + mGapLength - overlap, overlap); + } else /* where > mGapStart */ { + int overlap = where - mGapStart; + System.arraycopy(mText, where + mGapLength - overlap, mText, mGapStart, overlap); + } + // TODO: be more clever (although the win really isn't that big) + if (mSpanCount != 0) { + for (int i = 0; i < mSpanCount; i++) { + int start = mSpanStarts[i]; + int end = mSpanEnds[i]; + if (start > mGapStart) + start -= mGapLength; + if (start > where) + start += mGapLength; + else if (start == where) { + int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT; + if (flag == POINT || (atEnd && flag == PARAGRAPH)) + start += mGapLength; + } + if (end > mGapStart) + end -= mGapLength; + if (end > where) + end += mGapLength; + else if (end == where) { + int flag = (mSpanFlags[i] & END_MASK); + if (flag == POINT || (atEnd && flag == PARAGRAPH)) + end += mGapLength; + } + mSpanStarts[i] = start; + mSpanEnds[i] = end; + } + calcMax(treeRoot()); + } + mGapStart = where; + } + + // Documentation from interface + public SpannableStringBuilder insert(int where, CharSequence tb, int start, int end) { + return replace(where, where, tb, start, end); + } + + // Documentation from interface + public SpannableStringBuilder insert(int where, CharSequence tb) { + return replace(where, where, tb, 0, tb.length()); + } + + // Documentation from interface + public SpannableStringBuilder delete(int start, int end) { + SpannableStringBuilder ret = replace(start, end, "", 0, 0); + if (mGapLength > 2 * length()) + resizeFor(length()); + return ret; // == this + } + + // Documentation from interface + public void clear() { + replace(0, length(), "", 0, 0); + mSpanInsertCount = 0; + } + + // Documentation from interface + public void clearSpans() { + for (int i = mSpanCount - 1; i >= 0; i--) { + Object what = mSpans[i]; + int ostart = mSpanStarts[i]; + int oend = mSpanEnds[i]; + if (ostart > mGapStart) + ostart -= mGapLength; + if (oend > mGapStart) + oend -= mGapLength; + mSpanCount = i; + mSpans[i] = null; + sendSpanRemoved(what, ostart, oend); + } + if (mIndexOfSpan != null) { + mIndexOfSpan.clear(); + } + mSpanInsertCount = 0; + } + + // Documentation from interface + public SpannableStringBuilder append(CharSequence text) { + int length = length(); + return replace(length, length, text, 0, text.length()); + } + + /** + * Appends the character sequence {@code text} and spans {@code what} over the appended part. + * See {@link Spanned} for an explanation of what the flags mean. + * @param text the character sequence to append. + * @param what the object to be spanned over the appended text. + * @param flags see {@link Spanned}. + * @return this {@code SpannableStringBuilder}. + */ + public SpannableStringBuilder append(CharSequence text, Object what, int flags) { + int start = length(); + append(text); + setSpan(what, start, length(), flags); + return this; + } + + // Documentation from interface + public SpannableStringBuilder append(CharSequence text, int start, int end) { + int length = length(); + return replace(length, length, text, start, end); + } + + // Documentation from interface + public SpannableStringBuilder append(char text) { + return append(String.valueOf(text)); + } + + // Returns true if a node was removed (so we can restart search from root) + private boolean removeSpansForChange(int start, int end, boolean textIsRemoved, int i) { + if ((i & 1) != 0) { + // internal tree node + if (resolveGap(mSpanMax[i]) >= start && + removeSpansForChange(start, end, textIsRemoved, leftChild(i))) { + return true; + } + } + if (i < mSpanCount) { + if ((mSpanFlags[i] & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE && + mSpanStarts[i] >= start && mSpanStarts[i] < mGapStart + mGapLength && + mSpanEnds[i] >= start && mSpanEnds[i] < mGapStart + mGapLength && + // The following condition indicates that the span would become empty + (textIsRemoved || mSpanStarts[i] > start || mSpanEnds[i] < mGapStart)) { + mIndexOfSpan.remove(mSpans[i]); + removeSpan(i, 0 /* flags */); + return true; + } + return resolveGap(mSpanStarts[i]) <= end && (i & 1) != 0 && + removeSpansForChange(start, end, textIsRemoved, rightChild(i)); + } + return false; + } + + private void change(int start, int end, CharSequence cs, int csStart, int csEnd) { + // Can be negative + final int replacedLength = end - start; + final int replacementLength = csEnd - csStart; + final int nbNewChars = replacementLength - replacedLength; + boolean changed = false; + for (int i = mSpanCount - 1; i >= 0; i--) { + int spanStart = mSpanStarts[i]; + if (spanStart > mGapStart) + spanStart -= mGapLength; + int spanEnd = mSpanEnds[i]; + if (spanEnd > mGapStart) + spanEnd -= mGapLength; + if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) { + int ost = spanStart; + int oen = spanEnd; + int clen = length(); + if (spanStart > start && spanStart <= end) { + for (spanStart = end; spanStart < clen; spanStart++) + if (spanStart > end && charAt(spanStart - 1) == '\n') + break; + } + if (spanEnd > start && spanEnd <= end) { + for (spanEnd = end; spanEnd < clen; spanEnd++) + if (spanEnd > end && charAt(spanEnd - 1) == '\n') + break; + } + if (spanStart != ost || spanEnd != oen) { + setSpan(false, mSpans[i], spanStart, spanEnd, mSpanFlags[i], + true/*enforceParagraph*/); + changed = true; + } + } + int flags = 0; + if (spanStart == start) flags |= SPAN_START_AT_START; + else if (spanStart == end + nbNewChars) flags |= SPAN_START_AT_END; + if (spanEnd == start) flags |= SPAN_END_AT_START; + else if (spanEnd == end + nbNewChars) flags |= SPAN_END_AT_END; + mSpanFlags[i] |= flags; + } + if (changed) { + restoreInvariants(); + } + moveGapTo(end); + if (nbNewChars >= mGapLength) { + resizeFor(mText.length + nbNewChars - mGapLength); + } + final boolean textIsRemoved = replacementLength == 0; + // The removal pass needs to be done before the gap is updated in order to broadcast the + // correct previous positions to the correct intersecting SpanWatchers + if (replacedLength > 0) { // no need for span fixup on pure insertion + while (mSpanCount > 0 && + removeSpansForChange(start, end, textIsRemoved, treeRoot())) { + // keep deleting spans as needed, and restart from root after every deletion + // because deletion can invalidate an index. + } + } + mGapStart += nbNewChars; + mGapLength -= nbNewChars; + if (mGapLength < 1) + new Exception("mGapLength < 1").printStackTrace(); + TextUtils.getChars(cs, csStart, csEnd, mText, start); + if (replacedLength > 0) { // no need for span fixup on pure insertion + // TODO potential optimization: only update bounds on intersecting spans + final boolean atEnd = (mGapStart + mGapLength == mText.length); + for (int i = 0; i < mSpanCount; i++) { + final int startFlag = (mSpanFlags[i] & START_MASK) >> START_SHIFT; + mSpanStarts[i] = updatedIntervalBound(mSpanStarts[i], start, nbNewChars, startFlag, + atEnd, textIsRemoved); + final int endFlag = (mSpanFlags[i] & END_MASK); + mSpanEnds[i] = updatedIntervalBound(mSpanEnds[i], start, nbNewChars, endFlag, + atEnd, textIsRemoved); + } + // TODO potential optimization: only fix up invariants when bounds actually changed + restoreInvariants(); + } + if (cs instanceof Spanned) { + Spanned sp = (Spanned) cs; + Object[] spans = sp.getSpans(csStart, csEnd, Object.class); + for (int i = 0; i < spans.length; i++) { + int st = sp.getSpanStart(spans[i]); + int en = sp.getSpanEnd(spans[i]); + if (st < csStart) st = csStart; + if (en > csEnd) en = csEnd; + // Add span only if this object is not yet used as a span in this string + if (getSpanStart(spans[i]) < 0) { + int copySpanStart = st - csStart + start; + int copySpanEnd = en - csStart + start; + int copySpanFlags = sp.getSpanFlags(spans[i]) | SPAN_ADDED; + setSpan(false, spans[i], copySpanStart, copySpanEnd, copySpanFlags, + false/*enforceParagraph*/); + } + } + restoreInvariants(); + } + } + + private int updatedIntervalBound(int offset, int start, int nbNewChars, int flag, boolean atEnd, + boolean textIsRemoved) { + if (offset >= start && offset < mGapStart + mGapLength) { + if (flag == POINT) { + // A POINT located inside the replaced range should be moved to the end of the + // replaced text. + // The exception is when the point is at the start of the range and we are doing a + // text replacement (as opposed to a deletion): the point stays there. + if (textIsRemoved || offset > start) { + return mGapStart + mGapLength; + } + } else { + if (flag == PARAGRAPH) { + if (atEnd) { + return mGapStart + mGapLength; + } + } else { // MARK + // MARKs should be moved to the start, with the exception of a mark located at + // the end of the range (which will be < mGapStart + mGapLength since mGapLength + // is > 0, which should stay 'unchanged' at the end of the replaced text. + if (textIsRemoved || offset < mGapStart - nbNewChars) { + return start; + } else { + // Move to the end of replaced text (needed if nbNewChars != 0) + return mGapStart; + } + } + } + } + return offset; + } + + // Note: caller is responsible for removing the mIndexOfSpan entry. + private void removeSpan(int i, int flags) { + Object object = mSpans[i]; + int start = mSpanStarts[i]; + int end = mSpanEnds[i]; + if (start > mGapStart) start -= mGapLength; + if (end > mGapStart) end -= mGapLength; + int count = mSpanCount - (i + 1); + System.arraycopy(mSpans, i + 1, mSpans, i, count); + System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count); + System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count); + System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count); + System.arraycopy(mSpanOrder, i + 1, mSpanOrder, i, count); + mSpanCount--; + invalidateIndex(i); + mSpans[mSpanCount] = null; + // Invariants must be restored before sending span removed notifications. + restoreInvariants(); + if ((flags & Spanned.SPAN_INTERMEDIATE) == 0) { + sendSpanRemoved(object, start, end); + } + } + + // Documentation from interface + public SpannableStringBuilder replace(int start, int end, CharSequence tb) { + return replace(start, end, tb, 0, tb.length()); + } + + // Documentation from interface + public SpannableStringBuilder replace(final int start, final int end, + CharSequence tb, int tbstart, int tbend) { + checkRange("replace", start, end); + int filtercount = mFilters.length; + for (int i = 0; i < filtercount; i++) { + CharSequence repl = mFilters[i].filter(tb, tbstart, tbend, this, start, end); + if (repl != null) { + tb = repl; + tbstart = 0; + tbend = repl.length(); + } + } + final int origLen = end - start; + final int newLen = tbend - tbstart; + if (origLen == 0 && newLen == 0 && !hasNonExclusiveExclusiveSpanAt(tb, tbstart)) { + // This is a no-op iif there are no spans in tb that would be added (with a 0-length) + // Early exit so that the text watchers do not get notified + return this; + } + TextWatcher[] textWatchers = getSpans(start, start + origLen, TextWatcher.class); + sendBeforeTextChanged(textWatchers, start, origLen, newLen); + // Try to keep the cursor / selection at the same relative position during + // a text replacement. If replaced or replacement text length is zero, this + // is already taken care of. + boolean adjustSelection = origLen != 0 && newLen != 0; + int selectionStart = 0; + int selectionEnd = 0; + if (adjustSelection) { + selectionStart = Selection.getSelectionStart(this); + selectionEnd = Selection.getSelectionEnd(this); + } + change(start, end, tb, tbstart, tbend); + if (adjustSelection) { + boolean changed = false; + if (selectionStart > start && selectionStart < end) { + final long diff = selectionStart - start; + final int offset = Math.toIntExact(diff * newLen / origLen); + selectionStart = start + offset; + changed = true; + setSpan(false, Selection.SELECTION_START, selectionStart, selectionStart, + Spanned.SPAN_POINT_POINT, true/*enforceParagraph*/); + } + if (selectionEnd > start && selectionEnd < end) { + final long diff = selectionEnd - start; + final int offset = Math.toIntExact(diff * newLen / origLen); + selectionEnd = start + offset; + changed = true; + setSpan(false, Selection.SELECTION_END, selectionEnd, selectionEnd, + Spanned.SPAN_POINT_POINT, true/*enforceParagraph*/); + } + if (changed) { + restoreInvariants(); + } + } + sendTextChanged(textWatchers, start, origLen, newLen); + sendAfterTextChanged(textWatchers); + // Span watchers need to be called after text watchers, which may update the layout + sendToSpanWatchers(start, end, newLen - origLen); + return this; + } + + private static boolean hasNonExclusiveExclusiveSpanAt(CharSequence text, int offset) { + if (text instanceof Spanned) { + Spanned spanned = (Spanned) text; + Object[] spans = spanned.getSpans(offset, offset, Object.class); + final int length = spans.length; + for (int i = 0; i < length; i++) { + Object span = spans[i]; + int flags = spanned.getSpanFlags(span); + if (flags != Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) return true; + } + } + return false; + } + + private void sendToSpanWatchers(int replaceStart, int replaceEnd, int nbNewChars) { + for (int i = 0; i < mSpanCount; i++) { + int spanFlags = mSpanFlags[i]; + // This loop handles only modified (not added) spans. + if ((spanFlags & SPAN_ADDED) != 0) continue; + int spanStart = mSpanStarts[i]; + int spanEnd = mSpanEnds[i]; + if (spanStart > mGapStart) spanStart -= mGapLength; + if (spanEnd > mGapStart) spanEnd -= mGapLength; + int newReplaceEnd = replaceEnd + nbNewChars; + boolean spanChanged = false; + int previousSpanStart = spanStart; + if (spanStart > newReplaceEnd) { + if (nbNewChars != 0) { + previousSpanStart -= nbNewChars; + spanChanged = true; + } + } else if (spanStart >= replaceStart) { + // No change if span start was already at replace interval boundaries before replace + if ((spanStart != replaceStart || + ((spanFlags & SPAN_START_AT_START) != SPAN_START_AT_START)) && + (spanStart != newReplaceEnd || + ((spanFlags & SPAN_START_AT_END) != SPAN_START_AT_END))) { + // TODO A correct previousSpanStart cannot be computed at this point. + // It would require to save all the previous spans' positions before the replace + // Using an invalid -1 value to convey this would break the broacast range + spanChanged = true; + } + } + int previousSpanEnd = spanEnd; + if (spanEnd > newReplaceEnd) { + if (nbNewChars != 0) { + previousSpanEnd -= nbNewChars; + spanChanged = true; + } + } else if (spanEnd >= replaceStart) { + // No change if span start was already at replace interval boundaries before replace + if ((spanEnd != replaceStart || + ((spanFlags & SPAN_END_AT_START) != SPAN_END_AT_START)) && + (spanEnd != newReplaceEnd || + ((spanFlags & SPAN_END_AT_END) != SPAN_END_AT_END))) { + // TODO same as above for previousSpanEnd + spanChanged = true; + } + } + if (spanChanged) { + sendSpanChanged(mSpans[i], previousSpanStart, previousSpanEnd, spanStart, spanEnd); + } + mSpanFlags[i] &= ~SPAN_START_END_MASK; + } + // Handle added spans + for (int i = 0; i < mSpanCount; i++) { + int spanFlags = mSpanFlags[i]; + if ((spanFlags & SPAN_ADDED) != 0) { + mSpanFlags[i] &= ~SPAN_ADDED; + int spanStart = mSpanStarts[i]; + int spanEnd = mSpanEnds[i]; + if (spanStart > mGapStart) spanStart -= mGapLength; + if (spanEnd > mGapStart) spanEnd -= mGapLength; + sendSpanAdded(mSpans[i], spanStart, spanEnd); + } + } + } + + /** + * Mark the specified range of text with the specified object. + * The flags determine how the span will behave when text is + * inserted at the start or end of the span's range. + */ + public void setSpan(Object what, int start, int end, int flags) { + setSpan(true, what, start, end, flags, true/*enforceParagraph*/); + } + + // Note: if send is false, then it is the caller's responsibility to restore + // invariants. If send is false and the span already exists, then this method + // will not change the index of any spans. + private void setSpan(boolean send, Object what, int start, int end, int flags, + boolean enforceParagraph) { + checkRange("setSpan", start, end); + int flagsStart = (flags & START_MASK) >> START_SHIFT; + if (isInvalidParagraph(start, flagsStart)) { + if (!enforceParagraph) { + // do not set the span + return; + } + throw new RuntimeException("PARAGRAPH span must start at paragraph boundary" + + " (" + start + " follows " + charAt(start - 1) + ")"); + } + int flagsEnd = flags & END_MASK; + if (isInvalidParagraph(end, flagsEnd)) { + if (!enforceParagraph) { + // do not set the span + return; + } + throw new RuntimeException("PARAGRAPH span must end at paragraph boundary" + + " (" + end + " follows " + charAt(end - 1) + ")"); + } + // 0-length Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + if (flagsStart == POINT && flagsEnd == MARK && start == end) { + if (send) { + Log.e(TAG, "SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length"); + } + // Silently ignore invalid spans when they are created from this class. + // This avoids the duplication of the above test code before all the + // calls to setSpan that are done in this class + return; + } + int nstart = start; + int nend = end; + if (start > mGapStart) { + start += mGapLength; + } else if (start == mGapStart) { + if (flagsStart == POINT || (flagsStart == PARAGRAPH && start == length())) + start += mGapLength; + } + if (end > mGapStart) { + end += mGapLength; + } else if (end == mGapStart) { + if (flagsEnd == POINT || (flagsEnd == PARAGRAPH && end == length())) + end += mGapLength; + } + if (mIndexOfSpan != null) { + Integer index = mIndexOfSpan.get(what); + if (index != null) { + int i = index; + int ostart = mSpanStarts[i]; + int oend = mSpanEnds[i]; + if (ostart > mGapStart) + ostart -= mGapLength; + if (oend > mGapStart) + oend -= mGapLength; + mSpanStarts[i] = start; + mSpanEnds[i] = end; + mSpanFlags[i] = flags; + if (send) { + restoreInvariants(); + sendSpanChanged(what, ostart, oend, nstart, nend); + } + return; + } + } + mSpans = GrowingArrayUtils.append(mSpans, mSpanCount, what); + mSpanStarts = GrowingArrayUtils.append(mSpanStarts, mSpanCount, start); + mSpanEnds = GrowingArrayUtils.append(mSpanEnds, mSpanCount, end); + mSpanFlags = GrowingArrayUtils.append(mSpanFlags, mSpanCount, flags); + mSpanOrder = GrowingArrayUtils.append(mSpanOrder, mSpanCount, mSpanInsertCount); + invalidateIndex(mSpanCount); + mSpanCount++; + mSpanInsertCount++; + // Make sure there is enough room for empty interior nodes. + // This magic formula computes the size of the smallest perfect binary + // tree no smaller than mSpanCount. + int sizeOfMax = 2 * treeRoot() + 1; + if (mSpanMax.length < sizeOfMax) { + mSpanMax = new int[sizeOfMax]; + } + if (send) { + restoreInvariants(); + sendSpanAdded(what, nstart, nend); + } + } + + private boolean isInvalidParagraph(int index, int flag) { + return flag == PARAGRAPH && index != 0 && index != length() && charAt(index - 1) != '\n'; + } + + /** + * Remove the specified markup object from the buffer. + */ + public void removeSpan(Object what) { + removeSpan(what, 0 /* flags */); + } + + /** + * Remove the specified markup object from the buffer. + * + * @hide + */ + public void removeSpan(Object what, int flags) { + if (mIndexOfSpan == null) return; + Integer i = mIndexOfSpan.remove(what); + if (i != null) { + removeSpan(i.intValue(), flags); + } + } + + /** + * Return externally visible offset given offset into gapped buffer. + */ + private int resolveGap(int i) { + return i > mGapStart ? i - mGapLength : i; + } + + /** + * Return the buffer offset of the beginning of the specified + * markup object, or -1 if it is not attached to this buffer. + */ + public int getSpanStart(Object what) { + if (mIndexOfSpan == null) return -1; + Integer i = mIndexOfSpan.get(what); + return i == null ? -1 : resolveGap(mSpanStarts[i]); + } + + /** + * Return the buffer offset of the end of the specified + * markup object, or -1 if it is not attached to this buffer. + */ + public int getSpanEnd(Object what) { + if (mIndexOfSpan == null) return -1; + Integer i = mIndexOfSpan.get(what); + return i == null ? -1 : resolveGap(mSpanEnds[i]); + } + + /** + * Return the flags of the end of the specified + * markup object, or 0 if it is not attached to this buffer. + */ + public int getSpanFlags(Object what) { + if (mIndexOfSpan == null) return 0; + Integer i = mIndexOfSpan.get(what); + return i == null ? 0 : mSpanFlags[i]; + } + + /** + * Return an array of the spans of the specified type that overlap + * the specified range of the buffer. The kind may be Object.class to get + * a list of all the spans regardless of type. + */ + @SuppressWarnings("unchecked") + public T[] getSpans(int queryStart, int queryEnd, Class kind) { + return getSpans(queryStart, queryEnd, kind, true); + } + + /** + * Return an array of the spans of the specified type that overlap + * the specified range of the buffer. The kind may be Object.class to get + * a list of all the spans regardless of type. + * + * @param queryStart Start index. + * @param queryEnd End index. + * @param kind Class type to search for. + * @param sortByInsertionOrder If true the results are sorted by the insertion order. + * @param + * @return Array of the spans. Empty array if no results are found. + * + * @hide + */ + public T[] getSpans(int queryStart, int queryEnd, Class kind, + boolean sortByInsertionOrder) { + if (kind == null) return (T[]) ArrayUtils.emptyArray(Object.class); + if (mSpanCount == 0) return ArrayUtils.emptyArray(kind); + int count = countSpans(queryStart, queryEnd, kind, treeRoot()); + if (count == 0) { + return ArrayUtils.emptyArray(kind); + } + // Safe conversion, but requires a suppressWarning + T[] ret = (T[]) Array.newInstance(kind, count); + final int[] prioSortBuffer = sortByInsertionOrder ? obtain(count) : EmptyArray.INT; + final int[] orderSortBuffer = sortByInsertionOrder ? obtain(count) : EmptyArray.INT; + getSpansRec(queryStart, queryEnd, kind, treeRoot(), ret, prioSortBuffer, + orderSortBuffer, 0, sortByInsertionOrder); + if (sortByInsertionOrder) { + sort(ret, prioSortBuffer, orderSortBuffer); + recycle(prioSortBuffer); + recycle(orderSortBuffer); + } + return ret; + } + + private int countSpans(int queryStart, int queryEnd, Class kind, int i) { + int count = 0; + if ((i & 1) != 0) { + // internal tree node + int left = leftChild(i); + int spanMax = mSpanMax[left]; + if (spanMax > mGapStart) { + spanMax -= mGapLength; + } + if (spanMax >= queryStart) { + count = countSpans(queryStart, queryEnd, kind, left); + } + } + if (i < mSpanCount) { + int spanStart = mSpanStarts[i]; + if (spanStart > mGapStart) { + spanStart -= mGapLength; + } + if (spanStart <= queryEnd) { + int spanEnd = mSpanEnds[i]; + if (spanEnd > mGapStart) { + spanEnd -= mGapLength; + } + if (spanEnd >= queryStart && + (spanStart == spanEnd || queryStart == queryEnd || + (spanStart != queryEnd && spanEnd != queryStart)) && + (Object.class == kind || kind.isInstance(mSpans[i]))) { + count++; + } + if ((i & 1) != 0) { + count += countSpans(queryStart, queryEnd, kind, rightChild(i)); + } + } + } + return count; + } + + /** + * Fills the result array with the spans found under the current interval tree node. + * + * @param queryStart Start index for the interval query. + * @param queryEnd End index for the interval query. + * @param kind Class type to search for. + * @param i Index of the current tree node. + * @param ret Array to be filled with results. + * @param priority Buffer to keep record of the priorities of spans found. + * @param insertionOrder Buffer to keep record of the insertion orders of spans found. + * @param count The number of found spans. + * @param sort Flag to fill the priority and insertion order buffers. If false then + * the spans with priority flag will be sorted in the result array. + * @param + * @return The total number of spans found. + */ + @SuppressWarnings("unchecked") + private int getSpansRec(int queryStart, int queryEnd, Class kind, + int i, T[] ret, int[] priority, int[] insertionOrder, int count, boolean sort) { + if ((i & 1) != 0) { + // internal tree node + int left = leftChild(i); + int spanMax = mSpanMax[left]; + if (spanMax > mGapStart) { + spanMax -= mGapLength; + } + if (spanMax >= queryStart) { + count = getSpansRec(queryStart, queryEnd, kind, left, ret, priority, + insertionOrder, count, sort); + } + } + if (i >= mSpanCount) return count; + int spanStart = mSpanStarts[i]; + if (spanStart > mGapStart) { + spanStart -= mGapLength; + } + if (spanStart <= queryEnd) { + int spanEnd = mSpanEnds[i]; + if (spanEnd > mGapStart) { + spanEnd -= mGapLength; + } + if (spanEnd >= queryStart && + (spanStart == spanEnd || queryStart == queryEnd || + (spanStart != queryEnd && spanEnd != queryStart)) && + (Object.class == kind || kind.isInstance(mSpans[i]))) { + int spanPriority = mSpanFlags[i] & SPAN_PRIORITY; + int target = count; + if (sort) { + priority[target] = spanPriority; + insertionOrder[target] = mSpanOrder[i]; + } else if (spanPriority != 0) { + //insertion sort for elements with priority + int j = 0; + for (; j < count; j++) { + int p = getSpanFlags(ret[j]) & SPAN_PRIORITY; + if (spanPriority > p) break; + } + System.arraycopy(ret, j, ret, j + 1, count - j); + target = j; + } + ret[target] = (T) mSpans[i]; + count++; + } + if (count < ret.length && (i & 1) != 0) { + count = getSpansRec(queryStart, queryEnd, kind, rightChild(i), ret, priority, + insertionOrder, count, sort); + } + } + return count; + } + + /** + * Obtain a temporary sort buffer. + * + * @param elementCount the size of the int[] to be returned + * @return an int[] with elementCount length + */ + private static int[] obtain(final int elementCount) { + int[] result = null; + synchronized (sCachedIntBuffer) { + // try finding a tmp buffer with length of at least elementCount + // if not get the first available one + int candidateIndex = -1; + for (int i = sCachedIntBuffer.length - 1; i >= 0; i--) { + if (sCachedIntBuffer[i] != null) { + if (sCachedIntBuffer[i].length >= elementCount) { + candidateIndex = i; + break; + } else if (candidateIndex == -1) { + candidateIndex = i; + } + } + } + if (candidateIndex != -1) { + result = sCachedIntBuffer[candidateIndex]; + sCachedIntBuffer[candidateIndex] = null; + } + } + result = checkSortBuffer(result, elementCount); + return result; + } + + /** + * Recycle sort buffer. + * + * @param buffer buffer to be recycled + */ + private static void recycle(int[] buffer) { + synchronized (sCachedIntBuffer) { + for (int i = 0; i < sCachedIntBuffer.length; i++) { + if (sCachedIntBuffer[i] == null || buffer.length > sCachedIntBuffer[i].length) { + sCachedIntBuffer[i] = buffer; + break; + } + } + } + } + + /** + * Check the size of the buffer and grow if required. + * + * @param buffer buffer to be checked. + * @param size required size. + * @return Same buffer instance if the current size is greater than required size. Otherwise a + * new instance is created and returned. + */ + private static int[] checkSortBuffer(int[] buffer, int size) { + if (buffer == null || size > buffer.length) { + return ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(size)); + } + return buffer; + } + + /** + * An iterative heap sort implementation. It will sort the spans using first their priority + * then insertion order. A span with higher priority will be before a span with lower + * priority. If priorities are the same, the spans will be sorted with insertion order. A + * span with a lower insertion order will be before a span with a higher insertion order. + * + * @param array Span array to be sorted. + * @param priority Priorities of the spans + * @param insertionOrder Insertion orders of the spans + * @param Span object type. + * @param + */ + private final void sort(T[] array, int[] priority, int[] insertionOrder) { + int size = array.length; + for (int i = size / 2 - 1; i >= 0; i--) { + siftDown(i, array, size, priority, insertionOrder); + } + for (int i = size - 1; i > 0; i--) { + final T tmpSpan = array[0]; + array[0] = array[i]; + array[i] = tmpSpan; + final int tmpPriority = priority[0]; + priority[0] = priority[i]; + priority[i] = tmpPriority; + final int tmpOrder = insertionOrder[0]; + insertionOrder[0] = insertionOrder[i]; + insertionOrder[i] = tmpOrder; + siftDown(0, array, i, priority, insertionOrder); + } + } + + /** + * Helper function for heap sort. + * + * @param index Index of the element to sift down. + * @param array Span array to be sorted. + * @param size Current heap size. + * @param priority Priorities of the spans + * @param insertionOrder Insertion orders of the spans + * @param Span object type. + */ + private final void siftDown(int index, T[] array, int size, int[] priority, + int[] insertionOrder) { + int left = 2 * index + 1; + while (left < size) { + if (left < size - 1 && compareSpans(left, left + 1, priority, insertionOrder) < 0) { + left++; + } + if (compareSpans(index, left, priority, insertionOrder) >= 0) { + break; + } + final T tmpSpan = array[index]; + array[index] = array[left]; + array[left] = tmpSpan; + final int tmpPriority = priority[index]; + priority[index] = priority[left]; + priority[left] = tmpPriority; + final int tmpOrder = insertionOrder[index]; + insertionOrder[index] = insertionOrder[left]; + insertionOrder[left] = tmpOrder; + index = left; + left = 2 * index + 1; + } + } + + /** + * Compare two span elements in an array. Comparison is based first on the priority flag of + * the span, and then the insertion order of the span. + * + * @param left Index of the element to compare. + * @param right Index of the other element to compare. + * @param priority Priorities of the spans + * @param insertionOrder Insertion orders of the spans + * @return + */ + private final int compareSpans(int left, int right, int[] priority, + int[] insertionOrder) { + int priority1 = priority[left]; + int priority2 = priority[right]; + if (priority1 == priority2) { + return Integer.compare(insertionOrder[left], insertionOrder[right]); + } + // since high priority has to be before a lower priority, the arguments to compare are + // opposite of the insertion order check. + return Integer.compare(priority2, priority1); + } + + /** + * Return the next offset after start but less than or + * equal to limit where a span of the specified type + * begins or ends. + */ + public int nextSpanTransition(int start, int limit, Class kind) { + if (mSpanCount == 0) return limit; + if (kind == null) { + kind = Object.class; + } + return nextSpanTransitionRec(start, limit, kind, treeRoot()); + } + + private int nextSpanTransitionRec(int start, int limit, Class kind, int i) { + if ((i & 1) != 0) { + // internal tree node + int left = leftChild(i); + if (resolveGap(mSpanMax[left]) > start) { + limit = nextSpanTransitionRec(start, limit, kind, left); + } + } + if (i < mSpanCount) { + int st = resolveGap(mSpanStarts[i]); + int en = resolveGap(mSpanEnds[i]); + if (st > start && st < limit && kind.isInstance(mSpans[i])) + limit = st; + if (en > start && en < limit && kind.isInstance(mSpans[i])) + limit = en; + if (st < limit && (i & 1) != 0) { + limit = nextSpanTransitionRec(start, limit, kind, rightChild(i)); + } + } + return limit; + } + + /** + * Return a new CharSequence containing a copy of the specified + * range of this buffer, including the overlapping spans. + */ + public CharSequence subSequence(int start, int end) { + return new SpannableStringBuilder(this, start, end); + } + + /** + * Copy the specified range of chars from this buffer into the + * specified array, beginning at the specified offset. + */ + public void getChars(int start, int end, char[] dest, int destoff) { + checkRange("getChars", start, end); + if (end <= mGapStart) { + System.arraycopy(mText, start, dest, destoff, end - start); + } else if (start >= mGapStart) { + System.arraycopy(mText, start + mGapLength, dest, destoff, end - start); + } else { + System.arraycopy(mText, start, dest, destoff, mGapStart - start); + System.arraycopy(mText, mGapStart + mGapLength, + dest, destoff + (mGapStart - start), + end - mGapStart); + } + } + + /** + * Return a String containing a copy of the chars in this buffer. + */ + @Override + public String toString() { + int len = length(); + char[] buf = new char[len]; + getChars(0, len, buf, 0); + return new String(buf); + } + /** + * Return a String containing a copy of the chars in this buffer, limited to the + * [start, end[ range. + * @hide + */ + public String substring(int start, int end) { + char[] buf = new char[end - start]; + getChars(start, end, buf, 0); + return new String(buf); + } + + /** + * Returns the depth of TextWatcher callbacks. Returns 0 when the object is not handling + * TextWatchers. A return value greater than 1 implies that a TextWatcher caused a change that + * recursively triggered a TextWatcher. + */ + public int getTextWatcherDepth() { + return mTextWatcherDepth; + } + + private void sendBeforeTextChanged(TextWatcher[] watchers, int start, int before, int after) { + int n = watchers.length; + mTextWatcherDepth++; + for (int i = 0; i < n; i++) { + watchers[i].beforeTextChanged(this, start, before, after); + } + mTextWatcherDepth--; + } + + private void sendTextChanged(TextWatcher[] watchers, int start, int before, int after) { + int n = watchers.length; + mTextWatcherDepth++; + for (int i = 0; i < n; i++) { + watchers[i].onTextChanged(this, start, before, after); + } + mTextWatcherDepth--; + } + + private void sendAfterTextChanged(TextWatcher[] watchers) { + int n = watchers.length; + mTextWatcherDepth++; + for (int i = 0; i < n; i++) { + watchers[i].afterTextChanged(this); + } + mTextWatcherDepth--; + } + + private void sendSpanAdded(Object what, int start, int end) { + SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class); + int n = recip.length; + for (int i = 0; i < n; i++) { + recip[i].onSpanAdded(this, what, start, end); + } + } + + private void sendSpanRemoved(Object what, int start, int end) { + SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class); + int n = recip.length; + for (int i = 0; i < n; i++) { + recip[i].onSpanRemoved(this, what, start, end); + } + } + + private void sendSpanChanged(Object what, int oldStart, int oldEnd, int start, int end) { + // The bounds of a possible SpanWatcher are guaranteed to be set before this method is + // called, so that the order of the span does not affect this broadcast. + SpanWatcher[] spanWatchers = getSpans(Math.min(oldStart, start), + Math.min(Math.max(oldEnd, end), length()), SpanWatcher.class); + int n = spanWatchers.length; + for (int i = 0; i < n; i++) { + spanWatchers[i].onSpanChanged(this, what, oldStart, oldEnd, start, end); + } + } + + private static String region(int start, int end) { + return "(" + start + " ... " + end + ")"; + } + + private void checkRange(final String operation, int start, int end) { + if (end < start) { + throw new IndexOutOfBoundsException(operation + " " + + region(start, end) + " has end before start"); + } + int len = length(); + if (start > len || end > len) { + throw new IndexOutOfBoundsException(operation + " " + + region(start, end) + " ends beyond length " + len); + } + if (start < 0 || end < 0) { + throw new IndexOutOfBoundsException(operation + " " + + region(start, end) + " starts before 0"); + } + } + + // Documentation from interface + public void setFilters(InputFilter[] filters) { + if (filters == null) { + throw new IllegalArgumentException(); + } + mFilters = filters; + } + + // Documentation from interface + public InputFilter[] getFilters() { + return mFilters; + } + + // Same as SpannableStringInternal + @Override + public boolean equals(Object o) { + if (o instanceof Spanned && + toString().equals(o.toString())) { + final Spanned other = (Spanned) o; + // Check span data + final Object[] otherSpans = other.getSpans(0, other.length(), Object.class); + final Object[] thisSpans = getSpans(0, length(), Object.class); + if (mSpanCount == otherSpans.length) { + for (int i = 0; i < mSpanCount; ++i) { + final Object thisSpan = thisSpans[i]; + final Object otherSpan = otherSpans[i]; + if (thisSpan == this) { + if (other != otherSpan || + getSpanStart(thisSpan) != other.getSpanStart(otherSpan) || + getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) || + getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) { + return false; + } + } else if (!thisSpan.equals(otherSpan) || + getSpanStart(thisSpan) != other.getSpanStart(otherSpan) || + getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) || + getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) { + return false; + } + } + return true; + } + } + return false; + } + + // Same as SpannableStringInternal + @Override + public int hashCode() { + int hash = toString().hashCode(); + hash = hash * 31 + mSpanCount; + for (int i = 0; i < mSpanCount; ++i) { + Object span = mSpans[i]; + if (span != this) { + hash = hash * 31 + span.hashCode(); + } + hash = hash * 31 + getSpanStart(span); + hash = hash * 31 + getSpanEnd(span); + hash = hash * 31 + getSpanFlags(span); + } + return hash; + } + + // Primitives for treating span list as binary tree + // The spans (along with start and end offsets and flags) are stored in linear arrays sorted + // by start offset. For fast searching, there is a binary search structure imposed over these + // arrays. This structure is inorder traversal of a perfect binary tree, a slightly unusual + // but advantageous approach. + // The value-containing nodes are indexed 0 <= i < n (where n = mSpanCount), thus preserving + // logic that accesses the values as a contiguous array. Other balanced binary tree approaches + // (such as a complete binary tree) would require some shuffling of node indices. + // Basic properties of this structure: For a perfect binary tree of height m: + // The tree has 2^(m+1) - 1 total nodes. + // The root of the tree has index 2^m - 1. + // All leaf nodes have even index, all interior nodes odd. + // The height of a node of index i is the number of trailing ones in i's binary representation. + // The left child of a node i of height h is i - 2^(h - 1). + // The right child of a node i of height h is i + 2^(h - 1). + // Note that for arbitrary n, interior nodes of this tree may be >= n. Thus, the general + // structure of a recursive traversal of node i is: + // * traverse left child if i is an interior node + // * process i if i < n + // * traverse right child if i is an interior node and i < n + private int treeRoot() { + return Integer.highestOneBit(mSpanCount) - 1; + } + + // (i+1) & ~i is equal to 2^(the number of trailing ones in i) + private static int leftChild(int i) { + return i - (((i + 1) & ~i) >> 1); + } + + private static int rightChild(int i) { + return i + (((i + 1) & ~i) >> 1); + } + + // The span arrays are also augmented by an mSpanMax[] array that represents an interval tree + // over the binary tree structure described above. For each node, the mSpanMax[] array contains + // the maximum value of mSpanEnds of that node and its descendants. Thus, traversals can + // easily reject subtrees that contain no spans overlapping the area of interest. + // Note that mSpanMax[] also has a valid valuefor interior nodes of index >= n, but which have + // descendants of index < n. In these cases, it simply represents the maximum span end of its + // descendants. This is a consequence of the perfect binary tree structure. + private int calcMax(int i) { + int max = 0; + if ((i & 1) != 0) { + // internal tree node + max = calcMax(leftChild(i)); + } + if (i < mSpanCount) { + max = Math.max(max, mSpanEnds[i]); + if ((i & 1) != 0) { + max = Math.max(max, calcMax(rightChild(i))); + } + } + mSpanMax[i] = max; + return max; + } + + // restores binary interval tree invariants after any mutation of span structure + private void restoreInvariants() { + if (mSpanCount == 0) return; + // invariant 1: span starts are nondecreasing + // This is a simple insertion sort because we expect it to be mostly sorted. + for (int i = 1; i < mSpanCount; i++) { + if (mSpanStarts[i] < mSpanStarts[i - 1]) { + Object span = mSpans[i]; + int start = mSpanStarts[i]; + int end = mSpanEnds[i]; + int flags = mSpanFlags[i]; + int insertionOrder = mSpanOrder[i]; + int j = i; + do { + mSpans[j] = mSpans[j - 1]; + mSpanStarts[j] = mSpanStarts[j - 1]; + mSpanEnds[j] = mSpanEnds[j - 1]; + mSpanFlags[j] = mSpanFlags[j - 1]; + mSpanOrder[j] = mSpanOrder[j - 1]; + j--; + } while (j > 0 && start < mSpanStarts[j - 1]); + mSpans[j] = span; + mSpanStarts[j] = start; + mSpanEnds[j] = end; + mSpanFlags[j] = flags; + mSpanOrder[j] = insertionOrder; + invalidateIndex(j); + } + } + // invariant 2: max is max span end for each node and its descendants + calcMax(treeRoot()); + // invariant 3: mIndexOfSpan maps spans back to indices + if (mIndexOfSpan == null) { + mIndexOfSpan = new IdentityHashMap(); + } + for (int i = mLowWaterMark; i < mSpanCount; i++) { + Integer existing = mIndexOfSpan.get(mSpans[i]); + if (existing == null || existing != i) { + mIndexOfSpan.put(mSpans[i], i); + } + } + mLowWaterMark = Integer.MAX_VALUE; + } + + // Call this on any update to mSpans[], so that mIndexOfSpan can be updated + private void invalidateIndex(int i) { + mLowWaterMark = Math.min(i, mLowWaterMark); + } + + private static final InputFilter[] NO_FILTERS = new InputFilter[0]; + private static final int[][] sCachedIntBuffer = new int[6][0]; + private InputFilter[] mFilters = NO_FILTERS; + private char[] mText; + private int mGapStart; + private int mGapLength; + private Object[] mSpans; + private int[] mSpanStarts; + private int[] mSpanEnds; + private int[] mSpanMax; // see calcMax() for an explanation of what this array stores + private int[] mSpanFlags; + private int[] mSpanOrder; // store the order of span insertion + private int mSpanInsertCount; // counter for the span insertion + private int mSpanCount; + private IdentityHashMap mIndexOfSpan; + private int mLowWaterMark; // indices below this have not been touched + + // TextWatcher callbacks may trigger changes that trigger more callbacks. This keeps track of + // how deep the callbacks go. + private int mTextWatcherDepth; + + // TODO These value are tightly related to the public SPAN_MARK/POINT values in {@link Spanned} + private static final int MARK = 1; + private static final int POINT = 2; + private static final int PARAGRAPH = 3; + private static final int START_MASK = 0xF0; + private static final int END_MASK = 0x0F; + private static final int START_SHIFT = 4; + // These bits are not (currently) used by SPANNED flags + private static final int SPAN_ADDED = 0x800; + private static final int SPAN_START_AT_START = 0x1000; + private static final int SPAN_START_AT_END = 0x2000; + private static final int SPAN_END_AT_START = 0x4000; + private static final int SPAN_END_AT_END = 0x8000; + private static final int SPAN_START_END_MASK = 0xF000; +} \ No newline at end of file diff --git a/src/api-impl/android/text/StaticLayout.java b/src/api-impl/android/text/StaticLayout.java index 9df82998..4c48aa30 100644 --- a/src/api-impl/android/text/StaticLayout.java +++ b/src/api-impl/android/text/StaticLayout.java @@ -1,8 +1,6 @@ package android.text; -import android.text.Layout.Alignment; - -public class StaticLayout { +public class StaticLayout extends Layout { private CharSequence text; diff --git a/src/api-impl/android/text/TextUtils.java b/src/api-impl/android/text/TextUtils.java index 3ead693a..07474f42 100644 --- a/src/api-impl/android/text/TextUtils.java +++ b/src/api-impl/android/text/TextUtils.java @@ -108,4 +108,20 @@ public class TextUtils { float avail, TruncateAt where) { return text; } + + public static void getChars(CharSequence s, int start, int end, char[] dest, int destoff) { + Class c = s.getClass(); + if (c == String.class) + ((String) s).getChars(start, end, dest, destoff); + else if (c == StringBuffer.class) + ((StringBuffer) s).getChars(start, end, dest, destoff); + else if (c == StringBuilder.class) + ((StringBuilder) s).getChars(start, end, dest, destoff); + else if (s instanceof GetChars) + ((GetChars) s).getChars(start, end, dest, destoff); + else { + for (int i = start; i < end; i++) + dest[destoff++] = s.charAt(i); + } + } } diff --git a/src/api-impl/android/text/TextWatcher.java b/src/api-impl/android/text/TextWatcher.java index f7d82cf7..2f64a154 100644 --- a/src/api-impl/android/text/TextWatcher.java +++ b/src/api-impl/android/text/TextWatcher.java @@ -1,4 +1,10 @@ package android.text; public interface TextWatcher { + + public void beforeTextChanged(CharSequence s, int start, int count, int after); + + public void onTextChanged(CharSequence s, int start, int before, int count); + + public void afterTextChanged(Editable s); } diff --git a/src/api-impl/android/text/method/KeyListener.java b/src/api-impl/android/text/method/KeyListener.java new file mode 100644 index 00000000..067fb2ae --- /dev/null +++ b/src/api-impl/android/text/method/KeyListener.java @@ -0,0 +1,4 @@ +package android.text.method; + +public interface KeyListener { +} diff --git a/src/api-impl/android/text/method/NumberKeyListener.java b/src/api-impl/android/text/method/NumberKeyListener.java new file mode 100644 index 00000000..f06264ef --- /dev/null +++ b/src/api-impl/android/text/method/NumberKeyListener.java @@ -0,0 +1,4 @@ +package android.text.method; + +public abstract class NumberKeyListener implements KeyListener { +} diff --git a/src/api-impl/android/text/style/CharacterStyle.java b/src/api-impl/android/text/style/CharacterStyle.java new file mode 100644 index 00000000..fadd1d46 --- /dev/null +++ b/src/api-impl/android/text/style/CharacterStyle.java @@ -0,0 +1,4 @@ +package android.text.style; + +public class CharacterStyle { +} diff --git a/src/api-impl/android/view/Display.java b/src/api-impl/android/view/Display.java index 91cd6b83..c2613deb 100644 --- a/src/api-impl/android/view/Display.java +++ b/src/api-impl/android/view/Display.java @@ -1,5 +1,6 @@ package android.view; +import android.graphics.Point; import android.util.DisplayMetrics; public final class Display { @@ -54,4 +55,8 @@ public final class Display { public long getPresentationDeadlineNanos() { return 0; // what else... } + + public void getSize(Point size) { + size.set(getWidth(), getHeight()); + } } diff --git a/src/api-impl/android/view/GestureDetector.java b/src/api-impl/android/view/GestureDetector.java index 83544ee5..582b3087 100644 --- a/src/api-impl/android/view/GestureDetector.java +++ b/src/api-impl/android/view/GestureDetector.java @@ -1,7 +1,12 @@ package android.view; +import android.content.Context; +import android.os.Handler; + public class GestureDetector { + public GestureDetector(Context context, OnGestureListener listener, Handler handler) {} + public interface OnGestureListener { } diff --git a/src/api-impl/android/view/InputDevice.java b/src/api-impl/android/view/InputDevice.java index bc3de467..ea85e4e1 100644 --- a/src/api-impl/android/view/InputDevice.java +++ b/src/api-impl/android/view/InputDevice.java @@ -45,6 +45,10 @@ public class InputDevice { return new ArrayList(); } + public boolean supportsSource(int source) { + return true; + } + public class MotionRange { int axis; diff --git a/src/api-impl/android/view/Menu.java b/src/api-impl/android/view/Menu.java index 4e976b9b..9c786e91 100644 --- a/src/api-impl/android/view/Menu.java +++ b/src/api-impl/android/view/Menu.java @@ -13,4 +13,12 @@ public interface Menu { public void clear(); public void removeGroup(int groupId); + + public SubMenu addSubMenu(int id); + + public MenuItem add(int id); + + public MenuItem add(CharSequence text); + + public void setGroupCheckable(int group, boolean checkable, boolean exclusive); } diff --git a/src/api-impl/android/view/MenuItem.java b/src/api-impl/android/view/MenuItem.java index 127bd696..14421ff9 100644 --- a/src/api-impl/android/view/MenuItem.java +++ b/src/api-impl/android/view/MenuItem.java @@ -26,4 +26,6 @@ public interface MenuItem { public int getGroupId(); + public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener listener); + } \ No newline at end of file diff --git a/src/api-impl/android/view/View.java b/src/api-impl/android/view/View.java index d3290e6a..21f99f09 100644 --- a/src/api-impl/android/view/View.java +++ b/src/api-impl/android/view/View.java @@ -1,5 +1,6 @@ package android.view; +import android.animation.StateListAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; @@ -19,6 +20,7 @@ import android.view.animation.Animation; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; public class View extends Object { @@ -776,7 +778,7 @@ public class View extends Object { // --- end of subclasses - public int id; + public int id = NO_ID; private int system_ui_visibility = 0; public ViewGroup parent; public AttributeSet attrs; @@ -815,9 +817,11 @@ public class View extends Object { widget = native_constructor(context, attrs); if (attrs != null) { - id = attrs.getAttributeResourceValue("http://schemas.android.com/apk/res/android", "id", 0); - if (id != 0) - setId(id); + int id = attrs.getAttributeResourceValue("http://schemas.android.com/apk/res/android", "id", 0); + if (id != 0) { + this.id = id; + view_by_id.put(id, this); + } } } @@ -995,6 +999,14 @@ public class View extends Object { return 0; } + public int getPaddingStart() { + return 0; + } + + public int getPaddingEnd() { + return 0; + } + public void postInvalidate() { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override @@ -1328,4 +1340,107 @@ public class View extends Object { public Display getDisplay() { return new Display(); } + + private int importantForAccessibility; + public void setImportantForAccessibility(int importantForAccessibility) { + this.importantForAccessibility = importantForAccessibility; + } + public int getImportantForAccessibility() { + return importantForAccessibility; + } + + public boolean getFitsSystemWindows() {return true;} + + public void setOnApplyWindowInsetsListener(View.OnApplyWindowInsetsListener l) {} + + public final boolean isFocusable() {return true;} + public boolean isClickable() {return true;} + public boolean isLongClickable() {return true;} + + public int getLayoutDirection() {return LAYOUT_DIRECTION_LTR;} + + public void setBackground(Drawable background) {} + + private float elevation; + public void setElevation(float elevation) { + this.elevation = elevation; + } + public float getElevation() { + return elevation; + } + + public boolean isLaidOut() {return true;} + + public void postOnAnimation(Runnable action) { + post(action); + } + + public void setHorizontalScrollBarEnabled(boolean enabled) {} + + public void postInvalidateOnAnimation() { + postInvalidate(); + } + + public void setPaddingRelative(int start, int top, int end, int bottom) {} + + public boolean isAttachedToWindow() {return true;} + + public void requestApplyInsets() {} + + public View getRootView() { + View view = this; + while (view.parent != null) { + view = view.parent; + } + return view; + } + + public boolean isShown() {return true;} + + public int getWindowVisibility() {return VISIBLE;} + + public float getAlpha() {return 1.f;} + + public View findFocus() {return this;} + + public int getMinimumHeight() {return getSuggestedMinimumHeight();} + public int getMinimumWidth() {return getSuggestedMinimumWidth();} + + public boolean isNestedScrollingEnabled() {return true;} + + public void setClipToOutline(boolean clipToOutline) {} + + public boolean hasTransientState() {return false;} + + public final void cancelPendingInputEvents() {} + + public ViewOutlineProvider getOutlineProvider() {return new ViewOutlineProvider();} + public void setOutlineProvider(ViewOutlineProvider provider) {} + + public void setStateListAnimator(StateListAnimator stateListAnimator) {} + + private static final AtomicInteger nextGeneratedId = new AtomicInteger(1); + /** + * Generate a value suitable for use in {@link #setId(int)}. + * This value will not collide with ID values generated at build time by aapt for R.id. + * + * @return a generated ID value + */ + public static int generateViewId() { + for (;;) { + final int result = nextGeneratedId.get(); + // aapt-generated IDs have the high byte nonzero; clamp to the range under that. + int newValue = result + 1; + if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0. + if (nextGeneratedId.compareAndSet(result, newValue)) { + return result; + } + } + } + + public boolean isLayoutDirectionResolved() {return true;} + + public boolean isPaddingRelative() {return false;} + + public void setForeground(Drawable foreground) {} } diff --git a/src/api-impl/android/view/ViewGroup.java b/src/api-impl/android/view/ViewGroup.java index 3d2ceffe..21df8255 100644 --- a/src/api-impl/android/view/ViewGroup.java +++ b/src/api-impl/android/view/ViewGroup.java @@ -287,6 +287,8 @@ public class ViewGroup extends View implements ViewParent, ViewManager { } } + protected void setChildrenDrawingOrderEnabled(boolean enabled) {} + public static class LayoutParams { public static final int FILL_PARENT = -1; public static final int MATCH_PARENT = -1; @@ -355,6 +357,17 @@ public class ViewGroup extends View implements ViewParent, ViewManager { public MarginLayoutParams(Context context, AttributeSet attributeSet) { super(context, attributeSet); } + + public int getMarginStart() { + return leftMargin; + } + + public int getMarginEnd() { + return rightMargin; + } + + public void setMarginStart(int marginStart) {} + public void setMarginEnd(int marginEnd) {} } public interface OnHierarchyChangeListener { diff --git a/src/api-impl/android/view/ViewOutlineProvider.java b/src/api-impl/android/view/ViewOutlineProvider.java new file mode 100644 index 00000000..e41f20ce --- /dev/null +++ b/src/api-impl/android/view/ViewOutlineProvider.java @@ -0,0 +1,6 @@ +package android.view; + +public class ViewOutlineProvider { + + public static final ViewOutlineProvider BACKGROUND = new ViewOutlineProvider(); +} diff --git a/src/api-impl/android/view/ViewPropertyAnimator.java b/src/api-impl/android/view/ViewPropertyAnimator.java index e4c1a15f..04ea4022 100644 --- a/src/api-impl/android/view/ViewPropertyAnimator.java +++ b/src/api-impl/android/view/ViewPropertyAnimator.java @@ -13,7 +13,7 @@ public class ViewPropertyAnimator { public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) { if (listener != null) - listener.onAnimationEnd(null); + listener.onAnimationEnd(new Animator()); return this; } diff --git a/src/api-impl/android/view/WindowInsets.java b/src/api-impl/android/view/WindowInsets.java new file mode 100644 index 00000000..6947ba84 --- /dev/null +++ b/src/api-impl/android/view/WindowInsets.java @@ -0,0 +1,18 @@ +package android.view; + +public class WindowInsets { + + public static final WindowInsets CONSUMED = new WindowInsets(); + + public WindowInsets() {} + + public WindowInsets(WindowInsets windowInsets) {} + + public WindowInsets consumeStableInsets() { + return this; + } + + public WindowInsets consumeSystemWindowInsets() { + return this; + } +} diff --git a/src/api-impl/android/view/accessibility/AccessibilityNodeInfo.java b/src/api-impl/android/view/accessibility/AccessibilityNodeInfo.java new file mode 100644 index 00000000..f4a010a7 --- /dev/null +++ b/src/api-impl/android/view/accessibility/AccessibilityNodeInfo.java @@ -0,0 +1,12 @@ +package android.view.accessibility; + +public class AccessibilityNodeInfo { + + public static final class AccessibilityAction { + + public AccessibilityAction(int actionId, CharSequence label) {} + + public int getId() {return 0;} + + } +} diff --git a/src/api-impl/android/view/accessibility/CaptioningManager.java b/src/api-impl/android/view/accessibility/CaptioningManager.java new file mode 100644 index 00000000..53b47102 --- /dev/null +++ b/src/api-impl/android/view/accessibility/CaptioningManager.java @@ -0,0 +1,4 @@ +package android.view.accessibility; + +public class CaptioningManager { +} diff --git a/src/api-impl/android/widget/AbsListView.java b/src/api-impl/android/widget/AbsListView.java new file mode 100644 index 00000000..a068345b --- /dev/null +++ b/src/api-impl/android/widget/AbsListView.java @@ -0,0 +1,17 @@ +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +public abstract class AbsListView extends View { + + public AbsListView(Context context) { + super(context); + } + + public AbsListView(Context context, AttributeSet attributeSet) { + super(context, attributeSet); + } + +} diff --git a/src/api-impl/android/widget/AbsSpinner.java b/src/api-impl/android/widget/AbsSpinner.java new file mode 100644 index 00000000..869f8b7c --- /dev/null +++ b/src/api-impl/android/widget/AbsSpinner.java @@ -0,0 +1,16 @@ +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; + +public abstract class AbsSpinner extends AdapterView { + + public AbsSpinner(Context context) { + super(context); + } + + public AbsSpinner(Context context, AttributeSet attributeSet) { + super(context, attributeSet); + } + +} \ No newline at end of file diff --git a/src/api-impl/android/widget/AdapterView.java b/src/api-impl/android/widget/AdapterView.java index 286caf86..a9754c85 100644 --- a/src/api-impl/android/widget/AdapterView.java +++ b/src/api-impl/android/widget/AdapterView.java @@ -1,12 +1,32 @@ package android.widget; +import android.content.Context; +import android.util.AttributeSet; import android.view.ViewGroup; public abstract class AdapterView extends ViewGroup { + public AdapterView(Context context) { + super(context); + } + + public AdapterView(Context context, AttributeSet attributeSet) { + super(context, attributeSet); + } + public interface OnItemSelectedListener { } public interface OnItemClickListener { } + + public void setAdapter(SpinnerAdapter adapter) { + } + + public void setSelection(int i) { + } + + public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {} + + public void setSelection(int position, boolean animate) {} } diff --git a/src/api-impl/android/widget/EdgeEffect.java b/src/api-impl/android/widget/EdgeEffect.java index 79321447..8d40b9a6 100644 --- a/src/api-impl/android/widget/EdgeEffect.java +++ b/src/api-impl/android/widget/EdgeEffect.java @@ -16,6 +16,7 @@ public class EdgeEffect extends View { public void setSize(int width, int height) {} public void onPull(float deltaDistance) {} + public void onPull(float deltaDistance, float displacement) {} public boolean isFinished() {return true;} } diff --git a/src/api-impl/android/widget/EditText.java b/src/api-impl/android/widget/EditText.java index e3aa2ca3..454f902f 100644 --- a/src/api-impl/android/widget/EditText.java +++ b/src/api-impl/android/widget/EditText.java @@ -2,6 +2,7 @@ package android.widget; import android.content.Context; import android.text.Editable; +import android.text.SpannableStringBuilder; import android.text.TextWatcher; import android.util.AttributeSet; @@ -20,11 +21,11 @@ public class EditText extends TextView { protected native void native_addTextChangedListener(long widget, TextWatcher watcher); public Editable getText() { - return new FIXME_Editable(native_getText(widget)); + return new SpannableStringBuilder(native_getText(widget)); } public Editable getEditableText() { - return new FIXME_Editable(native_getText(widget)); + return new SpannableStringBuilder(native_getText(widget)); } @Override @@ -32,35 +33,6 @@ public class EditText extends TextView { @Override public void setTextSize(float size) {} - private class FIXME_Editable implements Editable { - private String string; - - public FIXME_Editable(String string) { - this.string = string; - } - - @Override - public int length() { - return string.length(); - } - - @Override - public char charAt(int index) { - return string.charAt(index); - } - - @Override - public CharSequence subSequence(int start, int end) { - return string.subSequence(start, end); - } - - @Override - public String toString() { - return string; - } - - } - @Override public void addTextChangedListener(TextWatcher watcher) { native_addTextChangedListener(widget, watcher); diff --git a/src/api-impl/android/widget/ListView.java b/src/api-impl/android/widget/ListView.java index ccb611ed..4f5dcc45 100644 --- a/src/api-impl/android/widget/ListView.java +++ b/src/api-impl/android/widget/ListView.java @@ -2,9 +2,8 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; -import android.view.View; -public class ListView extends View { +public class ListView extends AbsListView { public ListView(Context context) { super(context); diff --git a/src/api-impl/android/widget/SeekBar.java b/src/api-impl/android/widget/SeekBar.java index 0e91af23..09cd3fac 100644 --- a/src/api-impl/android/widget/SeekBar.java +++ b/src/api-impl/android/widget/SeekBar.java @@ -2,9 +2,8 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; -import android.view.View; -public class SeekBar extends View { +public class SeekBar extends ProgressBar { public SeekBar(Context context) { super(context); diff --git a/src/api-impl/android/widget/Spinner.java b/src/api-impl/android/widget/Spinner.java index bb0b3f64..2069adaf 100644 --- a/src/api-impl/android/widget/Spinner.java +++ b/src/api-impl/android/widget/Spinner.java @@ -2,9 +2,8 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; -import android.view.View; -public class Spinner extends View { +public class Spinner extends AbsSpinner { public Spinner(Context context) { super(context); @@ -14,14 +13,4 @@ public class Spinner extends View { super(context, attributeSet); } - public void setAdapter(SpinnerAdapter adapter) { - } - - public void setSelection(int i) { - } - - public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {} - - public void setSelection(int position, boolean animate) {} - } \ No newline at end of file diff --git a/src/api-impl/android/widget/TextView.java b/src/api-impl/android/widget/TextView.java index 7ae3635b..1772d343 100644 --- a/src/api-impl/android/widget/TextView.java +++ b/src/api-impl/android/widget/TextView.java @@ -5,10 +5,12 @@ import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.text.Editable; import android.text.InputFilter; import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.method.KeyListener; import android.text.method.TransformationMethod; import android.util.AttributeSet; import android.view.Gravity; @@ -149,6 +151,20 @@ public class TextView extends View { public final void setAutoLinkMask(int mask) {} + public void setEditableFactory(Editable.Factory factory) {} + + public KeyListener getKeyListener() {return null;} + + public int getInputType() {return 0;} + + public final void setTransformationMethod(TransformationMethod method) {} + + public InputFilter[] getFilters() {return new InputFilter[0];} + + public int getMaxLines() {return -1;} + + public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom) {} + public static interface OnEditorActionListener { } } diff --git a/src/api-impl/com/android/internal/util/ArrayUtils.java b/src/api-impl/com/android/internal/util/ArrayUtils.java index 8709a3c5..c347bfdd 100644 --- a/src/api-impl/com/android/internal/util/ArrayUtils.java +++ b/src/api-impl/com/android/internal/util/ArrayUtils.java @@ -17,6 +17,7 @@ package com.android.internal.util; import java.lang.reflect.Array; +import dalvik.system.VMRuntime; // XXX these should be changed to reflect the actual memory allocator we use. // it looks like right now objects want to be powers of 2 minus 8 @@ -34,6 +35,32 @@ public class ArrayUtils { private ArrayUtils() { /* cannot be instantiated */ } + public static byte[] newUnpaddedByteArray(int minLen) { + return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen); + } + public static char[] newUnpaddedCharArray(int minLen) { + return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen); + } + public static int[] newUnpaddedIntArray(int minLen) { + return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen); + } + public static boolean[] newUnpaddedBooleanArray(int minLen) { + return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen); + } + public static long[] newUnpaddedLongArray(int minLen) { + return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen); + } + public static float[] newUnpaddedFloatArray(int minLen) { + return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen); + } + public static Object[] newUnpaddedObjectArray(int minLen) { + return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen); + } + @SuppressWarnings("unchecked") + public static T[] newUnpaddedArray(Class clazz, int minLen) { + return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen); + } + public static int idealByteArraySize(int need) { for (int i = 4; i < 32; i++) if (need <= (1 << i) - 12) diff --git a/src/api-impl/com/android/internal/util/GrowingArrayUtils.java b/src/api-impl/com/android/internal/util/GrowingArrayUtils.java new file mode 100644 index 00000000..aee8ab4e --- /dev/null +++ b/src/api-impl/com/android/internal/util/GrowingArrayUtils.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2014 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 com.android.internal.util; + +/** + * A helper class that aims to provide comparable growth performance to ArrayList, but on primitive + * arrays. Common array operations are implemented for efficient use in dynamic containers. + * + * All methods in this class assume that the length of an array is equivalent to its capacity and + * NOT the number of elements in the array. The current size of the array is always passed in as a + * parameter. + * + * @hide + */ +public final class GrowingArrayUtils { + /** + * Appends an element to the end of the array, growing the array if there is no more room. + * @param array The array to which to append the element. This must NOT be null. + * @param currentSize The number of elements in the array. Must be less than or equal to + * array.length. + * @param element The element to append. + * @return the array to which the element was appended. This may be different than the given + * array. + */ + public static T[] append(T[] array, int currentSize, T element) { + assert currentSize <= array.length; + if (currentSize + 1 > array.length) { + @SuppressWarnings("unchecked") + T[] newArray = ArrayUtils.newUnpaddedArray( + (Class) array.getClass().getComponentType(), growSize(currentSize)); + System.arraycopy(array, 0, newArray, 0, currentSize); + array = newArray; + } + array[currentSize] = element; + return array; + } + + /** + * Primitive int version of {@link #append(Object[], int, Object)}. + */ + public static int[] append(int[] array, int currentSize, int element) { + assert currentSize <= array.length; + if (currentSize + 1 > array.length) { + int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize)); + System.arraycopy(array, 0, newArray, 0, currentSize); + array = newArray; + } + array[currentSize] = element; + return array; + } + + /** + * Primitive long version of {@link #append(Object[], int, Object)}. + */ + public static long[] append(long[] array, int currentSize, long element) { + assert currentSize <= array.length; + if (currentSize + 1 > array.length) { + long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize)); + System.arraycopy(array, 0, newArray, 0, currentSize); + array = newArray; + } + array[currentSize] = element; + return array; + } + + /** + * Primitive boolean version of {@link #append(Object[], int, Object)}. + */ + public static boolean[] append(boolean[] array, int currentSize, boolean element) { + assert currentSize <= array.length; + if (currentSize + 1 > array.length) { + boolean[] newArray = ArrayUtils.newUnpaddedBooleanArray(growSize(currentSize)); + System.arraycopy(array, 0, newArray, 0, currentSize); + array = newArray; + } + array[currentSize] = element; + return array; + } + + /** + * Inserts an element into the array at the specified index, growing the array if there is no + * more room. + * + * @param array The array to which to append the element. Must NOT be null. + * @param currentSize The number of elements in the array. Must be less than or equal to + * array.length. + * @param element The element to insert. + * @return the array to which the element was appended. This may be different than the given + * array. + */ + public static T[] insert(T[] array, int currentSize, int index, T element) { + assert currentSize <= array.length; + if (currentSize + 1 <= array.length) { + System.arraycopy(array, index, array, index + 1, currentSize - index); + array[index] = element; + return array; + } + @SuppressWarnings("unchecked") + T[] newArray = ArrayUtils.newUnpaddedArray((Class)array.getClass().getComponentType(), + growSize(currentSize)); + System.arraycopy(array, 0, newArray, 0, index); + newArray[index] = element; + System.arraycopy(array, index, newArray, index + 1, array.length - index); + return newArray; + } + + /** + * Primitive int version of {@link #insert(Object[], int, int, Object)}. + */ + public static int[] insert(int[] array, int currentSize, int index, int element) { + assert currentSize <= array.length; + if (currentSize + 1 <= array.length) { + System.arraycopy(array, index, array, index + 1, currentSize - index); + array[index] = element; + return array; + } + int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize)); + System.arraycopy(array, 0, newArray, 0, index); + newArray[index] = element; + System.arraycopy(array, index, newArray, index + 1, array.length - index); + return newArray; + } + + /** + * Primitive long version of {@link #insert(Object[], int, int, Object)}. + */ + public static long[] insert(long[] array, int currentSize, int index, long element) { + assert currentSize <= array.length; + if (currentSize + 1 <= array.length) { + System.arraycopy(array, index, array, index + 1, currentSize - index); + array[index] = element; + return array; + } + long[] newArray = ArrayUtils.newUnpaddedLongArray(growSize(currentSize)); + System.arraycopy(array, 0, newArray, 0, index); + newArray[index] = element; + System.arraycopy(array, index, newArray, index + 1, array.length - index); + return newArray; + } + + /** + * Primitive boolean version of {@link #insert(Object[], int, int, Object)}. + */ + public static boolean[] insert(boolean[] array, int currentSize, int index, boolean element) { + assert currentSize <= array.length; + if (currentSize + 1 <= array.length) { + System.arraycopy(array, index, array, index + 1, currentSize - index); + array[index] = element; + return array; + } + boolean[] newArray = ArrayUtils.newUnpaddedBooleanArray(growSize(currentSize)); + System.arraycopy(array, 0, newArray, 0, index); + newArray[index] = element; + System.arraycopy(array, index, newArray, index + 1, array.length - index); + return newArray; + } + + /** + * Given the current size of an array, returns an ideal size to which the array should grow. + * This is typically double the given size, but should not be relied upon to do so in the + * future. + */ + public static int growSize(int currentSize) { + return currentSize <= 4 ? 8 : currentSize * 2; + } + // Uninstantiable + private GrowingArrayUtils() {} +} \ No newline at end of file diff --git a/src/api-impl/com/android/org/conscrypt/OpenSSLSocketFactoryImpl.java b/src/api-impl/com/android/org/conscrypt/OpenSSLSocketFactoryImpl.java new file mode 100644 index 00000000..e698605c --- /dev/null +++ b/src/api-impl/com/android/org/conscrypt/OpenSSLSocketFactoryImpl.java @@ -0,0 +1,6 @@ +package com.android.org.conscrypt; + +import javax.net.ssl.SSLSocketFactory; + +public abstract class OpenSSLSocketFactoryImpl extends SSLSocketFactory { +} diff --git a/src/api-impl/com/android/org/conscrypt/OpenSSLSocketImpl.java b/src/api-impl/com/android/org/conscrypt/OpenSSLSocketImpl.java new file mode 100644 index 00000000..195f1d90 --- /dev/null +++ b/src/api-impl/com/android/org/conscrypt/OpenSSLSocketImpl.java @@ -0,0 +1,14 @@ +package com.android.org.conscrypt; + +import javax.net.ssl.SSLSocket; + +public abstract class OpenSSLSocketImpl extends SSLSocket { + + public abstract void setUseSessionTickets(boolean useSessionTickets); + + public abstract void setHostname(String hostName); + + public abstract byte[] getAlpnSelectedProtocol(); + + public abstract void setAlpnProtocols​(byte[] alpnProtos); +} diff --git a/src/api-impl/com/android/org/conscrypt/SSLParametersImpl.java b/src/api-impl/com/android/org/conscrypt/SSLParametersImpl.java new file mode 100644 index 00000000..01354f1e --- /dev/null +++ b/src/api-impl/com/android/org/conscrypt/SSLParametersImpl.java @@ -0,0 +1,4 @@ +package com.android.org.conscrypt; + +public class SSLParametersImpl { +} diff --git a/src/api-impl/meson.build b/src/api-impl/meson.build index df21f30b..d7f6bb21 100644 --- a/src/api-impl/meson.build +++ b/src/api-impl/meson.build @@ -7,6 +7,7 @@ hax_jar = jar('hax', [ 'android/animation/AnimatorSet.java', 'android/animation/LayoutTransition.java', 'android/animation/ObjectAnimator.java', + 'android/animation/StateListAnimator.java', 'android/animation/TimeInterpolator.java', 'android/animation/TypeEvaluator.java', 'android/animation/ValueAnimator.java', @@ -20,6 +21,7 @@ hax_jar = jar('hax', [ 'android/app/AlarmManager.java', 'android/app/AlertDialog.java', 'android/app/Application.java', + 'android/app/AppOpsManager.java', 'android/app/Dialog.java', 'android/app/DownloadManager.java', 'android/app/Fragment.java', @@ -28,6 +30,7 @@ hax_jar = jar('hax', [ 'android/app/IntentService.java', 'android/app/KeyguardManager.java', 'android/app/NativeActivity.java', + 'android/app/Notification.java', 'android/app/NotificationManager.java', 'android/app/PendingIntent.java', 'android/app/ProgressDialog.java', @@ -38,6 +41,10 @@ hax_jar = jar('hax', [ 'android/app/UiModeManager.java', 'android/app/WallpaperManager.java', 'android/app/admin/DevicePolicyManager.java', + 'android/app/job/JobScheduler.java', + 'android/app/job/JobService.java', + 'android/appwidget/AppWidgetManager.java', + 'android/bluetooth/BluetoothManager.java', 'android/content/ActivityNotFoundException.java', 'android/content/BroadcastReceiver.java', 'android/content/ClipboardManager.java', @@ -53,12 +60,15 @@ hax_jar = jar('hax', [ 'android/content/Intent.java', 'android/content/IntentSender.java', 'android/content/OperationApplicationException.java', + 'android/content/RestrictionsManager.java', + 'android/content/UriMatcher.java', 'android/content/pm/ActivityInfo.java', 'android/content/pm/ApplicationInfo.java', 'android/content/pm/ComponentInfo.java', 'android/content/pm/ConfigurationInfo.java', 'android/content/pm/FeatureInfo.java', 'android/content/pm/InstrumentationInfo.java', + 'android/content/pm/LauncherApps.java', 'android/content/pm/ManifestDigest.java', 'android/content/pm/PackageInfo.java', 'android/content/pm/PackageItemInfo.java', @@ -165,11 +175,17 @@ hax_jar = jar('hax', [ 'android/graphics/drawable/GradientDrawable.java', 'android/graphics/drawable/InsetDrawable.java', 'android/graphics/drawable/LayerDrawable.java', + 'android/graphics/drawable/RippleDrawable.java', 'android/graphics/drawable/Animatable.java', 'android/graphics/drawable/ScaleDrawable.java', + 'android/graphics/drawable/ShapeDrawable.java', 'android/graphics/drawable/StateListDrawable.java', + 'android/graphics/drawable/shapes/OvalShape.java', + 'android/graphics/drawable/shapes/Shape.java', + 'android/hardware/camera2/CameraManager.java', 'android/hardware/display/DisplayManager.java', 'android/hardware/input/InputManager.java', + 'android/hardware/ConsumerIrManager.java', 'android/hardware/SensorEvent.java', 'android/hardware/SensorEventListener.java', 'android/hardware/Sensor.java', @@ -180,14 +196,20 @@ hax_jar = jar('hax', [ 'android/location/LocationListener.java', 'android/location/LocationManager.java', 'android/Manifest.java', + 'android/media/AudioAttributes.java', 'android/media/AudioManager.java', 'android/media/AudioTrack.java', 'android/media/MediaPlayer.java', 'android/media/MediaRouter.java', 'android/media/SoundPool.java', + 'android/media/projection/MediaProjectionManager.java', + 'android/media/session/MediaSessionManager.java', + 'android/media/tv/TvInputManager.java', 'android/net/ConnectivityManager.java', 'android/net/NetworkInfo.java', 'android/net/Uri.java', + 'android/net/http/X509TrustManagerExtensions.java', + 'android/net/nsd/NsdManager.java', 'android/net/wifi/WifiManager.java', 'android/net/wifi/p2p/WifiP2pManager.java', 'android/nfc/NfcManager.java', @@ -198,6 +220,8 @@ hax_jar = jar('hax', [ 'android/opengl/GLSurfaceView.java', 'android/opengl/Matrix.java', 'android/os/AsyncTask.java', + 'android/os/BaseBundle.java', + 'android/os/BatteryManager.java', 'android/os/Binder.java', 'android/os/Build.java', 'android/os/Bundle.java', @@ -227,13 +251,16 @@ hax_jar = jar('hax', [ 'android/os/SystemClock.java', 'android/os/Trace.java', 'android/os/UserHandle.java', + 'android/os/UserManager.java', 'android/os/storage/StorageManager.java', 'android/preference/PreferenceManager.java', + 'android/print/PrintManager.java', 'android/provider/Settings.java', 'android/R.java', 'android/support/multidex/MultiDexApplication.java', 'android/support/v4/app/FragmentActivity.java', 'android/support/v7/app/AppCompatActivity.java', + 'android/telecom/TelecomManager.java', 'android/telephony/PhoneStateListener.java', 'android/telephony/TelephonyManager.java', 'android/text/ClipboardManager.java', @@ -243,7 +270,9 @@ hax_jar = jar('hax', [ 'android/text/InputFilter.java', 'android/text/Layout.java', 'android/text/NoCopySpan.java', + 'android/text/Selection.java', 'android/text/Spannable.java', + 'android/text/SpannableStringBuilder.java', 'android/text/SpannableStringInternal.java', 'android/text/SpannableString.java', 'android/text/Spanned.java', @@ -255,8 +284,11 @@ hax_jar = jar('hax', [ 'android/text/TextDirectionHeuristics.java', 'android/text/TextUtils.java', 'android/text/TextWatcher.java', + 'android/text/method/KeyListener.java', + 'android/text/method/NumberKeyListener.java', 'android/text/method/PasswordTransformationMethod.java', 'android/text/method/TransformationMethod.java', + 'android/text/style/CharacterStyle.java', 'android/text/util/Linkify.java', 'android/util/AndroidException.java', 'android/util/AndroidRuntimeException.java', @@ -317,11 +349,13 @@ hax_jar = jar('hax', [ 'android/view/ViewGroup.java', 'android/view/View.java', 'android/view/ViewManager.java', + 'android/view/ViewOutlineProvider.java', 'android/view/ViewParent.java', 'android/view/ViewPropertyAnimator.java', 'android/view/ViewStub.java', 'android/view/ViewTreeObserver.java', 'android/view/Window.java', + 'android/view/WindowInsets.java', 'android/view/WindowManagerImpl.java', 'android/view/WindowManager.java', 'android/view/animation/AccelerateDecelerateInterpolator.java', @@ -330,12 +364,16 @@ hax_jar = jar('hax', [ 'android/view/animation/AnimationUtils.java', 'android/view/animation/LinearInterpolator.java', 'android/view/accessibility/AccessibilityManager.java', + 'android/view/accessibility/AccessibilityNodeInfo.java', + 'android/view/accessibility/CaptioningManager.java', 'android/view/textservice/TextServicesManager.java', 'android/webkit/DownloadListener.java', 'android/webkit/MimeTypeMap.java', 'android/webkit/WebSettings.java', 'android/webkit/WebView.java', 'android/webkit/WebViewClient.java', + 'android/widget/AbsListView.java', + 'android/widget/AbsSpinner.java', 'android/widget/Adapter.java', 'android/widget/AdapterView.java', 'android/widget/ArrayAdapter.java', @@ -372,7 +410,11 @@ hax_jar = jar('hax', [ 'com/android/internal/R.java', 'com/android/internal/util/ArrayUtils.java', 'com/android/internal/util/FastXmlSerializer.java', + 'com/android/internal/util/GrowingArrayUtils.java', 'com/android/internal/util/XmlUtils.java', + 'com/android/org/conscrypt/OpenSSLSocketImpl.java', + 'com/android/org/conscrypt/OpenSSLSocketFactoryImpl.java', + 'com/android/org/conscrypt/SSLParametersImpl.java', 'com/google/android/gles_jni/EGLImpl.java', 'com/google/android/gles_jni/GLImpl.java', 'com/google/android/vending/expansion/downloader/IDownloaderClient.java',