diff --git a/meson.build b/meson.build index 328475d4..f08786ac 100644 --- a/meson.build +++ b/meson.build @@ -87,6 +87,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [ 'src/api-impl-jni/app/android_app_Dialog.c', 'src/api-impl-jni/audio/android_media_AudioTrack.c', 'src/api-impl-jni/audio/android_media_SoundPool.c', + 'src/api-impl-jni/content/android_content_ClipboardManager.c', 'src/api-impl-jni/content/android_content_Context.c', 'src/api-impl-jni/database/android_database_SQLiteCommon.c', 'src/api-impl-jni/database/android_database_SQLiteConnection.c', diff --git a/src/api-impl-jni/app/android_app_Activity.c b/src/api-impl-jni/app/android_app_Activity.c index be49b809..17c3d77c 100644 --- a/src/api-impl-jni/app/android_app_Activity.c +++ b/src/api-impl-jni/app/android_app_Activity.c @@ -177,11 +177,3 @@ JNIEXPORT void JNICALL Java_android_app_Activity_nativeOpenURI(JNIEnv *env, jcla } extern GtkWindow *window; // TODO: get this in a better way - -JNIEXPORT void JNICALL Java_android_app_Activity_nativeShare(JNIEnv *env, jclass class, jstring text_jstring) -{ - const char *text = (*env)->GetStringUTFChars(env, text_jstring, NULL); - GdkClipboard *clipboard = gdk_display_get_clipboard(gtk_root_get_display(GTK_ROOT(window))); - gdk_clipboard_set_text(clipboard, text); - (*env)->ReleaseStringUTFChars(env, text_jstring, text); -} diff --git a/src/api-impl-jni/content/android_content_ClipboardManager.c b/src/api-impl-jni/content/android_content_ClipboardManager.c new file mode 100644 index 00000000..0d5e9bbe --- /dev/null +++ b/src/api-impl-jni/content/android_content_ClipboardManager.c @@ -0,0 +1,16 @@ +#include + +#include "../defines.h" +#include "../util.h" + +#include "../generated_headers/android_content_ClipboardManager.h" + +extern GtkWindow *window; // TODO: get this in a better way + +JNIEXPORT void JNICALL Java_android_content_ClipboardManager_native_1set_1clipboard(JNIEnv *env, jclass class, jstring text_jstring) +{ + const char *text = (*env)->GetStringUTFChars(env, text_jstring, NULL); + GdkClipboard *clipboard = gdk_display_get_clipboard(gtk_root_get_display(GTK_ROOT(window))); + gdk_clipboard_set_text(clipboard, text); + (*env)->ReleaseStringUTFChars(env, text_jstring, text); +} diff --git a/src/api-impl-jni/generated_headers/android_app_Activity.h b/src/api-impl-jni/generated_headers/android_app_Activity.h index d92f64f7..a53c45c5 100644 --- a/src/api-impl-jni/generated_headers/android_app_Activity.h +++ b/src/api-impl-jni/generated_headers/android_app_Activity.h @@ -41,14 +41,6 @@ JNIEXPORT void JNICALL Java_android_app_Activity_nativeStartActivity JNIEXPORT void JNICALL Java_android_app_Activity_nativeOpenURI (JNIEnv *, jclass, jstring); -/* - * Class: android_app_Activity - * Method: nativeShare - * Signature: (Ljava/lang/String;)V - */ -JNIEXPORT void JNICALL Java_android_app_Activity_nativeShare - (JNIEnv *, jclass, jstring); - #ifdef __cplusplus } #endif diff --git a/src/api-impl-jni/generated_headers/android_content_ClipboardManager.h b/src/api-impl-jni/generated_headers/android_content_ClipboardManager.h new file mode 100644 index 00000000..14ff9bd6 --- /dev/null +++ b/src/api-impl-jni/generated_headers/android_content_ClipboardManager.h @@ -0,0 +1,23 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_content_ClipboardManager */ + +#ifndef _Included_android_content_ClipboardManager +#define _Included_android_content_ClipboardManager +#ifdef __cplusplus +extern "C" { +#endif +#undef android_content_ClipboardManager_MSG_REPORT_PRIMARY_CLIP_CHANGED +#define android_content_ClipboardManager_MSG_REPORT_PRIMARY_CLIP_CHANGED 1L +/* + * Class: android_content_ClipboardManager + * Method: native_set_clipboard + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_android_content_ClipboardManager_native_1set_1clipboard + (JNIEnv *, jclass, jstring); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/api-impl-jni/generated_headers/android_widget_TextView.h b/src/api-impl-jni/generated_headers/android_widget_TextView.h index e80bf33f..7fb16f63 100644 --- a/src/api-impl-jni/generated_headers/android_widget_TextView.h +++ b/src/api-impl-jni/generated_headers/android_widget_TextView.h @@ -239,6 +239,14 @@ JNIEXPORT void JNICALL Java_android_widget_TextView_setTextSize JNIEXPORT void JNICALL Java_android_widget_TextView_native_1setTextColor (JNIEnv *, jobject, jint); +/* + * Class: android_widget_TextView + * Method: getText + * Signature: ()Ljava/lang/CharSequence; + */ +JNIEXPORT jobject JNICALL Java_android_widget_TextView_getText + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif diff --git a/src/api-impl-jni/widgets/android_widget_TextView.c b/src/api-impl-jni/widgets/android_widget_TextView.c index b72507f6..6734b7f9 100644 --- a/src/api-impl-jni/widgets/android_widget_TextView.c +++ b/src/api-impl-jni/widgets/android_widget_TextView.c @@ -79,3 +79,8 @@ JNIEXPORT void JNICALL Java_android_widget_TextView_native_1set_1markup(JNIEnv * gtk_label_set_use_markup(label, value); } + +JNIEXPORT jobject JNICALL Java_android_widget_TextView_getText(JNIEnv *env, jobject this) +{ + return _JSTRING(gtk_label_get_text(GTK_LABEL(_PTR(_GET_LONG_FIELD(this, "widget"))))); +} diff --git a/src/api-impl/android/animation/AnimatorSet.java b/src/api-impl/android/animation/AnimatorSet.java index 06351d0f..f4abb6d6 100644 --- a/src/api-impl/android/animation/AnimatorSet.java +++ b/src/api-impl/android/animation/AnimatorSet.java @@ -2,4 +2,19 @@ package android.animation; public class AnimatorSet extends Animator { + public class Builder { + + public Builder with(Animator animator) { + return this; + } + } + + public Builder play(Animator animator) { + return new Builder(); + } + + public void setInterpolator(TimeInterpolator value) {} + + public void playSequentially(Animator[] animators) {} + } diff --git a/src/api-impl/android/app/Activity.java b/src/api-impl/android/app/Activity.java index 8cfdbc88..8e447132 100644 --- a/src/api-impl/android/app/Activity.java +++ b/src/api-impl/android/app/Activity.java @@ -254,7 +254,7 @@ public class Activity extends ContextWrapper implements Window.Callback { protected void onActivityResult(int requestCode, int resultCode, Intent data) {} - public void startActivityForResult(Intent intent, int requestCode) { + public void startActivityForResult(Intent intent, int requestCode, Bundle options) { System.out.println("startActivityForResult(" + intent + ", " + requestCode + ") called, but we don't currently support multiple activities"); if (intent.getComponent() != null) { try { @@ -276,9 +276,13 @@ public class Activity extends ContextWrapper implements Window.Callback { } } else { + System.out.println("startActivityForResult: intent was not handled. Calling onActivityResult(RESULT_CANCELED)."); onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent()); // RESULT_CANCELED is the only pre-defined return value, so hopefully it works out for us } } + public void startActivityForResult(Intent intent, int requestCode) { + startActivityForResult(intent, requestCode, null); + } public void setResult(int resultCode, Intent data) { if (resultActivity != null) { @@ -422,5 +426,4 @@ public class Activity extends ContextWrapper implements Window.Callback { public static native void nativeRecreateActivity(Activity activity); public static native void nativeStartActivity(Activity activity); public static native void nativeOpenURI(String uri); - public static native void nativeShare(String text); } diff --git a/src/api-impl/android/app/Notification.java b/src/api-impl/android/app/Notification.java index bef6b121..3b83630b 100644 --- a/src/api-impl/android/app/Notification.java +++ b/src/api-impl/android/app/Notification.java @@ -143,4 +143,13 @@ public class Notification { public MediaStyle setMediaSession(MediaSession.Token token) {return this;} } + + public static class BigTextStyle extends Style { + + public BigTextStyle(Notification.Builder builder) {} + + public BigTextStyle setBigContentTitle(CharSequence title) {return this;} + + public BigTextStyle bigText(CharSequence text) {return this;} + } } diff --git a/src/api-impl/android/app/NotificationManager.java b/src/api-impl/android/app/NotificationManager.java index add897ae..6313b4d8 100644 --- a/src/api-impl/android/app/NotificationManager.java +++ b/src/api-impl/android/app/NotificationManager.java @@ -6,4 +6,8 @@ public class NotificationManager { public void notify(String tag, int id, Notification notification) { System.out.println("notify(" + tag + ", " + id + ", " + notification + ") called"); } + + public void notify(int id, Notification notification) { + System.out.println("notify(" + id + ", " + notification + ") called"); + } } diff --git a/src/api-impl/android/app/PendingIntent.java b/src/api-impl/android/app/PendingIntent.java index f581d92f..9e914eb0 100644 --- a/src/api-impl/android/app/PendingIntent.java +++ b/src/api-impl/android/app/PendingIntent.java @@ -19,6 +19,10 @@ public class PendingIntent { return new PendingIntent(); } + public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags) { + return new PendingIntent(); + } + public class CanceledException extends Exception { } } diff --git a/src/api-impl/android/app/Service.java b/src/api-impl/android/app/Service.java index 2606cc80..8f2373f6 100644 --- a/src/api-impl/android/app/Service.java +++ b/src/api-impl/android/app/Service.java @@ -6,7 +6,9 @@ import android.os.IBinder; public abstract class Service extends Context { - public abstract void onCreate(); + public void onCreate() { + System.out.println("Service.onCreate() called"); + } public abstract IBinder onBind(Intent intent); @@ -16,4 +18,8 @@ public abstract class Service extends Context { System.out.println("startForeground(" + id + ", " + notification + ") called"); } + public void stopForeground(boolean remove) { + System.out.println("stopForeground(" + remove + ") called"); + } + } diff --git a/src/api-impl/android/content/ClipData.java b/src/api-impl/android/content/ClipData.java new file mode 100644 index 00000000..41b70582 --- /dev/null +++ b/src/api-impl/android/content/ClipData.java @@ -0,0 +1,12 @@ +package android.content; + +public class ClipData { + + String text; + + public static ClipData newPlainText(CharSequence label, CharSequence text) { + ClipData clip = new ClipData(); + clip.text = text.toString(); + return clip; + } +} diff --git a/src/api-impl/android/content/ClipboardManager.java b/src/api-impl/android/content/ClipboardManager.java index 6e60b8bc..ffabd1fe 100644 --- a/src/api-impl/android/content/ClipboardManager.java +++ b/src/api-impl/android/content/ClipboardManager.java @@ -14,6 +14,7 @@ * limitations under the License. */ package android.content; +import android.app.Activity; import android.content.Context; import android.os.Handler; import android.os.IBinder; @@ -22,7 +23,6 @@ import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; -class ClipData {} class ClipDescription {} /** @@ -79,6 +79,8 @@ public class ClipboardManager extends android.text.ClipboardManager { * @param clip The clipped data item to set. */ public void setPrimaryClip(ClipData clip) { + // this is not the primary clipboard in UNIX sense (not middle click paste) + native_set_clipboard(clip.text); } /** * Returns the current primary clip on the clipboard. @@ -125,4 +127,6 @@ public class ClipboardManager extends android.text.ClipboardManager { } void reportPrimaryClipChanged() { } + + public static native void native_set_clipboard(String text); } diff --git a/src/api-impl/android/content/Context.java b/src/api-impl/android/content/Context.java index d675876f..ea608693 100644 --- a/src/api-impl/android/content/Context.java +++ b/src/api-impl/android/content/Context.java @@ -24,6 +24,10 @@ import android.location.LocationManager; import android.media.AudioManager; import android.media.MediaRouter; import android.net.ConnectivityManager; +import android.net.Uri; +import android.net.wifi.WifiManager; +import android.os.Environment; +import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.Vibrator; @@ -86,6 +90,7 @@ public class Context extends Object { r = new Resources(assets, dm, config); theme = r.newTheme(); application_info = new ApplicationInfo(); + application_info.dataDir = Environment.getExternalStorageDirectory().getAbsolutePath(); InputStream inStream = ClassLoader.getSystemClassLoader().getResourceAsStream("AndroidManifest.xml"); try { manifest = AndroidManifestBlock.load(inStream); @@ -188,6 +193,8 @@ public class Context extends Object { return new AccessibilityManager(); case "layout_inflater": return new LayoutInflater(); + case "wifi": + return new WifiManager(); default: Slog.e(TAG, "!!!!!!! getSystemService: case >" + name + "< is not implemented yet"); return null; @@ -374,24 +381,29 @@ public class Context extends Object { public void registerComponentCallbacks(ComponentCallbacks callbacks) {} - public boolean bindService(Intent intent, ServiceConnection serviceConnection, int dummy3) { - try { - if(intent.getComponent() == null) { - Slog.w(TAG, "Context.bindService: intent.getComponent() is null"); - return false; // maybe? - } - - Class cls = Class.forName(intent.getComponent().getClassName()).asSubclass(Service.class); - if (!runningServices.containsKey(cls)) { - Service service = cls.getConstructor().newInstance(); - service.onCreate(); - runningServices.put(cls, service); - } - serviceConnection.onServiceConnected(intent.getComponent(), runningServices.get(cls).onBind(intent)); - } catch (ReflectiveOperationException e) { - e.printStackTrace(); + public boolean bindService(final Intent intent, final ServiceConnection serviceConnection, int dummy3) { + if(intent.getComponent() == null) { + Slog.w(TAG, "Context.bindService: intent.getComponent() is null"); + return false; } - return false; // maybe? + + new Handler().post(new Runnable() { // run this asynchron so the caller can finish its setup before onServiceConnected is called + @Override + public void run() { + try { + Class cls = Class.forName(intent.getComponent().getClassName()).asSubclass(Service.class); + if (!runningServices.containsKey(cls)) { + Service service = cls.getConstructor().newInstance(); + service.onCreate(); + runningServices.put(cls, service); + } + serviceConnection.onServiceConnected(intent.getComponent(), runningServices.get(cls).onBind(intent)); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } + }); + return true; } public void startActivity(Intent intent) { @@ -402,7 +414,7 @@ public class Context extends Object { if (intent.getComponent() == null) { if(intent.getAction() != null && intent.getAction().equals("android.intent.action.SEND")) { Slog.i(TAG, "starting extern activity with intent: " + intent); - Activity.nativeShare((String) intent.getExtras().get("android.intent.extra.TEXT")); + ClipboardManager.native_set_clipboard(intent.getStringExtra("android.intent.extra.TEXT")); } else if (intent.getData() != null) { Slog.i(TAG, "starting extern activity with intent: " + intent); Activity.nativeOpenURI(String.valueOf(intent.getData())); @@ -466,4 +478,8 @@ public class Context extends Object { public Context createPackageContext(String dummy, int dummy2) { return this; // FIXME? } + + public void grantUriPermission(String dummy, Uri dummy2, int dummy3) { + System.out.println("grantUriPermission(" + dummy + ", " + dummy2 + ", " + dummy3 + ") called"); + } } diff --git a/src/api-impl/android/content/Intent.java b/src/api-impl/android/content/Intent.java index 806e50b9..d2d55c81 100644 --- a/src/api-impl/android/content/Intent.java +++ b/src/api-impl/android/content/Intent.java @@ -278,4 +278,16 @@ public class Intent { this.type = type; return this; } + + public long getLongExtra(String name, long def) { + return extras.getLong(name, def); + } + + public char getCharExtra(String name, char def) { + return extras.getChar(name, def); + } + + public Parcelable[] getParcelableArrayExtra(String name) { + return extras.getParcelableArray(name); + } } diff --git a/src/api-impl/android/graphics/Color.java b/src/api-impl/android/graphics/Color.java index 405cbe32..bd6ec5e1 100644 --- a/src/api-impl/android/graphics/Color.java +++ b/src/api-impl/android/graphics/Color.java @@ -127,4 +127,31 @@ public class Color { sColorNameMap.put("silver", 0xC0C0C0); sColorNameMap.put("teal", 0x008080); } + + public static void colorToHSV(int color, float[] hsv) { + float red = ((color >> 16) & 0xFF) / 255.0f; + float green = ((color >> 8) & 0xFF) / 255.0f; + float blue = (color & 0xFF) / 255.0f; + float min = Math.min(red, Math.min(green, blue)); + float max = Math.max(red, Math.max(green, blue)); + float delta = max - min; + + if (delta == 0) { + hsv[0] = 6; + } else if (max == red) { + hsv[0] = (green - blue) / delta % 6; + } else if (max == green) { + hsv[0] = 2 + (blue - red) / delta + 2; + } else { + hsv[0] = 4 + (red - green) / delta + 4; + } + hsv[0] *= 60; + + if (max == 0) { + hsv[1] = 0; + } else { + hsv[1] = delta / max; + } + hsv[2] = max; + } } diff --git a/src/api-impl/android/net/ConnectivityManager.java b/src/api-impl/android/net/ConnectivityManager.java index b3588f9c..6590c679 100644 --- a/src/api-impl/android/net/ConnectivityManager.java +++ b/src/api-impl/android/net/ConnectivityManager.java @@ -9,6 +9,12 @@ public class ConnectivityManager { } public NetworkInfo getActiveNetworkInfo() { - return null; // there is no active network, because there isn't any network at all + return new NetworkInfo(); + } + + public void registerNetworkCallback(NetworkRequest request, NetworkCallback callback) {} + + public boolean isActiveNetworkMetered() { + return false; } } diff --git a/src/api-impl/android/net/NetworkInfo.java b/src/api-impl/android/net/NetworkInfo.java index a2a0b6c3..97f40026 100644 --- a/src/api-impl/android/net/NetworkInfo.java +++ b/src/api-impl/android/net/NetworkInfo.java @@ -17,4 +17,8 @@ public class NetworkInfo { public int getType() { return 0x8; // where did you even get a NetworkInfo object... there is no network } + + public boolean isConnected() { + return true; + } } diff --git a/src/api-impl/android/net/NetworkRequest.java b/src/api-impl/android/net/NetworkRequest.java new file mode 100644 index 00000000..a4cb99ed --- /dev/null +++ b/src/api-impl/android/net/NetworkRequest.java @@ -0,0 +1,11 @@ +package android.net; + +public class NetworkRequest { + + public class Builder { + + public NetworkRequest build() { + return new NetworkRequest(); + } + } +} diff --git a/src/api-impl/android/net/Uri.java b/src/api-impl/android/net/Uri.java index 98c06d4d..01a52623 100644 --- a/src/api-impl/android/net/Uri.java +++ b/src/api-impl/android/net/Uri.java @@ -7,7 +7,9 @@ import java.io.File; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; -public class Uri { +import android.os.Parcelable; + +public class Uri implements Parcelable { public static final Uri EMPTY = new Uri(); diff --git a/src/api-impl/android/net/wifi/WifiManager.java b/src/api-impl/android/net/wifi/WifiManager.java index 5526e8fc..59728e9f 100644 --- a/src/api-impl/android/net/wifi/WifiManager.java +++ b/src/api-impl/android/net/wifi/WifiManager.java @@ -1,5 +1,19 @@ package android.net.wifi; public class WifiManager { - + + public class WifiLock { + + public void setReferenceCounted(boolean referenceCounted) {} + + public void release() {} + + public void acquire() {} + + public boolean isHeld() { return false; } + } + + public WifiLock createWifiLock(int lockType, String tag) { + return new WifiLock(); + } } diff --git a/src/api-impl/android/os/PowerManager.java b/src/api-impl/android/os/PowerManager.java index 34eff171..403d21a6 100644 --- a/src/api-impl/android/os/PowerManager.java +++ b/src/api-impl/android/os/PowerManager.java @@ -7,6 +7,8 @@ public final class PowerManager { public void acquire() {} public void release() {} + + public boolean isHeld() { return false; } } public WakeLock newWakeLock(int levelAndFlags, String tag) { diff --git a/src/api-impl/android/text/Layout.java b/src/api-impl/android/text/Layout.java index c3e76fa2..9a390715 100644 --- a/src/api-impl/android/text/Layout.java +++ b/src/api-impl/android/text/Layout.java @@ -17,4 +17,10 @@ public class Layout { public TextPaint getPaint() {return new TextPaint();} public int getEllipsisCount(int line) {return 0;} + + public CharSequence getText() {return "FIXME Layout.getText";} + + public int getWidth() {return 10;} + + public int getHeight() {return 10;} } diff --git a/src/api-impl/android/view/MenuItem.java b/src/api-impl/android/view/MenuItem.java index 7483ed3d..852ce7a7 100644 --- a/src/api-impl/android/view/MenuItem.java +++ b/src/api-impl/android/view/MenuItem.java @@ -1,8 +1,12 @@ package android.view; +import android.graphics.drawable.Drawable; + public interface MenuItem { - public interface OnMenuItemClickListener {} + public interface OnMenuItemClickListener { + public boolean onMenuItemClick(MenuItem item); + } public MenuItem setIcon(int iconRes); @@ -30,4 +34,10 @@ public interface MenuItem { public MenuItem setTitle(int resId); + public boolean isVisible(); + + public Drawable getIcon(); + + public SubMenu getSubMenu(); + } \ 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 dfc55e27..70183f86 100644 --- a/src/api-impl/android/view/View.java +++ b/src/api-impl/android/view/View.java @@ -18,6 +18,7 @@ import android.os.Looper; import android.os.Parcelable; import android.util.AttributeSet; import android.util.LayoutDirection; +import android.util.Property; import android.util.Slog; import android.util.SparseArray; import android.view.animation.Animation; @@ -823,6 +824,13 @@ public class View extends Object { private int minWidth = 0; private int minHeight = 0; + public static final Property TRANSLATION_Z = new Property(Float.class, "translationZ") { + @Override + public Float get(View object) { + return 0.f; + } + }; + public View(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -1641,4 +1649,8 @@ public class View extends Object { } public boolean onCheckIsTextEditor() {return false;} + + public boolean hasOnClickListeners() {return false;} + + public void setTextAlignment(int textAlignment) {} } diff --git a/src/api-impl/android/view/ViewGroup.java b/src/api-impl/android/view/ViewGroup.java index c1ebfdcd..b58fda43 100644 --- a/src/api-impl/android/view/ViewGroup.java +++ b/src/api-impl/android/view/ViewGroup.java @@ -10,6 +10,7 @@ import java.util.Iterator; public class ViewGroup extends View implements ViewParent, ViewManager { public ArrayList children; + private OnHierarchyChangeListener onHierarchyChangeListener; public ViewGroup(Context context) { this(context, null); @@ -67,6 +68,9 @@ public class ViewGroup extends View implements ViewParent, ViewManager { if (isAttachedToWindow()) { child.onAttachedToWindow(); } + if (onHierarchyChangeListener != null) { + onHierarchyChangeListener.onChildViewAdded(this, child); + } requestLayout(); } @@ -85,6 +89,9 @@ public class ViewGroup extends View implements ViewParent, ViewManager { child.parent = null; children.remove(child); native_removeView(widget, child.widget); + if (onHierarchyChangeListener != null) { + onHierarchyChangeListener.onChildViewRemoved(this, child); + } } public void removeViewAt(int index) { @@ -97,6 +104,9 @@ public class ViewGroup extends View implements ViewParent, ViewManager { child.parent = null; it.remove(); native_removeView(widget, child.widget); + if (onHierarchyChangeListener != null) { + onHierarchyChangeListener.onChildViewRemoved(this, child); + } } } @@ -154,7 +164,9 @@ public class ViewGroup extends View implements ViewParent, ViewManager { public void setMotionEventSplittingEnabled(boolean enabled) {} - public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {} + public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { + this.onHierarchyChangeListener = listener; + } protected boolean checkLayoutParams(LayoutParams params) { return true; @@ -422,5 +434,7 @@ public class ViewGroup extends View implements ViewParent, ViewManager { } public interface OnHierarchyChangeListener { + public void onChildViewAdded(View parent, View child); + public void onChildViewRemoved(View parent, View child); } } diff --git a/src/api-impl/android/view/WindowManager.java b/src/api-impl/android/view/WindowManager.java index e7a1a7e7..18282da4 100644 --- a/src/api-impl/android/view/WindowManager.java +++ b/src/api-impl/android/view/WindowManager.java @@ -7,5 +7,14 @@ public interface WindowManager { public static final int FLAG_KEEP_SCREEN_ON = 0; public float screenBrightness; + public int softInputMode; + public int x; + public int y; + + public LayoutParams(int w, int h, int type, int flags, int format) { + super(w, h); + } + + public LayoutParams() {} } } diff --git a/src/api-impl/android/view/WindowManagerImpl.java b/src/api-impl/android/view/WindowManagerImpl.java index eb7f6d60..87c51fdf 100644 --- a/src/api-impl/android/view/WindowManagerImpl.java +++ b/src/api-impl/android/view/WindowManagerImpl.java @@ -1,10 +1,24 @@ package android.view; -public class WindowManagerImpl implements WindowManager { +public class WindowManagerImpl implements WindowManager, ViewManager { public android.view.Display getDefaultDisplay() { return new android.view.Display(); } - public class LayoutParams { + @Override + public void addView(View view, android.view.ViewGroup.LayoutParams params) { + System.out.println("WindowManagerImpl.addView(" + view + ", " + params + ") called"); + } + + @Override + public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'updateViewLayout'"); + } + + @Override + public void removeView(View view) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'removeView'"); } } diff --git a/src/api-impl/android/widget/AutoCompleteTextView.java b/src/api-impl/android/widget/AutoCompleteTextView.java index a0c6a6e1..2aae90e5 100644 --- a/src/api-impl/android/widget/AutoCompleteTextView.java +++ b/src/api-impl/android/widget/AutoCompleteTextView.java @@ -3,7 +3,7 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; -public class AutoCompleteTextView extends TextView { +public class AutoCompleteTextView extends EditText { public interface OnDismissListener { } diff --git a/src/api-impl/android/widget/Button.java b/src/api-impl/android/widget/Button.java index dc5074f7..9d8f7bf4 100644 --- a/src/api-impl/android/widget/Button.java +++ b/src/api-impl/android/widget/Button.java @@ -30,6 +30,9 @@ public class Button extends TextView { native_setText(widget, String.valueOf(text)); } + @Override + public CharSequence getText() { return "FIXME Button.getText"; } + @Override public void setTextSize(float size) {} diff --git a/src/api-impl/android/widget/Checkable.java b/src/api-impl/android/widget/Checkable.java index 842495de..0aef6271 100644 --- a/src/api-impl/android/widget/Checkable.java +++ b/src/api-impl/android/widget/Checkable.java @@ -3,4 +3,6 @@ package android.widget; public interface Checkable { public void setChecked(boolean checked); + + public boolean isChecked(); } \ No newline at end of file diff --git a/src/api-impl/android/widget/CompoundButton.java b/src/api-impl/android/widget/CompoundButton.java index 55fd8f30..7dbb41fc 100644 --- a/src/api-impl/android/widget/CompoundButton.java +++ b/src/api-impl/android/widget/CompoundButton.java @@ -16,14 +16,22 @@ public abstract class CompoundButton extends Button implements Checkable { @Override protected native long native_constructor(Context context, AttributeSet attrs); - public static interface OnCheckedChangeListener {} + public static interface OnCheckedChangeListener { + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked); + } public native void setOnCheckedChangeListener(OnCheckedChangeListener listener); + @Override public native void setChecked(boolean checked); + @Override public native boolean isChecked(); + public void toggle() { + setChecked(!isChecked()); + } + // following methods are overridden to prevent calling incompatible methods from superclasses @Override public void setOnClickListener(final OnClickListener l) {} diff --git a/src/api-impl/android/widget/ListView.java b/src/api-impl/android/widget/ListView.java index c17dec0a..e1173086 100644 --- a/src/api-impl/android/widget/ListView.java +++ b/src/api-impl/android/widget/ListView.java @@ -3,6 +3,7 @@ package android.widget; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.View; public class ListView extends AbsListView { @@ -20,4 +21,6 @@ public class ListView extends AbsListView { public void setTextFilterEnabled(boolean enabled) {} + public void addHeaderView(View v, Object data, boolean isSelectable) {} + } diff --git a/src/api-impl/android/widget/PopupMenu.java b/src/api-impl/android/widget/PopupMenu.java index f7b22767..999da674 100644 --- a/src/api-impl/android/widget/PopupMenu.java +++ b/src/api-impl/android/widget/PopupMenu.java @@ -115,6 +115,25 @@ public class PopupMenu { mOnDismissListener = listener; } + /** + * Inflate a menu resource into this PopupMenu. This is equivalent to + * calling {@code popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu())}. + * + * @param menuRes Menu resource to inflate + */ + public void inflate(int menuRes) { + System.out.println("PopupMenu.inflate(" + menuRes + ") called"); + } + + /** + * Show the menu popup anchored to the view specified during construction. + * + * @see #dismiss() + */ + public void show() { + System.out.println("PopupMenu.show() called"); + } + /** * Interface responsible for receiving menu item click events if the items * themselves do not have individual item click listeners. diff --git a/src/api-impl/android/widget/RadioGroup.java b/src/api-impl/android/widget/RadioGroup.java new file mode 100644 index 00000000..f09bad15 --- /dev/null +++ b/src/api-impl/android/widget/RadioGroup.java @@ -0,0 +1,422 @@ +/* +* Copyright (C) 2006 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package android.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.R; + + +/** + *

This class is used to create a multiple-exclusion scope for a set of radio +* buttons. Checking one radio button that belongs to a radio group unchecks +* any previously checked radio button within the same group.

+* +*

Intially, all of the radio buttons are unchecked. While it is not possible +* to uncheck a particular radio button, the radio group can be cleared to +* remove the checked state.

+* +*

The selection is identified by the unique id of the radio button as defined +* in the XML layout file.

+* +*

XML Attributes

+*

See {@link android.R.styleable#RadioGroup RadioGroup Attributes}, +* {@link android.R.styleable#LinearLayout LinearLayout Attributes}, +* {@link android.R.styleable#ViewGroup ViewGroup Attributes}, +* {@link android.R.styleable#View View Attributes}

+*

Also see +* {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams} +* for layout attributes.

+* +* @see RadioButton +* +*/ +public class RadioGroup extends LinearLayout { + + // holds the checked id; the selection is empty by default + private int mCheckedId = -1; + // tracks children radio buttons checked state + private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener; + // when true, mOnCheckedChangeListener discards events + private boolean mProtectFromCheckedChange = false; + private OnCheckedChangeListener mOnCheckedChangeListener; + private PassThroughHierarchyChangeListener mPassThroughListener; + + /** + * {@inheritDoc} + */ + public RadioGroup(Context context) { + super(context); + setOrientation(VERTICAL); + init(); + } + + /** + * {@inheritDoc} + */ + public RadioGroup(Context context, AttributeSet attrs) { + super(context, attrs); + + // retrieve selected radio button as requested by the user in the + // XML layout file + TypedArray attributes = context.obtainStyledAttributes( + attrs, com.android.internal.R.styleable.RadioGroup, com.android.internal.R.attr.radioButtonStyle, 0); + + int value = attributes.getResourceId(R.styleable.RadioGroup_checkedButton, View.NO_ID); + if (value != View.NO_ID) { + mCheckedId = value; + } + final int index = attributes.getInt(com.android.internal.R.styleable.RadioGroup_orientation, VERTICAL); + setOrientation(index); + + attributes.recycle(); + init(); + } + + private void init() { + mChildOnCheckedChangeListener = new CheckedStateTracker(); + mPassThroughListener = new PassThroughHierarchyChangeListener(); + super.setOnHierarchyChangeListener(mPassThroughListener); + } + + /** + * {@inheritDoc} + */ + @Override + public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { + // the user listener is delegated to our pass-through listener + mPassThroughListener.mOnHierarchyChangeListener = listener; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + // checks the appropriate radio button as requested in the XML file + if (mCheckedId != -1) { + mProtectFromCheckedChange = true; + setCheckedStateForView(mCheckedId, true); + mProtectFromCheckedChange = false; + setCheckedId(mCheckedId); + } + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (child instanceof RadioButton) { + final RadioButton button = (RadioButton) child; + if (button.isChecked()) { + mProtectFromCheckedChange = true; + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + mProtectFromCheckedChange = false; + setCheckedId(button.getId()); + } + } + + super.addView(child, index, params); + } + + /** + *

Sets the selection to the radio button whose identifier is passed in + * parameter. Using -1 as the selection identifier clears the selection; + * such an operation is equivalent to invoking {@link #clearCheck()}.

+ * + * @param id the unique id of the radio button to select in this group + * + * @see #getCheckedRadioButtonId() + * @see #clearCheck() + */ + public void check(int id) { + // don't even bother + if (id != -1 && (id == mCheckedId)) { + return; + } + + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + + if (id != -1) { + setCheckedStateForView(id, true); + } + + setCheckedId(id); + } + + private void setCheckedId(int id) { + mCheckedId = id; + + if (mOnCheckedChangeListener != null) { + mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId); + } + } + + private void setCheckedStateForView(int viewId, boolean checked) { + View checkedView = findViewById(viewId); + if (checkedView != null && checkedView instanceof RadioButton) { + ((RadioButton) checkedView).setChecked(checked); + } + } + + /** + *

Returns the identifier of the selected radio button in this group. + * Upon empty selection, the returned value is -1.

+ * + * @return the unique id of the selected radio button in this group + * + * @see #check(int) + * @see #clearCheck() + * + * @attr ref android.R.styleable#RadioGroup_checkedButton + */ + public int getCheckedRadioButtonId() { + return mCheckedId; + } + + /** + *

Clears the selection. When the selection is cleared, no radio button + * in this group is selected and {@link #getCheckedRadioButtonId()} returns + * null.

+ * + * @see #check(int) + * @see #getCheckedRadioButtonId() + */ + public void clearCheck() { + check(-1); + } + + /** + *

Register a callback to be invoked when the checked radio button + * changes in this group.

+ * + * @param listener the callback to call on checked state change + */ + public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { + mOnCheckedChangeListener = listener; + } + + /** + * {@inheritDoc} + */ + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new RadioGroup.LayoutParams(getContext(), attrs); + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof RadioGroup.LayoutParams; + } + + @Override + protected LinearLayout.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + public CharSequence getAccessibilityClassName() { + return RadioGroup.class.getName(); + } + + /** + *

This set of layout parameters defaults the width and the height of + * the children to {@link #WRAP_CONTENT} when they are not specified in the + * XML file. Otherwise, this class ussed the value read from the XML file.

+ * + *

See + * {@link android.R.styleable#LinearLayout_Layout LinearLayout Attributes} + * for a list of all child view attributes that this class supports.

+ * + */ + public static class LayoutParams extends LinearLayout.LayoutParams { + /** + * {@inheritDoc} + */ + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + /** + * {@inheritDoc} + */ + public LayoutParams(int w, int h) { + super(w, h); + } + + /** + * {@inheritDoc} + */ + public LayoutParams(int w, int h, float initWeight) { + super(w, h, initWeight); + } + + /** + * {@inheritDoc} + */ + public LayoutParams(ViewGroup.LayoutParams p) { + super(p); + } + + /** + * {@inheritDoc} + */ + public LayoutParams(MarginLayoutParams source) { + super(source); + } + + /** + *

Fixes the child's width to + * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the child's + * height to {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} + * when not specified in the XML file.

+ * + * @param a the styled attributes set + * @param widthAttr the width attribute to fetch + * @param heightAttr the height attribute to fetch + */ + protected void setBaseAttributes(TypedArray a, + int widthAttr, int heightAttr) { + + if (a.hasValue(widthAttr)) { + width = a.getLayoutDimension(widthAttr, "layout_width"); + } else { + width = WRAP_CONTENT; + } + + if (a.hasValue(heightAttr)) { + height = a.getLayoutDimension(heightAttr, "layout_height"); + } else { + height = WRAP_CONTENT; + } + } + } + + /** + *

Interface definition for a callback to be invoked when the checked + * radio button changed in this group.

+ */ + public interface OnCheckedChangeListener { + /** + *

Called when the checked radio button has changed. When the + * selection is cleared, checkedId is -1.

+ * + * @param group the group in which the checked radio button has changed + * @param checkedId the unique identifier of the newly checked radio button + */ + public void onCheckedChanged(RadioGroup group, int checkedId); + } + + private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + // prevents from infinite recursion + if (mProtectFromCheckedChange) { + return; + } + + mProtectFromCheckedChange = true; + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + mProtectFromCheckedChange = false; + + int id = buttonView.getId(); + setCheckedId(id); + } + } + + /** + *

A pass-through listener acts upon the events and dispatches them + * to another listener. This allows the table layout to set its own internal + * hierarchy change listener without preventing the user to setup this.

+ */ + private class PassThroughHierarchyChangeListener implements + ViewGroup.OnHierarchyChangeListener { + private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener; + + /** + * {@inheritDoc} + */ + @Override + public void onChildViewAdded(View parent, View child) { + if (parent == RadioGroup.this && child instanceof RadioButton) { + int id = child.getId(); + // generates an id if it's missing + if (id == View.NO_ID) { + id = View.generateViewId(); + child.setId(id); + } + ((RadioButton) child).setOnCheckedChangeListener( + mChildOnCheckedChangeListener); + } + + if (mOnHierarchyChangeListener != null) { + mOnHierarchyChangeListener.onChildViewAdded(parent, child); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onChildViewRemoved(View parent, View child) { + if (parent == RadioGroup.this && child instanceof RadioButton) { + ((RadioButton) child).setOnCheckedChangeListener(null); + } + + if (mOnHierarchyChangeListener != null) { + mOnHierarchyChangeListener.onChildViewRemoved(parent, child); + } + } + } + + int getIndexWithinVisibleButtons(View child) { + if (!(child instanceof RadioButton)) { + return -1; + } + int index = 0; + for (int i = 0; i < getChildCount(); i++) { + if (this.getChildAt(i) instanceof RadioButton) { + RadioButton button = (RadioButton) this.getChildAt(i); + if (button == child) { + return index; + } + if (isVisibleWithText(button)) { + index++; + } + } + } + return -1; + } + + private boolean isVisibleWithText(RadioButton button) { + return button.getVisibility() == VISIBLE && !TextUtils.isEmpty(button.getText()); + } +} diff --git a/src/api-impl/android/widget/Switch.java b/src/api-impl/android/widget/Switch.java index 8c93a420..aa2eb57c 100644 --- a/src/api-impl/android/widget/Switch.java +++ b/src/api-impl/android/widget/Switch.java @@ -13,4 +13,7 @@ public class Switch extends CompoundButton { super(context, attributeSet); } + public void setTextOn(CharSequence text) {} + public void setTextOff(CharSequence text) {} + } diff --git a/src/api-impl/android/widget/TextView.java b/src/api-impl/android/widget/TextView.java index f35bcd97..39c25336 100644 --- a/src/api-impl/android/widget/TextView.java +++ b/src/api-impl/android/widget/TextView.java @@ -154,9 +154,7 @@ public class TextView extends View { public int getCompoundPaddingTop() {return 0;} public int getCompoundPaddingBottom() {return 0;} - public CharSequence getText() { - return "FIXME"; - } + public native CharSequence getText(); public void setCompoundDrawablePadding(int pad) {} @@ -229,4 +227,8 @@ public class TextView extends View { public int getCompoundPaddingRight() {return 0;} public void setHint(int resId) {} + + public float getLetterSpacing() {return 0.f;} + + public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end, int bottom) {} } diff --git a/src/api-impl/meson.build b/src/api-impl/meson.build index 6668801c..3eeed32b 100644 --- a/src/api-impl/meson.build +++ b/src/api-impl/meson.build @@ -52,6 +52,7 @@ hax_jar = jar('hax', [ 'android/content/ActivityNotFoundException.java', 'android/content/BroadcastReceiver.java', 'android/content/ClipboardManager.java', + 'android/content/ClipData.java', 'android/content/ComponentCallbacks.java', 'android/content/ComponentCallbacks2.java', 'android/content/ComponentName.java', @@ -222,6 +223,7 @@ hax_jar = jar('hax', [ 'android/media/tv/TvInputManager.java', 'android/net/ConnectivityManager.java', 'android/net/NetworkInfo.java', + 'android/net/NetworkRequest.java', 'android/net/Uri.java', 'android/net/http/X509TrustManagerExtensions.java', 'android/net/nsd/NsdManager.java', @@ -428,6 +430,7 @@ hax_jar = jar('hax', [ 'android/widget/PopupWindow.java', 'android/widget/ProgressBar.java', 'android/widget/RadioButton.java', + 'android/widget/RadioGroup.java', 'android/widget/RelativeLayout.java', 'android/widget/RemoteViews.java', 'android/widget/ScrollView.java',