diff --git a/src/api-impl-jni/generated_headers/android_text_Layout.h b/src/api-impl-jni/generated_headers/android_text_Layout.h index ce6c339b..de727d5a 100644 --- a/src/api-impl-jni/generated_headers/android_text_Layout.h +++ b/src/api-impl-jni/generated_headers/android_text_Layout.h @@ -47,6 +47,22 @@ JNIEXPORT jint JNICALL Java_android_text_Layout_native_1get_1height JNIEXPORT jint JNICALL Java_android_text_Layout_native_1get_1line_1count (JNIEnv *, jobject, jlong); +/* + * Class: android_text_Layout + * Method: native_get_line_start + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_android_text_Layout_native_1get_1line_1start + (JNIEnv *, jobject, jlong, jint); + +/* + * Class: android_text_Layout + * Method: native_get_line_end + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_android_text_Layout_native_1get_1line_1end + (JNIEnv *, jobject, jlong, jint); + /* * Class: android_text_Layout * Method: native_get_line_top diff --git a/src/api-impl-jni/graphics/android_graphics_Matrix.c b/src/api-impl-jni/graphics/android_graphics_Matrix.c index 415dc37a..de869570 100644 --- a/src/api-impl-jni/graphics/android_graphics_Matrix.c +++ b/src/api-impl-jni/graphics/android_graphics_Matrix.c @@ -267,3 +267,11 @@ JNIEXPORT void JNICALL Java_android_graphics_Matrix_native_1setValues(JNIEnv *en graphene_matrix_init_from_float(matrix, *values4x4); (*env)->ReleaseFloatArrayElements(env, values_ref, values, 0); } + +JNIEXPORT void JNICALL Java_android_graphics_Matrix_native_1setRotate__JFFF(JNIEnv *env, jclass class, jlong matrix_ptr, jfloat degrees, jfloat px, jfloat py) +{ + graphene_matrix_t *matrix = (graphene_matrix_t *)_PTR(matrix_ptr); + graphene_matrix_init_translate(matrix, &GRAPHENE_POINT3D_INIT(-px, -py, 0)); + graphene_matrix_rotate_z(matrix, degrees); + graphene_matrix_translate(matrix, &GRAPHENE_POINT3D_INIT(px, py, 0)); +} diff --git a/src/api-impl-jni/text/android_text_Layout.c b/src/api-impl-jni/text/android_text_Layout.c index d8be648a..bb6ab23c 100644 --- a/src/api-impl-jni/text/android_text_Layout.c +++ b/src/api-impl-jni/text/android_text_Layout.c @@ -47,6 +47,25 @@ JNIEXPORT jint JNICALL Java_android_text_Layout_native_1get_1line_1count(JNIEnv return pango_layout_get_line_count(pango_layout); } +JNIEXPORT jint JNICALL Java_android_text_Layout_native_1get_1line_1start(JNIEnv *env, jobject object, jlong layout, jint line) +{ + PangoLayout *pango_layout = _PTR(layout); + PangoLayoutIter *pango_iter = pango_layout_get_iter(pango_layout); + while (line--) + pango_layout_iter_next_line(pango_iter); + return pango_layout_iter_get_index(pango_iter); +} + +JNIEXPORT jint JNICALL Java_android_text_Layout_native_1get_1line_1end(JNIEnv *env, jobject object, jlong layout, jint line) +{ + PangoLayout *pango_layout = _PTR(layout); + PangoLayoutIter *pango_iter = pango_layout_get_iter(pango_layout); + while (line--) + pango_layout_iter_next_line(pango_iter); + pango_layout_iter_next_line(pango_iter); + return pango_layout_iter_get_index(pango_iter); +} + static void get_line_extends(PangoLayout *pango_layout, int line, PangoRectangle *logical_rect) { PangoRectangle ink_rect; PangoLayoutIter *pango_iter = pango_layout_get_iter(pango_layout); diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index 66028336..8fa829df 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -134,7 +134,7 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.view.onTouchEvent = _METHOD(handle_cache.view.class, "onTouchEvent", "(Landroid/view/MotionEvent;)Z"); handle_cache.view.dispatchTouchEvent = _METHOD(handle_cache.view.class, "dispatchTouchEvent", "(Landroid/view/MotionEvent;)Z"); handle_cache.view.onInterceptTouchEvent = _METHOD(handle_cache.view.class, "onInterceptTouchEvent", "(Landroid/view/MotionEvent;)Z"); - handle_cache.view.layoutInternal = _METHOD(handle_cache.view.class, "layoutInternal", "(II)Z"); + handle_cache.view.layoutInternal = _METHOD(handle_cache.view.class, "layoutInternal", "(II)V"); handle_cache.view.measure = _METHOD(handle_cache.view.class, "measure", "(II)V"); handle_cache.view.performLongClick = _METHOD(handle_cache.view.class, "performLongClick", "(FF)Z"); handle_cache.view.getId = _METHOD(handle_cache.view.class, "getId", "()I"); diff --git a/src/api-impl-jni/views/AndroidLayout.c b/src/api-impl-jni/views/AndroidLayout.c index 8ae56687..fea5bdf7 100644 --- a/src/api-impl-jni/views/AndroidLayout.c +++ b/src/api-impl-jni/views/AndroidLayout.c @@ -70,18 +70,9 @@ static void android_layout_allocate(GtkLayoutManager *layout_manager, GtkWidget height = layout->real_height; } - jboolean layoutChanged = (*env)->CallBooleanMethod(env, layout->view, handle_cache.view.layoutInternal, width, height); + (*env)->CallVoidMethod(env, layout->view, handle_cache.view.layoutInternal, width, height); if((*env)->ExceptionCheck(env)) (*env)->ExceptionDescribe(env); - if (!layoutChanged) { // No change in layout. Reapply the current allocations - for (GtkWidget *child = gtk_widget_get_first_child(widget); child; child = gtk_widget_get_next_sibling(child)) { - graphene_matrix_t transform_matrix; - if (gtk_widget_compute_transform(child, widget, &transform_matrix)) { - GskTransform *transform = gsk_transform_matrix(NULL, &transform_matrix); - gtk_widget_allocate(child, gtk_widget_get_width(child), gtk_widget_get_height(child), gtk_widget_get_baseline(child), transform); - } - } - } } static GtkSizeRequestMode android_layout_get_request_mode(GtkLayoutManager *layout_manager, GtkWidget *widget) diff --git a/src/api-impl/android/app/Activity.java b/src/api-impl/android/app/Activity.java index ee5b44f5..19d41aa0 100644 --- a/src/api-impl/android/app/Activity.java +++ b/src/api-impl/android/app/Activity.java @@ -27,6 +27,7 @@ import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerImpl; +import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -89,15 +90,18 @@ public class Activity extends ContextThemeWrapper implements Window.Callback, La Uri uri = uriString != null ? Uri.parse(uriString) : null; if (className == null) { for (PackageParser.Activity activity: pkg.activities) { + boolean isDefault = false; for (PackageParser.IntentInfo intent: activity.intents) { Slog.i(TAG, intent.toString()); if ((uri == null && intent.hasCategory("android.intent.category.LAUNCHER")) || (uri != null && intent.hasDataScheme(uri.getScheme()))) { className = activity.className; - break; + isDefault = intent.hasCategory("android.intent.category.DEFAULT"); + if (isDefault) + break; } } - if (className != null) + if (isDefault) break; } } else { @@ -334,6 +338,19 @@ public class Activity extends ContextThemeWrapper implements Window.Callback, La } } else if (FILE_CHOOSER_ACTIONS.contains(intent.getAction())) { nativeFileChooser(FILE_CHOOSER_ACTIONS.indexOf(intent.getAction()), intent.getType(), intent.getStringExtra("android.intent.extra.TITLE"), requestCode); + } else if ("android.intent.action.INSTALL_PACKAGE".equals(intent.getAction())) { + try { + Process p = new ProcessBuilder("/usr/bin/env", "android-translation-layer", "--install", intent.getData().getPath()).start(); + int exitValue = p.waitFor(); + if (exitValue == 0) { + onActivityResult(requestCode, -1 /*RESULT_OK*/, new Intent()); + } else { + onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent()); + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent()); + } } else { Slog.i(TAG, "startActivityForResult: intent was not handled. Calling onActivityResult(RESULT_CANCELED)."); onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent()); diff --git a/src/api-impl/android/app/ActivityOptions.java b/src/api-impl/android/app/ActivityOptions.java new file mode 100644 index 00000000..245fca21 --- /dev/null +++ b/src/api-impl/android/app/ActivityOptions.java @@ -0,0 +1,16 @@ +package android.app; + +import android.os.Bundle; +import android.util.Pair; +import android.view.View; + +public class ActivityOptions { + + public static ActivityOptions makeSceneTransitionAnimation(Activity activity, Pair... pairs) { + return new ActivityOptions(); + } + + public Bundle toBundle() { + return null; + } +} diff --git a/src/api-impl/android/app/AppOpsManager.java b/src/api-impl/android/app/AppOpsManager.java index a6775d2c..57a65da2 100644 --- a/src/api-impl/android/app/AppOpsManager.java +++ b/src/api-impl/android/app/AppOpsManager.java @@ -1,4 +1,12 @@ package android.app; public class AppOpsManager { + + public static String permissionToOp(String permission) { + return permission; + } + + public int noteProxyOpNoThrow(String op, String pkg) { + return 0; + } } diff --git a/src/api-impl/android/app/PendingIntent.java b/src/api-impl/android/app/PendingIntent.java index 3710b696..33dd358e 100644 --- a/src/api-impl/android/app/PendingIntent.java +++ b/src/api-impl/android/app/PendingIntent.java @@ -3,8 +3,10 @@ package android.app; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.os.Bundle; +import android.os.Parcelable; -public class PendingIntent { +public class PendingIntent implements Parcelable { private int requestCode; Intent intent; @@ -25,6 +27,17 @@ public class PendingIntent { public void send(Context context, int code, Intent intent) {} + public void send() { + Context context = Context.this_application; + if (type == 0) { // type Activity + context.startActivity(intent); + } else if (type == 1) { // type Service + context.startService(intent); + } else if (type == 2) { // type Broadcast + context.sendBroadcast(intent); + } + } + public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags) { return new PendingIntent(requestCode, intent, 0); } @@ -33,6 +46,10 @@ public class PendingIntent { return new PendingIntent(requestCode, intent, 1); } + public static PendingIntent getActivities(Context context, int requestCode, Intent[] intents, int flags, Bundle options) { + return new PendingIntent(requestCode, intents[0], 0); + } + public String toString() { return "PendingIntent [requestCode=" + requestCode + ", intent=" + intent + ", type=" + new String[] { "activity", "service", "broadcast" }[type] + "]"; diff --git a/src/api-impl/android/app/job/JobInfo.java b/src/api-impl/android/app/job/JobInfo.java index 12e23720..4ea88201 100644 --- a/src/api-impl/android/app/job/JobInfo.java +++ b/src/api-impl/android/app/job/JobInfo.java @@ -1,6 +1,7 @@ package android.app.job; import android.content.ComponentName; +import android.os.PersistableBundle; public class JobInfo { @@ -21,6 +22,26 @@ public class JobInfo { return this; } + public Builder setExtras(PersistableBundle extras) { + return this; + } + + public Builder setRequiresCharging(boolean requiresCharging) { + return this; + } + + public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { + return this; + } + + public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) { + return this; + } + + public Builder setPersisted(boolean persisted) { + return this; + } + public JobInfo build() { return new JobInfo(); } diff --git a/src/api-impl/android/app/job/JobScheduler.java b/src/api-impl/android/app/job/JobScheduler.java index 2c9bba9c..22225937 100644 --- a/src/api-impl/android/app/job/JobScheduler.java +++ b/src/api-impl/android/app/job/JobScheduler.java @@ -17,4 +17,8 @@ public class JobScheduler { public int enqueue(JobInfo job, JobWorkItem work) { return 1; //RESULT_SUCCESS } + + public int schedule(JobInfo job) { + return 1; //RESULT_SUCCESS + } } diff --git a/src/api-impl/android/content/Context.java b/src/api-impl/android/content/Context.java index 206200c9..97926655 100644 --- a/src/api-impl/android/content/Context.java +++ b/src/api-impl/android/content/Context.java @@ -60,6 +60,8 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.security.Provider; +import java.security.Security; import java.util.HashMap; import java.util.Map; @@ -125,6 +127,12 @@ public class Context extends Object { application_info.nativeLibraryDir = (new File(Environment.getExternalStorageDirectory(), "lib")).getAbsolutePath(); application_info.sourceDir = native_get_apk_path(); package_manager = new PackageManager(); + + Provider provider = new Provider("AndroidKeyStore", 1.0, "Android KeyStore provider") {}; + provider.put("KeyStore.AndroidKeyStore", "android.security.keystore.AndroidKeyStore"); + Security.addProvider(provider); + + r.applyPackageQuirks(application_info.packageName); } private static native String native_get_apk_path(); @@ -237,6 +245,8 @@ public class Context extends Object { } public final Object getSystemService(Class serviceClass) throws InstantiationException, IllegalAccessException, InvocationTargetException { + if (serviceClass == LayoutInflater.class) + return layout_inflater; return serviceClass.getConstructors()[0].newInstance(); } @@ -698,4 +708,13 @@ public class Context extends Object { public Drawable getWallpaper() { return null; } + + public String[] databaseList() { + File databaseDir = new File(getDataDirFile(), "databases"); + if (databaseDir.exists()) { + return databaseDir.list(); + } else { + return new String[0]; + } + } } diff --git a/src/api-impl/android/content/Intent.java b/src/api-impl/android/content/Intent.java index 17fbe922..13546c80 100644 --- a/src/api-impl/android/content/Intent.java +++ b/src/api-impl/android/content/Intent.java @@ -1,8 +1,13 @@ package android.content; +import android.content.pm.PackageManager; +import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; +import android.util.AttributeSet; +import org.xmlpull.v1.XmlPullParser; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -378,4 +383,12 @@ public class Intent implements Parcelable { public int filterHashCode() { return 0; } + + public static Intent parseIntent(Resources res, XmlPullParser parser, AttributeSet attrs) { + return new Intent(); + } + + public ComponentName resolveActivity(PackageManager pm) { + return component; + } } diff --git a/src/api-impl/android/content/IntentFilter.java b/src/api-impl/android/content/IntentFilter.java index 7d4fae06..9c677c56 100644 --- a/src/api-impl/android/content/IntentFilter.java +++ b/src/api-impl/android/content/IntentFilter.java @@ -52,6 +52,10 @@ public class IntentFilter { dataSchemes.add(dataScheme); } + public void addDataAuthority(String host, String port) {} + + public void addDataPath(String path, int type) {} + public boolean hasDataScheme(String dataScheme) { return dataSchemes.contains(dataScheme); } @@ -73,6 +77,17 @@ public class IntentFilter { } public int match(String action, String type, String scheme, Uri data, Set categories, String logTag) { - return -1/*NO_MATCH_TYPE*/; + int ret = 0; + if (!matchAction(action)) { + ret = -3/*NO_MATCH_ACTION*/; + } + if (scheme == null) { + ret = 0x00100000/*MATCH_CATEGORY_EMPTY*/ | 0x00008000/*MATCH_ADJUSTMENT_NORMAL*/; + } else if (hasDataScheme(scheme)) { + ret = 0x00200000/*MATCH_CATEGORY_SCHEME*/ | 0x00008000/*MATCH_ADJUSTMENT_NORMAL*/; + } else { + ret = -2/*NO_MATCH_DATA*/; + } + return ret; } } diff --git a/src/api-impl/android/content/pm/PackageInstaller.java b/src/api-impl/android/content/pm/PackageInstaller.java new file mode 100644 index 00000000..47076b53 --- /dev/null +++ b/src/api-impl/android/content/pm/PackageInstaller.java @@ -0,0 +1,11 @@ +package android.content.pm; + +import java.util.Collections; +import java.util.List; + +public class PackageInstaller { + + public List getMySessions() { + return Collections.emptyList(); + } +} diff --git a/src/api-impl/android/content/pm/PackageManager.java b/src/api-impl/android/content/pm/PackageManager.java index 1f9daf61..81f3363b 100644 --- a/src/api-impl/android/content/pm/PackageManager.java +++ b/src/api-impl/android/content/pm/PackageManager.java @@ -1385,7 +1385,11 @@ public class PackageManager { * @see #GET_UNINSTALLED_PACKAGES */ public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException { - return PackageParser.generatePackageInfo(Context.pkg, new int[0], flags, 0, 0, new HashSet<>(), new PackageUserState()); + if (packageName.equals(Context.pkg.packageName)) { + return PackageParser.generatePackageInfo(Context.pkg, new int[0], flags, 0, 0, new HashSet<>(), new PackageUserState()); + } else { + throw new NameNotFoundException(); + } } /** @@ -1820,15 +1824,15 @@ public class PackageManager { * @see #PERMISSION_DENIED */ public int checkPermission(String permName, String pkgName) { + if (permName != null && permName.endsWith(".DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION")) + return PERMISSION_GRANTED; + switch (permName) { // TODO: we shouldn't just automatically grant these once we have bubblewrap set up // for now, the app can access anything it wants, so no point telling it otherwise case "android.permission.WRITE_EXTERNAL_STORAGE": case "android.permission.READ_EXTERNAL_STORAGE": case "com.google.android.c2dm.permission.SEND": - case "com.fsck.k9.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION": - case "net.thunderbird.android.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION": - case "de.danoeh.antennapod.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION": return PERMISSION_GRANTED; default: System.out.println("PackageManager.checkPermission: >" + permName + "< not handled\n"); @@ -2094,7 +2098,7 @@ public class PackageManager { * that are available on the system, or null if there are none(!!). */ public FeatureInfo[] getSystemAvailableFeatures() { - return null; + return new FeatureInfo[0]; } /** @@ -3454,4 +3458,8 @@ public class PackageManager { // TODO: This should be shared with Installer's knowledge of user directory return Environment.getDataDirectory().toString() + "/user/" + userId + "/" + packageName; } + + public PackageInstaller getPackageInstaller() { + return new PackageInstaller(); + } } diff --git a/src/api-impl/android/content/pm/PackageParser.java b/src/api-impl/android/content/pm/PackageParser.java index 50805d42..935427cf 100644 --- a/src/api-impl/android/content/pm/PackageParser.java +++ b/src/api-impl/android/content/pm/PackageParser.java @@ -492,7 +492,7 @@ public class PackageParser { } public Package parsePackage(File sourceFile, String destCodePath, - DisplayMetrics metrics, int flags) { /* + DisplayMetrics metrics, int flags) { mParseError = PackageManager.INSTALL_SUCCEEDED; mArchiveSourcePath = sourceFile.getPath(); @@ -521,7 +521,7 @@ public class PackageParser { boolean assetError = true; try { assmgr = new AssetManager(); - int cookie = assmgr.addAssetPath(mArchiveSourcePath); + int cookie = 1; assmgr.addAssetPath(mArchiveSourcePath); if (cookie != 0) { res = new Resources(assmgr, metrics, null); assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -583,8 +583,6 @@ public class PackageParser { pkg.mSignatures = null; return pkg; - */ - return null; } /** diff --git a/src/api-impl/android/content/res/Resources.java b/src/api-impl/android/content/res/Resources.java index c29bbca9..9175645b 100644 --- a/src/api-impl/android/content/res/Resources.java +++ b/src/api-impl/android/content/res/Resources.java @@ -109,7 +109,7 @@ public class Resources { /*package*/ final Object mAccessLock = new Object(); /*package*/ final Configuration mTmpConfig = new Configuration(); /*package*/ TypedValue mTmpValue = new TypedValue(); - /*package*/ final Map> mDrawableCache = new HashMap>(0); + /*package*/ Object mDrawableCache = new HashMap>(0); private final ConfigurationBoundResourceCache mComplexColorCache = new ConfigurationBoundResourceCache<>(this); /*package*/ final LongSparseArray> mColorDrawableCache = new LongSparseArray>(0); /*package*/ boolean mPreloading; @@ -213,6 +213,19 @@ public class Resources { // assets.ensureStringBlocks(); } + public void applyPackageQuirks(String packageName) { + // F-Droid expects mDrawableCache to be a ThemedResourceCache while most other apps expect a Map + if ("org.fdroid.fdroid".equals(packageName)) { + mDrawableCache = new ThemedResourceCache() { + @Override + protected boolean shouldInvalidateEntry(Drawable entry, int configChanges) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'shouldInvalidateEntry'"); + } + }; + } + } + /** * Return a global shared Resources object that provides access to only * system resources (no application resources), and is not configured for diff --git a/src/api-impl/android/graphics/Bitmap.java b/src/api-impl/android/graphics/Bitmap.java index 11854fb8..560bedbf 100644 --- a/src/api-impl/android/graphics/Bitmap.java +++ b/src/api-impl/android/graphics/Bitmap.java @@ -217,6 +217,10 @@ public final class Bitmap { public void setPremultiplied(boolean premultiplied) {} + public Bitmap extractAlpha() { + return this.copy(config, mutable); + } + @SuppressWarnings("deprecation") @Override protected void finalize() throws Throwable { diff --git a/src/api-impl/android/graphics/Canvas.java b/src/api-impl/android/graphics/Canvas.java index 0d792c72..59b6f414 100644 --- a/src/api-impl/android/graphics/Canvas.java +++ b/src/api-impl/android/graphics/Canvas.java @@ -482,6 +482,10 @@ public class Canvas { return false; } + public boolean clipRect(RectF rect, Region.Op op) { + return false; + } + public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean includeCenter, Paint paint) {} public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) {} diff --git a/src/api-impl/android/graphics/Color.java b/src/api-impl/android/graphics/Color.java index 39d28ef9..c4c853c7 100644 --- a/src/api-impl/android/graphics/Color.java +++ b/src/api-impl/android/graphics/Color.java @@ -158,4 +158,25 @@ public class Color { } hsv[2] = max; } + + public static int HSVToColor(float[] hsv) { + float h = hsv[0]; + float s = hsv[1]; + float v_ = hsv[2]; + int hi = (int)Math.floor(h / 60) % 6; + float f = (h / 60 - (float)Math.floor(h / 60)) * 6; + int p = (int)(v_ * (1 - s) * 255.f + 0.5f); + int q = (int)(v_ * (1 - f * s) * 255.f + 0.5f); + int t = (int)(v_ * (1 - (1 - f) * s) * 255.f + 0.5f); + int v = (int)(v_ * 255.f + 0.5f); + switch (hi) { + case 0: return Color.rgb(v, t, p); + case 1: return Color.rgb(q, v, p); + case 2: return Color.rgb(p, v, t); + case 3: return Color.rgb(p, q, v); + case 4: return Color.rgb(t, p, v); + case 5: return Color.rgb(v, p, q); + } + return 0; + } } diff --git a/src/api-impl/android/graphics/GskCanvas.java b/src/api-impl/android/graphics/GskCanvas.java index b32d7f85..ada39020 100644 --- a/src/api-impl/android/graphics/GskCanvas.java +++ b/src/api-impl/android/graphics/GskCanvas.java @@ -53,7 +53,8 @@ public class GskCanvas extends Canvas { @Override public void drawPath(Path path, Paint paint) { - native_drawPath(snapshot, path.getGskPath(), paint != null ? paint.paint : default_paint.paint); + if (path != null) + native_drawPath(snapshot, path.getGskPath(), paint != null ? paint.paint : default_paint.paint); } @Override diff --git a/src/api-impl/android/graphics/Path.java b/src/api-impl/android/graphics/Path.java index 40735507..1f355bd4 100644 --- a/src/api-impl/android/graphics/Path.java +++ b/src/api-impl/android/graphics/Path.java @@ -191,6 +191,12 @@ public class Path { addPath(src); } + public void offset(float dx, float dy) { + Matrix matrix = new Matrix(); + matrix.setTranslate(dx, dy); + transform(matrix); + } + @SuppressWarnings("deprecation") @Override protected void finalize() throws Throwable { diff --git a/src/api-impl/android/graphics/drawable/Drawable.java b/src/api-impl/android/graphics/drawable/Drawable.java index 50ff767f..0c6dab7b 100644 --- a/src/api-impl/android/graphics/drawable/Drawable.java +++ b/src/api-impl/android/graphics/drawable/Drawable.java @@ -24,6 +24,7 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.LayoutDirection; import android.util.TypedValue; public class Drawable { @@ -323,6 +324,10 @@ public class Drawable { public void setHotspot(float x, float y) {} + public int getLayoutDirection() { + return LayoutDirection.LTR; + } + protected static native long native_paintable_from_path(String path); protected native long native_constructor(); protected native void native_invalidate(long paintable); diff --git a/src/api-impl/android/graphics/drawable/Icon.java b/src/api-impl/android/graphics/drawable/Icon.java index 28a22892..ba7ff924 100644 --- a/src/api-impl/android/graphics/drawable/Icon.java +++ b/src/api-impl/android/graphics/drawable/Icon.java @@ -1,8 +1,14 @@ package android.graphics.drawable; +import android.graphics.Bitmap; + public class Icon { public static Icon createWithResource(String packageName, int resourceId) { return null; } + + public static Icon createWithBitmap(Bitmap bitmap) { + return null; + } } diff --git a/src/api-impl/android/net/ConnectivityManager.java b/src/api-impl/android/net/ConnectivityManager.java index e7dd7892..6994b29b 100644 --- a/src/api-impl/android/net/ConnectivityManager.java +++ b/src/api-impl/android/net/ConnectivityManager.java @@ -2,6 +2,8 @@ package android.net; import android.os.Handler; +class NetworkCapabilities {} + public class ConnectivityManager { public class NetworkCallback { @@ -19,6 +21,8 @@ public class ConnectivityManager { public native void registerNetworkCallback(NetworkRequest request, NetworkCallback callback); + public void unregisterNetworkCallback(NetworkCallback callback) {} + public native boolean isActiveNetworkMetered(); protected native boolean nativeGetNetworkAvailable(); @@ -33,4 +37,8 @@ public class ConnectivityManager { public void registerDefaultNetworkCallback(NetworkCallback cb, Handler hdl) {} + public NetworkCapabilities getNetworkCapabilities(Network network) { + return null; + } + } diff --git a/src/api-impl/android/net/http/X509TrustManagerExtensions.java b/src/api-impl/android/net/http/X509TrustManagerExtensions.java index 3be59002..fdaf9122 100644 --- a/src/api-impl/android/net/http/X509TrustManagerExtensions.java +++ b/src/api-impl/android/net/http/X509TrustManagerExtensions.java @@ -1,7 +1,7 @@ package android.net.http; import java.security.cert.X509Certificate; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.net.ssl.X509TrustManager; @@ -12,6 +12,6 @@ public class X509TrustManagerExtensions { public List checkServerTrusted (X509Certificate[] chain, String authType, String host) { - return new ArrayList<>(); + return Arrays.asList(chain); } } diff --git a/src/api-impl/android/os/BaseBundle.java b/src/api-impl/android/os/BaseBundle.java index 06b6042c..c9cd835a 100644 --- a/src/api-impl/android/os/BaseBundle.java +++ b/src/api-impl/android/os/BaseBundle.java @@ -12,7 +12,7 @@ public class BaseBundle { // Invariant - exactly one of mMap / mParcelledData will be null // (except inside a call to unparcel) - /* package */ ArrayMap mMap = null; + /* package */ ArrayMap mMap = new ArrayMap<>(); // Log a message if the value was non-null but not of the expected type void typeWarning(String key, Object value, String className, diff --git a/src/api-impl/android/os/Environment.java b/src/api-impl/android/os/Environment.java index aa08613a..1d2db2e4 100644 --- a/src/api-impl/android/os/Environment.java +++ b/src/api-impl/android/os/Environment.java @@ -799,4 +799,8 @@ public class Environment { public static boolean isExternalStorageLegacy() { return true; } + + public static String getExternalStorageState(File path) { + return Environment.MEDIA_MOUNTED; + } } diff --git a/src/api-impl/android/provider/MediaStore.java b/src/api-impl/android/provider/MediaStore.java new file mode 100644 index 00000000..ed145721 --- /dev/null +++ b/src/api-impl/android/provider/MediaStore.java @@ -0,0 +1,14 @@ +package android.provider; + +import android.net.Uri; + +public class MediaStore { + + public static class Images { + + public static class Media { + + public static final Uri EXTERNAL_CONTENT_URI = Uri.parse("content://media/external/images/media"); + } + } +} diff --git a/src/api-impl/android/provider/Settings.java b/src/api-impl/android/provider/Settings.java index 2d58ecf5..51008722 100644 --- a/src/api-impl/android/provider/Settings.java +++ b/src/api-impl/android/provider/Settings.java @@ -18,12 +18,16 @@ public class Settings { } } public static int getInt(ContentResolver content_resolver, String key) { + return getInt(content_resolver, key, -1); + } + + public static int getInt(ContentResolver content_resolver, String key, int def) { switch (key) { case "limit_ad_tracking": return 1; // obviously, duh default: java.lang.System.out.println("!!!! Settings$Secure.getInt: unknown key: >" + key + "<"); - return -1; + return def; } } } diff --git a/src/api-impl/android/security/keystore/AndroidKeyStore.java b/src/api-impl/android/security/keystore/AndroidKeyStore.java new file mode 100644 index 00000000..2b7e24b7 --- /dev/null +++ b/src/api-impl/android/security/keystore/AndroidKeyStore.java @@ -0,0 +1,120 @@ +package android.security.keystore; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.KeyStoreSpi; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; + +public class AndroidKeyStore extends KeyStoreSpi { + + HashMap map = new HashMap<>(); + + @Override + public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineGetKey'"); + } + + @Override + public Certificate[] engineGetCertificateChain(String alias) { + // TODO Auto-generated method stub + System.out.println("engineGetCertificateChain(" + alias + ") called"); + return new Certificate[0]; + } + + @Override + public Certificate engineGetCertificate(String alias) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineGetCertificate'"); + } + + @Override + public Date engineGetCreationDate(String alias) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineGetCreationDate'"); + } + + @Override + public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) + throws KeyStoreException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineSetKeyEntry'"); + } + + @Override + public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineSetKeyEntry'"); + } + + @Override + public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineSetCertificateEntry'"); + } + + @Override + public void engineDeleteEntry(String alias) throws KeyStoreException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineDeleteEntry'"); + } + + @Override + public Enumeration engineAliases() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineAliases'"); + } + + @Override + public boolean engineContainsAlias(String alias) { + // TODO Auto-generated method stub + System.out.println("engineContainsAlias(" + alias + ") called"); + return map.containsKey(alias); + } + + @Override + public int engineSize() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineSize'"); + } + + @Override + public boolean engineIsKeyEntry(String alias) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineIsKeyEntry'"); + } + + @Override + public boolean engineIsCertificateEntry(String alias) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineIsCertificateEntry'"); + } + + @Override + public String engineGetCertificateAlias(Certificate cert) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineGetCertificateAlias'"); + } + + @Override + public void engineStore(OutputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'engineStore'"); + } + + @Override + public void engineLoad(InputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException { + } + +} diff --git a/src/api-impl/android/text/Layout.java b/src/api-impl/android/text/Layout.java index 2ea11fee..d8aeec5c 100644 --- a/src/api-impl/android/text/Layout.java +++ b/src/api-impl/android/text/Layout.java @@ -80,6 +80,10 @@ public class Layout { return paint.measureText(source, start, end); } + public static float getDesiredWidth(CharSequence source, TextPaint paint) { + return paint.measureText(source, 0, source.length()); + } + public int getLineBaseline(int line) { if (line < 0 || line >= getLineCount()) throw new ArrayIndexOutOfBoundsException(); @@ -155,13 +159,13 @@ public class Layout { public int getLineStart(int line) { if (line < 0 || line >= getLineCount()) throw new ArrayIndexOutOfBoundsException(); - return native_get_line_left(layout, line); + return native_get_line_start(layout, line); } public int getLineEnd(int line) { if (line < 0 || line >= getLineCount()) throw new ArrayIndexOutOfBoundsException(); - return native_get_line_right(layout, line); + return native_get_line_end(layout, line); } public boolean isSpanned() { @@ -238,6 +242,8 @@ public class Layout { protected native int native_get_width(long layout); protected native int native_get_height(long layout); protected native int native_get_line_count(long layout); + protected native int native_get_line_start(long layout, int line); + protected native int native_get_line_end(long layout, int line); protected native int native_get_line_top(long layout, int line); protected native int native_get_line_bottom(long layout, int line); protected native int native_get_line_left(long layout, int line); diff --git a/src/api-impl/android/text/format/DateUtils.java b/src/api-impl/android/text/format/DateUtils.java index 9d096387..8152fc0e 100644 --- a/src/api-impl/android/text/format/DateUtils.java +++ b/src/api-impl/android/text/format/DateUtils.java @@ -44,4 +44,8 @@ public class DateUtils { else return formatDateTime(context, fromMillis, flags) + " - " + formatDateTime(context, toMillis, flags); } + + public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolutionMillis, int flags) { + return new Date(time).toString(); + } } diff --git a/src/api-impl/android/text/style/URLSpan.java b/src/api-impl/android/text/style/URLSpan.java index fc706885..f6a813dd 100644 --- a/src/api-impl/android/text/style/URLSpan.java +++ b/src/api-impl/android/text/style/URLSpan.java @@ -2,5 +2,13 @@ package android.text.style; public class URLSpan { - public URLSpan(String url) {} + private String url; + + public URLSpan(String url) { + this.url = url; + } + + public String getURL() { + return url; + } } diff --git a/src/api-impl/android/util/ArraySet.java b/src/api-impl/android/util/ArraySet.java new file mode 100644 index 00000000..c6283837 --- /dev/null +++ b/src/api-impl/android/util/ArraySet.java @@ -0,0 +1,988 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import android.annotation.Nullable; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * ArraySet is a generic set data structure that is designed to be more memory efficient than a + * traditional {@link java.util.HashSet}. The design is very similar to + * {@link ArrayMap}, with all of the caveats described there. This implementation is + * separate from ArrayMap, however, so the Object array contains only one item for each + * entry in the set (instead of a pair for a mapping). + * + *

Note that this implementation is not intended to be appropriate for data structures + * that may contain large numbers of items. It is generally slower than a traditional + * HashSet, since lookups require a binary search and adds and removes require inserting + * and deleting entries in the array. For containers holding up to hundreds of items, + * the performance difference is not significant, less than 50%.

+ * + *

Because this container is intended to better balance memory use, unlike most other + * standard Java containers it will shrink its array as items are removed from it. Currently + * you have no control over this shrinking -- if you set a capacity and then remove an + * item, it may reduce the capacity to better match the current size. In the future an + * explicit call to set the capacity should turn off this aggressive shrinking behavior.

+ * + *

This structure is NOT thread-safe.

+ */ +public final class ArraySet implements Set { + private static final boolean DEBUG = false; + private static final String TAG = "ArraySet"; + + /** + * The minimum amount by which the capacity of a ArraySet will increase. + * This is tuned to be relatively space-efficient. + */ + private static final int BASE_SIZE = 4; + + /** + * Maximum number of entries to have in array caches. + */ + private static final int CACHE_SIZE = 10; + + /** + * Caches of small array objects to avoid spamming garbage. The cache + * Object[] variable is a pointer to a linked list of array objects. + * The first entry in the array is a pointer to the next array in the + * list; the second entry is a pointer to the int[] hash code array for it. + */ + static Object[] sBaseCache; + static int sBaseCacheSize; + static Object[] sTwiceBaseCache; + static int sTwiceBaseCacheSize; + /** + * Separate locks for each cache since each can be accessed independently of the other without + * risk of a deadlock. + */ + private static final Object sBaseCacheLock = new Object(); + private static final Object sTwiceBaseCacheLock = new Object(); + + private final boolean mIdentityHashCode; + int[] mHashes; + Object[] mArray; + int mSize; + private MapCollections mCollections; + + private int binarySearch(int[] hashes, int hash) { + try { + return ContainerHelpers.binarySearch(hashes, mSize, hash); + } catch (ArrayIndexOutOfBoundsException e) { + throw new ConcurrentModificationException(); + } + } + + + private int indexOf(Object key, int hash) { + final int N = mSize; + + // Important fast case: if nothing is in here, nothing to look for. + if (N == 0) { + return ~0; + } + + int index = binarySearch(mHashes, hash); + + // If the hash code wasn't found, then we have no entry for this key. + if (index < 0) { + return index; + } + + // If the key at the returned index matches, that's what we want. + if (key.equals(mArray[index])) { + return index; + } + + // Search for a matching key after the index. + int end; + for (end = index + 1; end < N && mHashes[end] == hash; end++) { + if (key.equals(mArray[end])) return end; + } + + // Search for a matching key before the index. + for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) { + if (key.equals(mArray[i])) return i; + } + + // Key not found -- return negative value indicating where a + // new entry for this key should go. We use the end of the + // hash chain to reduce the number of array entries that will + // need to be copied when inserting. + return ~end; + } + + private int indexOfNull() { + final int N = mSize; + + // Important fast case: if nothing is in here, nothing to look for. + if (N == 0) { + return ~0; + } + + int index = binarySearch(mHashes, 0); + + // If the hash code wasn't found, then we have no entry for this key. + if (index < 0) { + return index; + } + + // If the key at the returned index matches, that's what we want. + if (null == mArray[index]) { + return index; + } + + // Search for a matching key after the index. + int end; + for (end = index + 1; end < N && mHashes[end] == 0; end++) { + if (null == mArray[end]) return end; + } + + // Search for a matching key before the index. + for (int i = index - 1; i >= 0 && mHashes[i] == 0; i--) { + if (null == mArray[i]) return i; + } + + // Key not found -- return negative value indicating where a + // new entry for this key should go. We use the end of the + // hash chain to reduce the number of array entries that will + // need to be copied when inserting. + return ~end; + } + + private void allocArrays(final int size) { + if (size == (BASE_SIZE * 2)) { + synchronized (sTwiceBaseCacheLock) { + if (sTwiceBaseCache != null) { + final Object[] array = sTwiceBaseCache; + try { + mArray = array; + sTwiceBaseCache = (Object[]) array[0]; + mHashes = (int[]) array[1]; + if (mHashes != null) { + array[0] = array[1] = null; + sTwiceBaseCacheSize--; + if (DEBUG) { + Log.d(TAG, "Retrieving 2x cache " + Arrays.toString(mHashes) + + " now have " + sTwiceBaseCacheSize + " entries"); + } + return; + } + } catch (ClassCastException e) { + } + // Whoops! Someone trampled the array (probably due to not protecting + // their access with a lock). Our cache is corrupt; report and give up. + Slog.wtf(TAG, "Found corrupt ArraySet cache: [0]=" + array[0] + + " [1]=" + array[1]); + sTwiceBaseCache = null; + sTwiceBaseCacheSize = 0; + } + } + } else if (size == BASE_SIZE) { + synchronized (sBaseCacheLock) { + if (sBaseCache != null) { + final Object[] array = sBaseCache; + try { + mArray = array; + sBaseCache = (Object[]) array[0]; + mHashes = (int[]) array[1]; + if (mHashes != null) { + array[0] = array[1] = null; + sBaseCacheSize--; + if (DEBUG) { + Log.d(TAG, "Retrieving 1x cache " + Arrays.toString(mHashes) + + " now have " + sBaseCacheSize + " entries"); + } + return; + } + } catch (ClassCastException e) { + } + // Whoops! Someone trampled the array (probably due to not protecting + // their access with a lock). Our cache is corrupt; report and give up. + Slog.wtf(TAG, "Found corrupt ArraySet cache: [0]=" + array[0] + + " [1]=" + array[1]); + sBaseCache = null; + sBaseCacheSize = 0; + } + } + } + + mHashes = new int[size]; + mArray = new Object[size]; + } + + /** + * Make sure NOT to call this method with arrays that can still be modified. In other + * words, don't pass mHashes or mArray in directly. + */ + private static void freeArrays(final int[] hashes, final Object[] array, final int size) { + if (hashes.length == (BASE_SIZE * 2)) { + synchronized (sTwiceBaseCacheLock) { + if (sTwiceBaseCacheSize < CACHE_SIZE) { + array[0] = sTwiceBaseCache; + array[1] = hashes; + for (int i = size - 1; i >= 2; i--) { + array[i] = null; + } + sTwiceBaseCache = array; + sTwiceBaseCacheSize++; + if (DEBUG) { + Log.d(TAG, "Storing 2x cache " + Arrays.toString(array) + " now have " + + sTwiceBaseCacheSize + " entries"); + } + } + } + } else if (hashes.length == BASE_SIZE) { + synchronized (sBaseCacheLock) { + if (sBaseCacheSize < CACHE_SIZE) { + array[0] = sBaseCache; + array[1] = hashes; + for (int i = size - 1; i >= 2; i--) { + array[i] = null; + } + sBaseCache = array; + sBaseCacheSize++; + if (DEBUG) { + Log.d(TAG, "Storing 1x cache " + Arrays.toString(array) + " now have " + + sBaseCacheSize + " entries"); + } + } + } + } + } + + /** + * Create a new empty ArraySet. The default capacity of an array map is 0, and + * will grow once items are added to it. + */ + public ArraySet() { + this(0, false); + } + + /** + * Create a new ArraySet with a given initial capacity. + */ + public ArraySet(int capacity) { + this(capacity, false); + } + + /** {@hide} */ + public ArraySet(int capacity, boolean identityHashCode) { + mIdentityHashCode = identityHashCode; + if (capacity == 0) { + mHashes = new int[0]; + mArray = new Object[0]; + } else { + allocArrays(capacity); + } + mSize = 0; + } + + /** + * Create a new ArraySet with the mappings from the given ArraySet. + */ + public ArraySet(ArraySet set) { + this(); + if (set != null) { + addAll(set); + } + } + + /** + * Create a new ArraySet with items from the given collection. + */ + public ArraySet(Collection set) { + this(); + if (set != null) { + addAll(set); + } + } + + /** + * Create a new ArraySet with items from the given array + */ + public ArraySet(@Nullable E[] array) { + this(); + if (array != null) { + for (E value : array) { + add(value); + } + } + } + + /** + * Make the array map empty. All storage is released. + */ + @Override + public void clear() { + if (mSize != 0) { + final int[] ohashes = mHashes; + final Object[] oarray = mArray; + final int osize = mSize; + mHashes = new int[0]; + mArray = new Object[0]; + mSize = 0; + freeArrays(ohashes, oarray, osize); + } + if (mSize != 0) { + throw new ConcurrentModificationException(); + } + } + + /** + * Ensure the array map can hold at least minimumCapacity + * items. + */ + public void ensureCapacity(int minimumCapacity) { + final int oSize = mSize; + if (mHashes.length < minimumCapacity) { + final int[] ohashes = mHashes; + final Object[] oarray = mArray; + allocArrays(minimumCapacity); + if (mSize > 0) { + System.arraycopy(ohashes, 0, mHashes, 0, mSize); + System.arraycopy(oarray, 0, mArray, 0, mSize); + } + freeArrays(ohashes, oarray, mSize); + } + if (mSize != oSize) { + throw new ConcurrentModificationException(); + } + } + + /** + * Check whether a value exists in the set. + * + * @param key The value to search for. + * @return Returns true if the value exists, else false. + */ + @Override + public boolean contains(Object key) { + return indexOf(key) >= 0; + } + + /** + * Returns the index of a value in the set. + * + * @param key The value to search for. + * @return Returns the index of the value if it exists, else a negative integer. + */ + public int indexOf(Object key) { + return key == null ? indexOfNull() + : indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode()); + } + + /** + * Return the value at the given index in the array. + * + *

For indices outside of the range 0...size()-1, the behavior is undefined for + * apps targeting {@link android.os.Build.VERSION_CODES#P} and earlier, and an + * {@link ArrayIndexOutOfBoundsException} is thrown for apps targeting + * {@link android.os.Build.VERSION_CODES#Q} and later.

+ * + * @param index The desired index, must be between 0 and {@link #size()}-1. + * @return Returns the value stored at the given index. + */ + public E valueAt(int index) { + if (index >= mSize && /*UtilConfig.sThrowExceptionForUpperArrayOutOfBounds*/true) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + // Check if exception should be thrown outside of the critical path. + throw new ArrayIndexOutOfBoundsException(index); + } + return valueAtUnchecked(index); + } + + /** + * Returns the value at the given index in the array without checking that the index is within + * bounds. This allows testing values at the end of the internal array, outside of the + * [0, mSize) bounds. + * + * @hide + */ + public E valueAtUnchecked(int index) { + return (E) mArray[index]; + } + + /** + * Return true if the array map contains no items. + */ + @Override + public boolean isEmpty() { + return mSize <= 0; + } + + /** + * Adds the specified object to this set. The set is not modified if it + * already contains the object. + * + * @param value the object to add. + * @return {@code true} if this set is modified, {@code false} otherwise. + */ + @Override + public boolean add(E value) { + final int oSize = mSize; + final int hash; + int index; + if (value == null) { + hash = 0; + index = indexOfNull(); + } else { + hash = mIdentityHashCode ? System.identityHashCode(value) : value.hashCode(); + index = indexOf(value, hash); + } + if (index >= 0) { + return false; + } + + index = ~index; + if (oSize >= mHashes.length) { + final int n = oSize >= (BASE_SIZE * 2) ? (oSize + (oSize >> 1)) + : (oSize >= BASE_SIZE ? (BASE_SIZE * 2) : BASE_SIZE); + + if (DEBUG) Log.d(TAG, "add: grow from " + mHashes.length + " to " + n); + + final int[] ohashes = mHashes; + final Object[] oarray = mArray; + allocArrays(n); + + if (oSize != mSize) { + throw new ConcurrentModificationException(); + } + + if (mHashes.length > 0) { + if (DEBUG) Log.d(TAG, "add: copy 0-" + oSize + " to 0"); + System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length); + System.arraycopy(oarray, 0, mArray, 0, oarray.length); + } + + freeArrays(ohashes, oarray, oSize); + } + + if (index < oSize) { + if (DEBUG) { + Log.d(TAG, "add: move " + index + "-" + (oSize - index) + " to " + (index + 1)); + } + System.arraycopy(mHashes, index, mHashes, index + 1, oSize - index); + System.arraycopy(mArray, index, mArray, index + 1, oSize - index); + } + + if (oSize != mSize || index >= mHashes.length) { + throw new ConcurrentModificationException(); + } + + mHashes[index] = hash; + mArray[index] = value; + mSize++; + return true; + } + + /** + * Special fast path for appending items to the end of the array without validation. + * The array must already be large enough to contain the item. + * @hide + */ + public void append(E value) { + final int oSize = mSize; + final int index = mSize; + final int hash = value == null ? 0 + : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode()); + if (index >= mHashes.length) { + throw new IllegalStateException("Array is full"); + } + if (index > 0 && mHashes[index - 1] > hash) { + // Cannot optimize since it would break the sorted order - fallback to add() + if (DEBUG) { + RuntimeException e = new RuntimeException("here"); + Log.w(TAG, "New hash " + hash + + " is before end of array hash " + mHashes[index - 1] + + " at index " + index, e); + } + add(value); + return; + } + + if (oSize != mSize) { + throw new ConcurrentModificationException(); + } + + mSize = index + 1; + mHashes[index] = hash; + mArray[index] = value; + } + + /** + * Perform a {@link #add(Object)} of all values in array + * @param array The array whose contents are to be retrieved. + */ + public void addAll(ArraySet array) { + final int N = array.mSize; + ensureCapacity(mSize + N); + if (mSize == 0) { + if (N > 0) { + System.arraycopy(array.mHashes, 0, mHashes, 0, N); + System.arraycopy(array.mArray, 0, mArray, 0, N); + if (0 != mSize) { + throw new ConcurrentModificationException(); + } + mSize = N; + } + } else { + for (int i = 0; i < N; i++) { + add(array.valueAt(i)); + } + } + } + + /** + * Removes the specified object from this set. + * + * @param object the object to remove. + * @return {@code true} if this set was modified, {@code false} otherwise. + */ + @Override + public boolean remove(Object object) { + final int index = indexOf(object); + if (index >= 0) { + removeAt(index); + return true; + } + return false; + } + + /** Returns true if the array size should be decreased. */ + private boolean shouldShrink() { + return mHashes.length > (BASE_SIZE * 2) && mSize < mHashes.length / 3; + } + + /** + * Returns the new size the array should have. Is only valid if {@link #shouldShrink} returns + * true. + */ + private int getNewShrunkenSize() { + // We don't allow it to shrink smaller than (BASE_SIZE*2) to avoid flapping between that + // and BASE_SIZE. + return mSize > (BASE_SIZE * 2) ? (mSize + (mSize >> 1)) : (BASE_SIZE * 2); + } + + /** + * Remove the key/value mapping at the given index. + * + *

For indices outside of the range 0...size()-1, the behavior is undefined for + * apps targeting {@link android.os.Build.VERSION_CODES#P} and earlier, and an + * {@link ArrayIndexOutOfBoundsException} is thrown for apps targeting + * {@link android.os.Build.VERSION_CODES#Q} and later.

+ * + * @param index The desired index, must be between 0 and {@link #size()}-1. + * @return Returns the value that was stored at this index. + */ + public E removeAt(int index) { + if (index >= mSize && /*UtilConfig.sThrowExceptionForUpperArrayOutOfBounds*/true) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + // Check if exception should be thrown outside of the critical path. + throw new ArrayIndexOutOfBoundsException(index); + } + final int oSize = mSize; + final Object old = mArray[index]; + if (oSize <= 1) { + // Now empty. + if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0"); + clear(); + } else { + final int nSize = oSize - 1; + if (shouldShrink()) { + // Shrunk enough to reduce size of arrays. + final int n = getNewShrunkenSize(); + + if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n); + + final int[] ohashes = mHashes; + final Object[] oarray = mArray; + allocArrays(n); + + if (index > 0) { + if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0"); + System.arraycopy(ohashes, 0, mHashes, 0, index); + System.arraycopy(oarray, 0, mArray, 0, index); + } + if (index < nSize) { + if (DEBUG) { + Log.d(TAG, "remove: copy from " + (index + 1) + "-" + nSize + + " to " + index); + } + System.arraycopy(ohashes, index + 1, mHashes, index, nSize - index); + System.arraycopy(oarray, index + 1, mArray, index, nSize - index); + } + } else { + if (index < nSize) { + if (DEBUG) { + Log.d(TAG, "remove: move " + (index + 1) + "-" + nSize + " to " + index); + } + System.arraycopy(mHashes, index + 1, mHashes, index, nSize - index); + System.arraycopy(mArray, index + 1, mArray, index, nSize - index); + } + mArray[nSize] = null; + } + if (oSize != mSize) { + throw new ConcurrentModificationException(); + } + mSize = nSize; + } + return (E) old; + } + + /** + * Perform a {@link #remove(Object)} of all values in array + * @param array The array whose contents are to be removed. + */ + public boolean removeAll(ArraySet array) { + // TODO: If array is sufficiently large, a marking approach might be beneficial. In a first + // pass, use the property that the sets are sorted by hash to make this linear passes + // (except for hash collisions, which means worst case still n*m), then do one + // collection pass into a new array. This avoids binary searches and excessive memcpy. + final int N = array.mSize; + + // Note: ArraySet does not make thread-safety guarantees. So instead of OR-ing together all + // the single results, compare size before and after. + final int originalSize = mSize; + for (int i = 0; i < N; i++) { + remove(array.valueAt(i)); + } + return originalSize != mSize; + } + + /** + * Removes all values that satisfy the predicate. This implementation avoids using the + * {@link #iterator()}. + * + * @param filter A predicate which returns true for elements to be removed + */ + @Override + public boolean removeIf(Predicate filter) { + if (mSize == 0) { + return false; + } + + // Intentionally not using removeAt() to avoid unnecessary intermediate resizing. + + int replaceIndex = 0; + int numRemoved = 0; + for (int i = 0; i < mSize; ++i) { + if (filter.test((E) mArray[i])) { + numRemoved++; + } else { + if (replaceIndex != i) { + mArray[replaceIndex] = mArray[i]; + mHashes[replaceIndex] = mHashes[i]; + } + replaceIndex++; + } + } + + if (numRemoved == 0) { + return false; + } else if (numRemoved == mSize) { + clear(); + return true; + } + + mSize -= numRemoved; + if (shouldShrink()) { + // Shrunk enough to reduce size of arrays. + final int n = getNewShrunkenSize(); + final int[] ohashes = mHashes; + final Object[] oarray = mArray; + allocArrays(n); + + System.arraycopy(ohashes, 0, mHashes, 0, mSize); + System.arraycopy(oarray, 0, mArray, 0, mSize); + } else { + // Null out values at the end of the array. Not doing it in the loop above to avoid + // writing twice to the same index or writing unnecessarily if the array would have been + // discarded anyway. + for (int i = mSize; i < mArray.length; ++i) { + mArray[i] = null; + } + } + return true; + } + + /** + * Return the number of items in this array map. + */ + @Override + public int size() { + return mSize; + } + + /** + * Performs the given action for all elements in the stored order. This implementation overrides + * the default implementation to avoid using the {@link #iterator()}. + * + * @param action The action to be performed for each element + */ + @Override + public void forEach(Consumer action) { + if (action == null) { + throw new NullPointerException("action must not be null"); + } + + for (int i = 0; i < mSize; ++i) { + action.accept(valueAt(i)); + } + } + + @Override + public Object[] toArray() { + Object[] result = new Object[mSize]; + System.arraycopy(mArray, 0, result, 0, mSize); + return result; + } + + @Override + public T[] toArray(T[] array) { + if (array.length < mSize) { + @SuppressWarnings("unchecked") T[] newArray = + (T[]) Array.newInstance(array.getClass().getComponentType(), mSize); + array = newArray; + } + System.arraycopy(mArray, 0, array, 0, mSize); + if (array.length > mSize) { + array[mSize] = null; + } + return array; + } + + /** + * {@inheritDoc} + * + *

This implementation returns false if the object is not a set, or + * if the sets have different sizes. Otherwise, for each value in this + * set, it checks to make sure the value also exists in the other set. + * If any value doesn't exist, the method returns false; otherwise, it + * returns true. + */ + @Override + public boolean equals(@Nullable Object object) { + if (this == object) { + return true; + } + if (object instanceof Set) { + Set set = (Set) object; + if (size() != set.size()) { + return false; + } + + try { + for (int i = 0; i < mSize; i++) { + E mine = valueAt(i); + if (!set.contains(mine)) { + return false; + } + } + } catch (NullPointerException ignored) { + return false; + } catch (ClassCastException ignored) { + return false; + } + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int[] hashes = mHashes; + int result = 0; + for (int i = 0, s = mSize; i < s; i++) { + result += hashes[i]; + } + return result; + } + + /** + * {@inheritDoc} + * + *

This implementation composes a string by iterating over its values. If + * this set contains itself as a value, the string "(this Set)" + * will appear in its place. + */ + @Override + public String toString() { + if (isEmpty()) { + return "{}"; + } + + StringBuilder buffer = new StringBuilder(mSize * 14); + buffer.append('{'); + for (int i = 0; i < mSize; i++) { + if (i > 0) { + buffer.append(", "); + } + Object value = valueAt(i); + if (value != this) { + buffer.append(value); + } else { + buffer.append("(this Set)"); + } + } + buffer.append('}'); + return buffer.toString(); + } + + // ------------------------------------------------------------------------ + // Interop with traditional Java containers. Not as efficient as using + // specialized collection APIs. + // ------------------------------------------------------------------------ + + private MapCollections getCollection() { + if (mCollections == null) { + mCollections = new MapCollections() { + @Override + protected int colGetSize() { + return mSize; + } + + @Override + protected Object colGetEntry(int index, int offset) { + return mArray[index]; + } + + @Override + protected int colIndexOfKey(Object key) { + return indexOf(key); + } + + @Override + protected int colIndexOfValue(Object value) { + return indexOf(value); + } + + @Override + protected Map colGetMap() { + throw new UnsupportedOperationException("not a map"); + } + + @Override + protected void colPut(E key, E value) { + add(key); + } + + @Override + protected E colSetValue(int index, E value) { + throw new UnsupportedOperationException("not a map"); + } + + @Override + protected void colRemoveAt(int index) { + removeAt(index); + } + + @Override + protected void colClear() { + clear(); + } + }; + } + return mCollections; + } + + /** + * Return an {@link java.util.Iterator} over all values in the set. + * + *

Note: this is a fairly inefficient way to access the array contents, it + * requires generating a number of temporary objects and allocates additional state + * information associated with the container that will remain for the life of the container.

+ */ + @Override + public Iterator iterator() { + return getCollection().getKeySet().iterator(); + } + + /** + * Determine if the array set contains all of the values in the given collection. + * @param collection The collection whose contents are to be checked against. + * @return Returns true if this array set contains a value for every entry + * in collection, else returns false. + */ + @Override + public boolean containsAll(Collection collection) { + Iterator it = collection.iterator(); + while (it.hasNext()) { + if (!contains(it.next())) { + return false; + } + } + return true; + } + + /** + * Perform an {@link #add(Object)} of all values in collection + * @param collection The collection whose contents are to be retrieved. + */ + @Override + public boolean addAll(Collection collection) { + ensureCapacity(mSize + collection.size()); + boolean added = false; + for (E value : collection) { + added |= add(value); + } + return added; + } + + /** + * Remove all values in the array set that exist in the given collection. + * @param collection The collection whose contents are to be used to remove values. + * @return Returns true if any values were removed from the array set, else false. + */ + @Override + public boolean removeAll(Collection collection) { + boolean removed = false; + for (Object value : collection) { + removed |= remove(value); + } + return removed; + } + + /** + * Remove all values in the array set that do not exist in the given collection. + * @param collection The collection whose contents are to be used to determine which + * values to keep. + * @return Returns true if any values were removed from the array set, else false. + */ + @Override + public boolean retainAll(Collection collection) { + boolean removed = false; + for (int i = mSize - 1; i >= 0; i--) { + if (!collection.contains(mArray[i])) { + removeAt(i); + removed = true; + } + } + return removed; + } +} \ No newline at end of file diff --git a/src/api-impl/android/util/TypedValue.java b/src/api-impl/android/util/TypedValue.java index 4d23ec8e..b70bb245 100644 --- a/src/api-impl/android/util/TypedValue.java +++ b/src/api-impl/android/util/TypedValue.java @@ -572,4 +572,8 @@ public class TypedValue { sb.append("}"); return sb.toString(); } + + public int getComplexUnit() { + return (data >> COMPLEX_UNIT_SHIFT) & COMPLEX_UNIT_MASK; + } }; diff --git a/src/api-impl/android/view/View.java b/src/api-impl/android/view/View.java index 90f611dc..6033818a 100644 --- a/src/api-impl/android/view/View.java +++ b/src/api-impl/android/view/View.java @@ -10,6 +10,7 @@ import android.graphics.Canvas; import android.graphics.GskCanvas; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; @@ -847,7 +848,6 @@ public class View implements Drawable.Callback { private int oldWidthMeasureSpec = -1; private int oldHeightMeasureSpec = -1; private boolean layoutRequested = true; - private boolean layoutPending = true; private int oldWidth; private int oldHeight; protected boolean haveCustomMeasure = true; @@ -889,7 +889,11 @@ public class View implements Drawable.Callback { public static final Property ALPHA = new Property(Float.class, "alpha") { @Override public Float get(View object) { - return 0.f; + return object.getAlpha(); + } + @Override + public void set(View object, Float value) { + object.setAlpha(value); } }; @@ -1367,7 +1371,6 @@ public class View implements Drawable.Callback { oldHeightMeasureSpec = heightMeasureSpec; onMeasure(widthMeasureSpec, heightMeasureSpec); layoutRequested = false; - layoutPending = true; } } @@ -1443,10 +1446,8 @@ public class View implements Drawable.Callback { native_layout(widget, l, t, r, b); } - /** Helper function to be called from GTK's LayoutManager via JNI - * @return true if the layout changed - */ - private boolean layoutInternal(int width, int height) { + /** Helper function to be called from GTKs LayoutManager via JNI */ + private void layoutInternal(int width, int height) { // if the layout is triggered from a native widget, we might not have measured yet if (width != getMeasuredWidth() || height != getMeasuredHeight()) { measure(width | MeasureSpec.EXACTLY, height | MeasureSpec.EXACTLY); @@ -1456,15 +1457,9 @@ public class View implements Drawable.Callback { onSizeChanged(width, height, oldWidth, oldHeight); bottom = top + height; right = left + width; - if (changed || layoutPending) { - layoutPending = false; - onLayout(changed, 0, 0, width, height); - oldWidth = width; - oldHeight = height; - return true; - } else { - return false; - } + onLayout(changed, 0, 0, width, height); + oldWidth = width; + oldHeight = height; } public int getLeft() { @@ -1732,7 +1727,15 @@ public class View implements Drawable.Callback { public void setOnCreateContextMenuListener (View.OnCreateContextMenuListener l) {} - public void startAnimation(Animation animation) {} + protected void onAnimationStart() {} + + protected void onAnimationEnd() {} + + public void startAnimation(Animation animation) { + onAnimationStart(); + animation.start(); + onAnimationEnd(); + } public void getDrawingRect(Rect rect) { rect.left = getScrollX(); @@ -1859,7 +1862,7 @@ public class View implements Drawable.Callback { public void setForeground(Drawable foreground) {} - public boolean canScrollVertically(int value) {return true;} + public boolean canScrollVertically(int value) {return false;} public boolean isInTouchMode() {return false;} @@ -2161,4 +2164,14 @@ public class View implements Drawable.Callback { public boolean isDirty() { return false; } public float getX() { return getLeft(); } + + public boolean getGlobalVisibleRect(Rect visibleRect, Point globalOffset) { + boolean result = native_getGlobalVisibleRect(widget, visibleRect); + globalOffset.set(visibleRect.left, visibleRect.top); + return result; + } + + public void restoreHierarchyState(SparseArray container) {} + + public boolean isHovered() { return false; } } diff --git a/src/api-impl/android/view/ViewGroup.java b/src/api-impl/android/view/ViewGroup.java index 8bff127f..b8defa1d 100644 --- a/src/api-impl/android/view/ViewGroup.java +++ b/src/api-impl/android/view/ViewGroup.java @@ -15,6 +15,7 @@ import java.util.Objects; public class ViewGroup extends View implements ViewParent, ViewManager { public ArrayList children; + private ArrayList detachedChildren; private OnHierarchyChangeListener onHierarchyChangeListener; private LayoutTransition transition; @@ -34,6 +35,7 @@ public class ViewGroup extends View implements ViewParent, ViewManager { super(context, attrs, defStyleAttr); children = new ArrayList(); + detachedChildren = new ArrayList(); } public void addView(View child) { @@ -136,10 +138,15 @@ public class ViewGroup extends View implements ViewParent, ViewManager { } public void detachViewFromParent(int index) { - children.remove(index).parent = null; + View child = children.remove(index); + child.parent = null; + detachedChildren.add(child); } public void attachViewToParent(View view, int index, LayoutParams params) { + if (!detachedChildren.remove(view)) { + addViewInternal(view, index, params); + } if (!checkLayoutParams(params)) params = generateLayoutParams(params); @@ -151,6 +158,9 @@ public class ViewGroup extends View implements ViewParent, ViewManager { } protected void removeDetachedView(View child, boolean animate) { + if (!detachedChildren.remove(child)) + return; + child.parent = null; native_removeView(widget, child.widget); if (isAttachedToWindow()) child.detachFromWindowInternal(); @@ -388,6 +398,7 @@ public class ViewGroup extends View implements ViewParent, ViewManager { public void detachViewFromParent(View view) { children.remove(view); view.parent = null; + detachedChildren.add(view); } public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) {} @@ -623,4 +634,6 @@ public class ViewGroup extends View implements ViewParent, ViewManager { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'onStopNestedScroll'"); } + + public void requestChildFocus(View child, View focused) {} } diff --git a/src/api-impl/android/view/animation/Animation.java b/src/api-impl/android/view/animation/Animation.java index a68029d7..2d8e4f7a 100644 --- a/src/api-impl/android/view/animation/Animation.java +++ b/src/api-impl/android/view/animation/Animation.java @@ -1,8 +1,5 @@ package android.view.animation; -import android.os.Handler; -import android.os.Looper; - public class Animation { public interface AnimationListener { @@ -11,6 +8,8 @@ public class Animation { public void onAnimationStart(Animation animation); } + private AnimationListener listener; + public void setDuration(long durationMillis) {} public void setInterpolator(Interpolator i) {} @@ -23,12 +22,7 @@ public class Animation { public void setStartOffset(long offset) {} public void setAnimationListener(AnimationListener l) { - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - l.onAnimationEnd(Animation.this); // FIXME - } - }); + this.listener = l; } public void setRepeatCount(int count) {} @@ -37,5 +31,14 @@ public class Animation { public void reset() {} - public void start() {} + public void start() { + if (listener != null) { + listener.onAnimationStart(this); + listener.onAnimationEnd(this); + } + } + + public boolean hasStarted() { + return false; + } } diff --git a/src/api-impl/android/widget/PopupMenu.java b/src/api-impl/android/widget/PopupMenu.java index aa197e8b..66881b88 100644 --- a/src/api-impl/android/widget/PopupMenu.java +++ b/src/api-impl/android/widget/PopupMenu.java @@ -234,9 +234,8 @@ public class PopupMenu { } @Override - public MenuItem getItem(int id) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getItem'"); + public MenuItem getItem(int index) { + return items.get(index); } @Override @@ -297,8 +296,7 @@ public class PopupMenu { @Override public int size() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'size'"); + return items.size(); } @Override diff --git a/src/api-impl/android/widget/PopupWindow.java b/src/api-impl/android/widget/PopupWindow.java index a511a901..5e0abe64 100644 --- a/src/api-impl/android/widget/PopupWindow.java +++ b/src/api-impl/android/widget/PopupWindow.java @@ -1,6 +1,7 @@ package android.widget; import android.content.Context; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; @@ -118,4 +119,8 @@ public class PopupWindow { } public void setWindowLayoutType(int type) {} + + public void setIsClippedToScreen(boolean isClippedToScreen) {} + + public void setEpicenterBounds(Rect bounds) {} } diff --git a/src/api-impl/android/widget/ProgressBar.java b/src/api-impl/android/widget/ProgressBar.java index 9b7931ee..f0a81a8a 100644 --- a/src/api-impl/android/widget/ProgressBar.java +++ b/src/api-impl/android/widget/ProgressBar.java @@ -13,6 +13,7 @@ public class ProgressBar extends View { protected int progress = 0; private boolean indeterminate = false; private Drawable indeterminateDrawable; + private Drawable progressDrawable = new Drawable(); public ProgressBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); @@ -58,13 +59,7 @@ public class ProgressBar extends View { public Drawable getProgressDrawable() { - return new Drawable() { - @Override - public void draw(Canvas canvas) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'draw'"); - } - }; + return progressDrawable; } public Drawable getIndeterminateDrawable() { @@ -103,7 +98,8 @@ public class ProgressBar extends View { this.indeterminateDrawable = indeterminateDrawable; } - public void setProgressDrawable(Drawable indeterminateDrawable) { + public void setProgressDrawable(Drawable progressDrawable) { + this.progressDrawable = progressDrawable; } public native void native_setIndeterminate(boolean indeterminate); diff --git a/src/api-impl/android/widget/RatingBar.java b/src/api-impl/android/widget/RatingBar.java new file mode 100644 index 00000000..9ed43fd1 --- /dev/null +++ b/src/api-impl/android/widget/RatingBar.java @@ -0,0 +1,16 @@ +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; + +public class RatingBar extends AbsSeekBar { + + public RatingBar(Context context) { + this(context, null); + } + + public RatingBar(Context context, AttributeSet attributeSet) { + super(context, attributeSet); + } + +} diff --git a/src/api-impl/android/widget/TextView.java b/src/api-impl/android/widget/TextView.java index 7e108f9f..1b4c5728 100644 --- a/src/api-impl/android/widget/TextView.java +++ b/src/api-impl/android/widget/TextView.java @@ -397,4 +397,6 @@ public class TextView extends View { public int getImeOptions() {return 0;} public void setShadowLayer(float radius, float dx, float dy, int color) {} + + public void setBreakStrategy(int strategy) {} } diff --git a/src/api-impl/android/widget/ViewFlipper.java b/src/api-impl/android/widget/ViewFlipper.java new file mode 100644 index 00000000..4b92c238 --- /dev/null +++ b/src/api-impl/android/widget/ViewFlipper.java @@ -0,0 +1,16 @@ +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; + +public class ViewFlipper extends ViewAnimator { + + public ViewFlipper(Context context) { + this(context, null); + } + + public ViewFlipper(Context context, AttributeSet attrs) { + super(context, attrs); + } + +} diff --git a/src/api-impl/meson.build b/src/api-impl/meson.build index 2afa31db..3abb2f97 100644 --- a/src/api-impl/meson.build +++ b/src/api-impl/meson.build @@ -25,6 +25,7 @@ srcs = [ 'android/annotation/Widget.java', 'android/app/Activity.java', 'android/app/ActivityManager.java', + 'android/app/ActivityOptions.java', 'android/app/ActivityThread.java', 'android/app/AlarmManager.java', 'android/app/AlertDialog.java', @@ -102,6 +103,7 @@ srcs = [ 'android/content/pm/LauncherApps.java', 'android/content/pm/ManifestDigest.java', 'android/content/pm/PackageInfo.java', + 'android/content/pm/PackageInstaller.java', 'android/content/pm/PackageItemInfo.java', 'android/content/pm/PackageManager.java', 'android/content/pm/PackageParser.java', @@ -353,7 +355,9 @@ srcs = [ 'android/provider/BaseColumns.java', 'android/provider/CalendarContract.java', 'android/provider/ContactsContract.java', + 'android/provider/MediaStore.java', 'android/provider/Settings.java', + 'android/security/keystore/AndroidKeyStore.java', 'android/service/media/MediaBrowserService.java', 'android/telecom/ConnectionService.java', 'android/telecom/TelecomManager.java', @@ -424,6 +428,7 @@ srcs = [ 'android/util/AndroidException.java', 'android/util/AndroidRuntimeException.java', 'android/util/ArrayMap.java', + 'android/util/ArraySet.java', 'android/util/AttributeSet.java', 'android/util/Base64.java', 'android/util/Base64DataException.java', @@ -585,6 +590,7 @@ srcs = [ 'android/widget/ProgressBar.java', 'android/widget/RadioButton.java', 'android/widget/RadioGroup.java', + 'android/widget/RatingBar.java', 'android/widget/RelativeLayout.java', 'android/widget/RemoteViews.java', 'android/widget/ScrollView.java', @@ -603,6 +609,7 @@ srcs = [ 'android/widget/ToggleButton.java', 'android/widget/Toolbar.java', 'android/widget/ViewAnimator.java', + 'android/widget/ViewFlipper.java', 'android/widget/ZoomButton.java', 'android/widget/ZoomButtonsController.java', 'com/android/internal/Manifest.java',