diff --git a/Android.mk b/Android.mk index d67a21e46e897..764e88b394500 100644 --- a/Android.mk +++ b/Android.mk @@ -97,7 +97,9 @@ LOCAL_SRC_FILES += \ core/java/android/view/IWindowManager.aidl \ core/java/android/view/IWindowSession.aidl \ core/java/com/android/internal/app/IBatteryStats.aidl \ + core/java/com/android/internal/app/IUsageStats.aidl \ core/java/com/android/internal/gadget/IGadgetService.aidl \ + core/java/com/android/internal/gadget/IGadgetHost.aidl \ core/java/com/android/internal/view/IInputContext.aidl \ core/java/com/android/internal/view/IInputContextCallback.aidl \ core/java/com/android/internal/view/IInputMethod.aidl \ @@ -173,6 +175,7 @@ aidl_files := \ frameworks/base/core/java/android/view/MotionEvent.aidl \ frameworks/base/core/java/android/view/Surface.aidl \ frameworks/base/core/java/android/view/WindowManager.aidl \ + frameworks/base/core/java/android/widget/RemoteViews.aidl \ frameworks/base/core/java/com/android/internal/view/IInputContext.aidl \ frameworks/base/core/java/com/android/internal/view/IInputMethod.aidl \ frameworks/base/core/java/com/android/internal/view/IInputMethodCallback.aidl \ diff --git a/api/3.xml b/api/3.xml index 9d9ce98b41a04..66cdbe8b1e134 100644 --- a/api/3.xml +++ b/api/3.xml @@ -79027,7 +79027,7 @@ type="java.lang.String" transient="false" volatile="false" - value=""name ASC"" + value=""bucket_display_name"" static="true" final="true" deprecated="not deprecated" @@ -79385,7 +79385,7 @@ type="java.lang.String" transient="false" volatile="false" - value=""name ASC"" + value=""_display_name"" static="true" final="true" deprecated="not deprecated" diff --git a/api/current.xml b/api/current.xml index 935454a19e53e..55994fb6f1d09 100644 --- a/api/current.xml +++ b/api/current.xml @@ -144,6 +144,17 @@ visibility="public" > + + + + + + + + + + + + + + + + + + + + @@ -6602,7 +6712,7 @@ type="int" transient="false" volatile="false" - value="16843330" + value="16843324" static="true" final="true" deprecated="not deprecated" @@ -7511,6 +7621,17 @@ visibility="public" > + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -52106,6 +52770,17 @@ visibility="public" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -57268,6 +58197,8 @@ deprecated="not deprecated" visibility="public" > + + @@ -60857,6 +61788,111 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -88874,6 +91622,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -112186,12 +116200,38 @@ type="android.view.GestureDetector" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -113325,6 +117407,17 @@ visibility="public" > + + + + + + @@ -121889,7 +125995,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > @@ -121933,7 +126039,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > @@ -121944,7 +126050,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > @@ -121959,6 +126065,83 @@ visibility="public" > + + + + + + + + + + + + + + @@ -121999,7 +126182,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > @@ -122010,7 +126193,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > @@ -125315,6 +129498,19 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -130428,7 +134804,9 @@ deprecated="not deprecated" visibility="public" > - + + + + + + + + + + + + + + + + + - - - - - - - - - - @@ -134994,7 +139390,7 @@ synchronized="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > @@ -138293,6 +142689,17 @@ visibility="public" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + & mem) +{ +} + // --------------------------------------------------------------------------- int CameraHardwareStub::beginAutoFocusThread(void *cookie) diff --git a/camera/libcameraservice/CameraHardwareStub.h b/camera/libcameraservice/CameraHardwareStub.h index 9f5ddf13ec655..cdd60116f36ef 100644 --- a/camera/libcameraservice/CameraHardwareStub.h +++ b/camera/libcameraservice/CameraHardwareStub.h @@ -34,6 +34,12 @@ public: virtual status_t startPreview(preview_callback cb, void* user); virtual void stopPreview(); virtual bool previewEnabled(); + + virtual status_t startRecording(recording_callback cb, void* user); + virtual void stopRecording(); + virtual bool recordingEnabled(); + virtual void releaseRecordingFrame(const sp& mem); + virtual status_t autoFocus(autofocus_callback, void *user); virtual status_t takePicture(shutter_callback, raw_callback, diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 36c5ada97a932..e5d42203f5142 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -86,18 +86,19 @@ sp CameraService::connect(const sp& cameraClient) LOGD("Connect E from ICameraClient %p", cameraClient->asBinder().get()); Mutex::Autolock lock(mLock); + sp client; if (mClient != 0) { sp currentClient = mClient.promote(); if (currentClient != 0) { sp currentCameraClient(currentClient->getCameraClient()); if (cameraClient->asBinder() == currentCameraClient->asBinder()) { // this is the same client reconnecting... - LOGD("Connect X same client is reconnecting..."); + LOGD("Connect X same client (%p) is reconnecting...", cameraClient->asBinder().get()); return currentClient; } else { - // it's another client... boot the previous one... - LOGD("new client connecting, booting the old one..."); - mClient.clear(); + // it's another client... reject it + LOGD("new client (%p) attempting to connect - rejected", cameraClient->asBinder().get()); + return client; } } else { // can't promote, the previous client has died... @@ -107,7 +108,7 @@ sp CameraService::connect(const sp& cameraClient) } // create a new Client object - sp client = new Client(this, cameraClient, IPCThreadState::self()->getCallingPid()); + client = new Client(this, cameraClient, IPCThreadState::self()->getCallingPid()); mClient = client; #if DEBUG_CLIENT_REFERENCES // Enable tracking for this object, and track increments and decrements of @@ -158,21 +159,23 @@ CameraService::Client::Client(const sp& cameraService, mCameraClient = cameraClient; mClientPid = clientPid; mHardware = openCameraHardware(); + mUseOverlay = mHardware->useOverlay(); // Callback is disabled by default - mFrameCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; + mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; LOGD("Client X constructor"); } status_t CameraService::Client::checkPid() { if (mClientPid == IPCThreadState::self()->getCallingPid()) return NO_ERROR; - LOGW("Attempt to use locked camera from different process"); + LOGW("Attempt to use locked camera (%p) from different process", getCameraClient()->asBinder().get()); return -EBUSY; } status_t CameraService::Client::lock() { + Mutex::Autolock _l(mLock); // lock camera to this client if the the camera is unlocked if (mClientPid == 0) { mClientPid = IPCThreadState::self()->getCallingPid(); @@ -184,8 +187,9 @@ status_t CameraService::Client::lock() status_t CameraService::Client::unlock() { + Mutex::Autolock _l(mLock); // allow anyone to use camera - LOGV("unlock"); + LOGV("unlock (%p)", getCameraClient()->asBinder().get()); status_t result = checkPid(); if (result == NO_ERROR) mClientPid = 0; return result; @@ -194,27 +198,40 @@ status_t CameraService::Client::unlock() status_t CameraService::Client::connect(const sp& client) { // connect a new process to the camera - LOGV("connect"); + LOGV("connect (%p)", client->asBinder().get()); - // hold a reference to the old client or we will deadlock if the client is - // in the same process and we hold the lock when we remove the reference - sp oldClient; + // I hate this hack, but things get really ugly when the media recorder + // service is handing back the camera to the app. The ICameraClient + // destructor will be called during the same IPC, making it look like + // the remote client is trying to disconnect. This hack temporarily + // sets the mClientPid to an invalid pid to prevent the hardware from + // being torn down. { - Mutex::Autolock _l(mLock); - if (mClientPid != 0) { - LOGW("Tried to connect to locked camera"); - return -EBUSY; + + // hold a reference to the old client or we will deadlock if the client is + // in the same process and we hold the lock when we remove the reference + sp oldClient; + { + Mutex::Autolock _l(mLock); + if (mClientPid != 0) { + LOGW("Tried to connect to locked camera"); + return -EBUSY; + } + oldClient = mCameraClient; + + // did the client actually change? + if (client->asBinder() == mCameraClient->asBinder()) return NO_ERROR; + + mCameraClient = client; + mClientPid = -1; + mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; + LOGV("connect new process (%d) to existing camera client", mClientPid); } - oldClient = mCameraClient; - // did the client actually change? - if (client->asBinder() == mCameraClient->asBinder()) return NO_ERROR; - - LOGV("connect new process to existing camera client"); - mCameraClient = client; - mClientPid = IPCThreadState::self()->getCallingPid(); - mFrameCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; } + // the old client destructor is called when oldClient goes out of scope + // now we set the new PID to lock the interface again + mClientPid = IPCThreadState::self()->getCallingPid(); return NO_ERROR; } @@ -232,8 +249,8 @@ static void *unregister_surface(void *arg) CameraService::Client::~Client() { // tear down client - LOGD("Client E destructor"); - if (mSurface != 0) { + LOGD("Client (%p) E destructor", getCameraClient()->asBinder().get()); + if (mSurface != 0 && !mUseOverlay) { #if HAVE_ANDROID_OS pthread_t thr; // We unregister the buffers in a different thread because binder does @@ -244,7 +261,7 @@ CameraService::Client::~Client() mSurface.get()); pthread_join(thr, NULL); #else - mSurface->unregisterBuffers(); + mSurface->unregisterBuffers(); #endif } @@ -256,16 +273,22 @@ CameraService::Client::~Client() void CameraService::Client::disconnect() { - LOGD("Client E disconnect"); + LOGD("Client (%p) E disconnect from (%d)", + getCameraClient()->asBinder().get(), + IPCThreadState::self()->getCallingPid()); Mutex::Autolock lock(mLock); - if (mClientPid == 0) { + if (mClientPid <= 0) { LOGV("camera is unlocked, don't tear down hardware"); return; } - if (checkPid() != NO_ERROR) return; + if (checkPid() != NO_ERROR) { + LOGV("Different client - don't disconnect"); + return; + } mCameraService->removeClient(mCameraClient); if (mHardware != 0) { + LOGV("hardware teardown"); // Before destroying mHardware, we must make sure it's in the // idle state. mHardware->stopPreview(); @@ -288,7 +311,7 @@ status_t CameraService::Client::setPreviewDisplay(const sp& surface) Mutex::Autolock surfaceLock(mSurfaceLock); // asBinder() is safe on NULL (returns NULL) if (surface->asBinder() != mSurface->asBinder()) { - if (mSurface != 0) { + if (mSurface != 0 && !mUseOverlay) { LOGD("clearing old preview surface %p", mSurface.get()); mSurface->unregisterBuffers(); } @@ -297,19 +320,20 @@ status_t CameraService::Client::setPreviewDisplay(const sp& surface) return NO_ERROR; } -// set the frame callback flag to affect how the received frames from +// set the preview callback flag to affect how the received frames from // preview are handled. -void CameraService::Client::setFrameCallbackFlag(int frame_callback_flag) +void CameraService::Client::setPreviewCallbackFlag(int callback_flag) { + LOGV("setPreviewCallbackFlag"); Mutex::Autolock lock(mLock); if (checkPid() != NO_ERROR) return; - mFrameCallbackFlag = frame_callback_flag; + mPreviewCallbackFlag = callback_flag; } // start preview mode, must call setPreviewDisplay first -status_t CameraService::Client::startPreview() +status_t CameraService::Client::startCameraMode(camera_mode mode) { - LOGD("startPreview()"); + LOGD("startCameraMode(%d)", mode); /* we cannot call into mHardware with mLock held because * mHardware has callbacks onto us which acquire this lock @@ -325,37 +349,124 @@ status_t CameraService::Client::startPreview() } if (mSurface == 0) { - LOGE("setPreviewDisplay must be called before startPreview!"); + LOGE("setPreviewDisplay must be called before startCameraMode!"); return INVALID_OPERATION; } - // do nothing if preview is already started - if (mHardware->previewEnabled()) return NO_ERROR; + switch(mode) { + case CAMERA_RECORDING_MODE: + return startRecordingMode(); - // XXX: This needs to be improved. remove all hardcoded stuff + default: // CAMERA_PREVIEW_MODE + return startPreviewMode(); + } +} +status_t CameraService::Client::startRecordingMode() +{ + LOGV("startRecordingMode"); + + status_t ret = UNKNOWN_ERROR; + + // if preview has not been started, start preview first + if (!mHardware->previewEnabled()) { + ret = startPreviewMode(); + if (ret != NO_ERROR) { + return ret; + } + } + + // if recording has been enabled, nothing needs to be done + if (mHardware->recordingEnabled()) { + return NO_ERROR; + } + + // start recording mode + ret = mHardware->startRecording(recordingCallback, + mCameraService.get()); + if (ret != NO_ERROR) { + LOGE("mHardware->startRecording() failed with status %d", ret); + } + return ret; +} + +status_t CameraService::Client::startPreviewMode() +{ + LOGV("startPreviewMode"); + + // if preview has been enabled, nothing needs to be done + if (mHardware->previewEnabled()) { + return NO_ERROR; + } + + // start preview mode +#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE + debug_frame_cnt = 0; +#endif + status_t ret = UNKNOWN_ERROR; int w, h; CameraParameters params(mHardware->getParameters()); params.getPreviewSize(&w, &h); -#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE - debug_frame_cnt = 0; -#endif + if (mUseOverlay) { + const char *format = params.getPreviewFormat(); + int fmt; + LOGD("Use Overlays"); + if (!strcmp(format, "yuv422i")) + fmt = OVERLAY_FORMAT_YCbCr_422_I; + else if (!strcmp(format, "rgb565")) + fmt = OVERLAY_FORMAT_RGB_565; + else { + LOGE("Invalid preview format for overlays"); + return -EINVAL; + } + sp ref = mSurface->createOverlay(w, h, fmt); + ret = mHardware->setOverlay(new Overlay(ref)); + if (ret != NO_ERROR) { + LOGE("mHardware->setOverlay() failed with status %d\n", ret); + return ret; + } + ret = mHardware->startPreview(NULL, mCameraService.get()); + if (ret != NO_ERROR) + LOGE("mHardware->startPreview() failed with status %d\n", ret); + + } else { + ret = mHardware->startPreview(previewCallback, + mCameraService.get()); + if (ret == NO_ERROR) { - status_t ret = mHardware->startPreview(previewCallback, - mCameraService.get()); - if (ret == NO_ERROR) { - mSurface->unregisterBuffers(); - mSurface->registerBuffers(w,h,w,h, - PIXEL_FORMAT_YCbCr_420_SP, - mHardware->getPreviewHeap()); + mSurface->unregisterBuffers(); + + uint32_t transform = 0; + if (params.getOrientation() == + CameraParameters::CAMERA_ORIENTATION_PORTRAIT) { + LOGV("portrait mode"); + transform = ISurface::BufferHeap::ROT_90; + } + ISurface::BufferHeap buffers(w, h, w, h, + PIXEL_FORMAT_YCbCr_420_SP, + transform, + 0, + mHardware->getPreviewHeap()); + + mSurface->registerBuffers(buffers); + } else { + LOGE("mHardware->startPreview() failed with status %d", ret); + } } - else LOGE("mHardware->startPreview() failed with status %d\n", - ret); - return ret; } +status_t CameraService::Client::startPreview() +{ + return startCameraMode(CAMERA_PREVIEW_MODE); +} + +status_t CameraService::Client::startRecording() +{ + return startCameraMode(CAMERA_RECORDING_MODE); +} + // stop preview mode void CameraService::Client::stopPreview() { @@ -372,12 +483,46 @@ void CameraService::Client::stopPreview() mHardware->stopPreview(); LOGD("stopPreview(), hardware stopped OK"); - if (mSurface != 0) { + if (mSurface != 0 && !mUseOverlay) { mSurface->unregisterBuffers(); } mPreviewBuffer.clear(); } +// stop recording mode +void CameraService::Client::stopRecording() +{ + LOGV("stopRecording()"); + + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return; + } + + mHardware->stopRecording(); + LOGV("stopRecording(), hardware stopped OK"); + mPreviewBuffer.clear(); +} + +// release a recording frame +void CameraService::Client::releaseRecordingFrame(const sp& mem) +{ + LOGV("releaseRecordingFrame()"); + + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return; + } + + mHardware->releaseRecordingFrame(mem); +} + bool CameraService::Client::previewEnabled() { Mutex::Autolock lock(mLock); @@ -385,6 +530,13 @@ bool CameraService::Client::previewEnabled() return mHardware->previewEnabled(); } +bool CameraService::Client::recordingEnabled() +{ + Mutex::Autolock lock(mLock); + if (mHardware == 0) return false; + return mHardware->recordingEnabled(); +} + // Safely retrieves a strong pointer to the client during a hardware callback. sp CameraService::Client::getClientFromCookie(void* user) { @@ -476,7 +628,7 @@ void CameraService::Client::previewCallback(const sp& mem, void* user) #endif // The strong pointer guarantees the client will exist, but no lock is held. - client->postFrame(mem); + client->postPreviewFrame(mem); #if DEBUG_CLIENT_REFERENCES //**** if the client's refcount is 1, then we are about to destroy it here, @@ -488,6 +640,18 @@ void CameraService::Client::previewCallback(const sp& mem, void* user) #endif } +// recording callback +void CameraService::Client::recordingCallback(const sp& mem, void* user) +{ + LOGV("recordingCallback"); + sp client = getClientFromCookie(user); + if (client == 0) { + return; + } + // The strong pointer guarantees the client will exist, but no lock is held. + client->postRecordingFrame(mem); +} + // take a picture - image is returned in callback status_t CameraService::Client::autoFocus() { @@ -520,7 +684,7 @@ status_t CameraService::Client::takePicture() return INVALID_OPERATION; } - if (mSurface != NULL) + if (mSurface != NULL && !mUseOverlay) mSurface->unregisterBuffers(); return mHardware->takePicture(shutterCallback, @@ -573,10 +737,18 @@ void CameraService::Client::yuvPictureCallback(const sp& mem, params.getPictureSize(&w, &h); // Mutex::Autolock clientLock(client->mLock); - if (client->mSurface != 0) { + if (client->mSurface != 0 && !client->mUseOverlay) { client->mSurface->unregisterBuffers(); - client->mSurface->registerBuffers(w,h,w,h, - PIXEL_FORMAT_YCbCr_420_SP, heap); + + uint32_t transform = 0; + if (params.getOrientation() == CameraParameters::CAMERA_ORIENTATION_PORTRAIT) { + LOGV("portrait mode"); + transform = ISurface::BufferHeap::ROT_90; + } + ISurface::BufferHeap buffers(w, h, w, h, + PIXEL_FORMAT_YCbCr_420_SP, transform, 0, heap); + + client->mSurface->registerBuffers(buffers); client->mSurface->postBuffer(offset); } @@ -730,12 +902,22 @@ void CameraService::Client::copyFrameAndPostCopiedFrame(sp heap, si LOGE("failed to allocate space for frame callback"); return; } - mCameraClient->frameCallback(frame); + mCameraClient->previewCallback(frame); } -void CameraService::Client::postFrame(const sp& mem) +void CameraService::Client::postRecordingFrame(const sp& frame) { - LOGV("postFrame"); + LOGV("postRecordingFrame"); + if (frame == 0) { + LOGW("frame is a null pointer"); + return; + } + mCameraClient->recordingCallback(frame); +} + +void CameraService::Client::postPreviewFrame(const sp& mem) +{ + LOGV("postPreviewFrame"); if (mem == 0) { LOGW("mem is a null pointer"); return; @@ -752,31 +934,32 @@ void CameraService::Client::postFrame(const sp& mem) } // Is the callback enabled or not? - if (!(mFrameCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)) { + if (!(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)) { // If the enable bit is off, the copy-out and one-shot bits are ignored LOGV("frame callback is diabled"); return; } // Is the received frame copied out or not? - if (mFrameCallbackFlag & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) { + if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) { LOGV("frame is copied out"); copyFrameAndPostCopiedFrame(heap, offset, size); } else { LOGV("frame is directly sent out without copying"); - mCameraClient->frameCallback(mem); + mCameraClient->previewCallback(mem); } // Is this is one-shot only? - if (mFrameCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) { + if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) { LOGV("One-shot only, thus clear the bits and disable frame callback"); - mFrameCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK | + mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK | FRAME_CALLBACK_FLAG_COPY_OUT_MASK | FRAME_CALLBACK_FLAG_ENABLE_MASK); } } -void CameraService::Client::postError(status_t error) { +void CameraService::Client::postError(status_t error) +{ mCameraClient->errorCallback(error); } @@ -796,6 +979,11 @@ status_t CameraService::dump(int fd, const Vector& args) AutoMutex lock(&mLock); if (mClient != 0) { sp currentClient = mClient.promote(); + sprintf(buffer, "Client (%p) PID: %d", + currentClient->getCameraClient()->asBinder().get(), + currentClient->mClientPid); + result.append(buffer); + write(fd, result.string(), result.size()); currentClient->mHardware->dump(fd, args); } else { result.append("No camera client yet.\n"); @@ -880,5 +1068,3 @@ status_t CameraService::onTransact( #endif // DEBUG_HEAP_LEAKS }; // namespace android - - diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h index cd8c1e9a41cfa..d9b79276a72a8 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -82,9 +82,9 @@ private: // pass the buffered ISurface to the camera service virtual status_t setPreviewDisplay(const sp& surface); - // set the frame callback flag to affect how the received frames from + // set the preview callback flag to affect how the received frames from // preview are handled. - virtual void setFrameCallbackFlag(int frame_callback_flag); + virtual void setPreviewCallbackFlag(int callback_flag); // start preview mode, must call setPreviewDisplay first virtual status_t startPreview(); @@ -95,6 +95,18 @@ private: // get preview state virtual bool previewEnabled(); + // start recording mode + virtual status_t startRecording(); + + // stop recording mode + virtual void stopRecording(); + + // get recording state + virtual bool recordingEnabled(); + + // release a recording frame + virtual void releaseRecordingFrame(const sp& mem); + // auto focus virtual status_t autoFocus(); @@ -120,6 +132,7 @@ private: status_t checkPid(); + static void recordingCallback(const sp& mem, void* user); static void previewCallback(const sp& mem, void* user); static void shutterCallback(void *user); static void yuvPictureCallback(const sp& mem, void* user); @@ -130,17 +143,28 @@ private: void postShutter(); void postRaw(const sp& mem); void postJpeg(const sp& mem); - void postFrame(const sp& mem); + void postPreviewFrame(const sp& mem); + void postRecordingFrame(const sp& frame); void copyFrameAndPostCopiedFrame(sp heap, size_t offset, size_t size); void postError(status_t error); void postAutoFocus(bool focused); - // Ensures atomicity among the public methods + // camera operation mode + enum camera_mode { + CAMERA_PREVIEW_MODE = 0, // frame automatically released + CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame() + }; + status_t startCameraMode(camera_mode mode); + status_t startPreviewMode(); + status_t startRecordingMode(); + + // Ensures atomicity among the public methods mutable Mutex mLock; + // mSurfaceLock synchronizes access to mSurface between - // setPreviewSurface() and postFrame(). Note that among + // setPreviewSurface() and postPreviewFrame(). Note that among // the public methods, all accesses to mSurface are - // syncrhonized by mLock. However, postFrame() is called + // syncrhonized by mLock. However, postPreviewFrame() is called // by the CameraHardwareInterface callback, and needs to // access mSurface. It cannot hold mLock, however, because // stopPreview() may be holding that lock while attempting @@ -152,13 +176,14 @@ private: sp mCameraService; sp mSurface; sp mPreviewBuffer; - int mFrameCallbackFlag; + int mPreviewCallbackFlag; // these are immutable once the object is created, // they don't need to be protected by a lock sp mCameraClient; sp mHardware; pid_t mClientPid; + bool mUseOverlay; }; // ---------------------------------------------------------------------------- diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index dea269dcf3bed..4621f579a2f8b 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -224,6 +224,8 @@ int main(int argc, char *argv[]) { } if (compress) strcat(path, ".gz"); + else + strcat(path, ".txt"); /* ensure that all directories in the path exist */ create_directories(path); diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index a3651b23e5190..70a1206b11ecb 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -413,7 +413,8 @@ int create_cache_path(char path[PKG_PATH_MAX], const char *src) return 0; } -static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name) +static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name, + const char* dexopt_flags) { static const char* DEX_OPT_BIN = "/system/bin/dexopt"; static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig @@ -424,7 +425,7 @@ static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name) sprintf(odex_num, "%d", odex_fd); execl(DEX_OPT_BIN, DEX_OPT_BIN, "--zip", zip_num, odex_num, input_file_name, - (char*) NULL); + dexopt_flags, (char*) NULL); LOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno)); } @@ -465,6 +466,7 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) struct utimbuf ut; struct stat apk_stat, dex_stat; char dex_path[PKG_PATH_MAX]; + char dexopt_flags[PROPERTY_VALUE_MAX]; char *end; int res, zip_fd=-1, odex_fd=-1; @@ -475,6 +477,9 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) return -1; } + /* platform-specific flags affecting optimization and verification */ + property_get("dalvik.vm.dexopt-flags", dexopt_flags, ""); + strcpy(dex_path, apk_path); end = strrchr(dex_path, '.'); if (end != NULL) { @@ -533,8 +538,8 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) exit(66); } - run_dexopt(zip_fd, odex_fd, apk_path); /* does not return */ - exit(67); + run_dexopt(zip_fd, odex_fd, apk_path, dexopt_flags); + exit(67); /* only get here on exec failure */ } else { res = wait_dexopt(pid, apk_path); if (res != 0) { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4dc4b6a48b85f..8236943a3649d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -53,6 +53,7 @@ import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewManager; import android.view.Window; import android.view.WindowManager; import android.view.ContextMenu.ContextMenuInfo; @@ -93,11 +94,11 @@ import java.util.HashMap; * {@link android.R.styleable#AndroidManifestActivity <activity>} * declaration in their package's AndroidManifest.xml.

* - *

The Activity class is an important part of an - * application's overall lifecycle, + *

The Activity class is an important part of an application's overall lifecycle, * and the way activities are launched and put together is a fundamental - * part of the platform's - * application model.

+ * part of the platform's application model. For a detailed perspective on the structure of + * Android applications and lifecycles, please read the Dev Guide document on + * Application Fundamentals.

* *

Topics covered here: *

    @@ -527,7 +528,7 @@ import java.util.HashMap; * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} * element in their own manifest to be able to start that activity. * - *

    See the Security Model + *

    See the Security and Permissions * document for more information on permissions and security in general. * * @@ -629,6 +630,9 @@ public class Activity extends ContextThemeWrapper private WindowManager mWindowManager; /*package*/ View mDecor = null; + /*package*/ boolean mWindowAdded = false; + /*package*/ boolean mVisibleFromServer = false; + /*package*/ boolean mVisibleFromClient = true; private CharSequence mTitle; private int mTitleColor = 0; @@ -779,6 +783,8 @@ public class Activity extends ContextThemeWrapper * @see #onPostCreate */ protected void onCreate(Bundle savedInstanceState) { + mVisibleFromClient = mWindow.getWindowStyle().getBoolean( + com.android.internal.R.styleable.Window_windowNoDisplay, true); mCalled = true; } @@ -1134,12 +1140,19 @@ public class Activity extends ContextThemeWrapper /** * Called as part of the activity lifecycle when an activity is about to go * into the background as the result of user choice. For example, when the - * user presses the Home key, {@link #onUserLeaving} will be called, but + * user presses the Home key, {@link #onUserLeaveHint} will be called, but * when an incoming phone call causes the in-call Activity to be automatically - * brought to the foreground, {@link #onUserLeaving} will not be called on - * the activity being interrupted. + * brought to the foreground, {@link #onUserLeaveHint} will not be called on + * the activity being interrupted. In cases when it is invoked, this method + * is called right before the activity's {@link #onPause} callback. + * + *

    This callback and {@link #onUserInteraction} are intended to help + * activities manage status bar notifications intelligently; specifically, + * for helping activities determine the proper time to cancel a notfication. + * + * @see #onUserInteraction() */ - protected void onUserLeaving() { + protected void onUserLeaveHint() { } /** @@ -1443,7 +1456,6 @@ public class Activity extends ContextThemeWrapper * @return The Cursor that was returned by query(). * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) - * @see #managedCommitUpdates * @see #startManagingCursor * @hide */ @@ -1475,7 +1487,6 @@ public class Activity extends ContextThemeWrapper * @return The Cursor that was returned by query(). * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) - * @see #managedCommitUpdates * @see #startManagingCursor */ public final Cursor managedQuery(Uri uri, @@ -1863,6 +1874,28 @@ public class Activity extends ContextThemeWrapper return false; } + /** + * Called whenever a key, touch, or trackball event is dispatched to the + * activity. Implement this method if you wish to know that the user has + * interacted with the device in some way while your activity is running. + * This callback and {@link #onUserLeaveHint} are intended to help + * activities manage status bar notifications intelligently; specifically, + * for helping activities determine the proper time to cancel a notfication. + * + *

    All calls to your activity's {@link #onUserLeaveHint} callback will + * be accompanied by calls to {@link #onUserInteraction}. This + * ensures that your activity will be told of relevant user activity such + * as pulling down the notification pane and touching an item there. + * + *

    Note that this callback will be invoked for the touch down action + * that begins a touch gesture, but may not be invoked for the touch-moved + * and touch-up actions that follow. + * + * @see #onUserLeaveHint() + */ + public void onUserInteraction() { + } + public void onWindowAttributesChanged(WindowManager.LayoutParams params) { // Update window manager if: we have a view, that view is // attached to its parent (which will be a RootView), and @@ -1935,6 +1968,7 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchKeyEvent(KeyEvent event) { + onUserInteraction(); if (getWindow().superDispatchKeyEvent(event)) { return true; } @@ -1952,6 +1986,9 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + onUserInteraction(); + } if (getWindow().superDispatchTouchEvent(ev)) { return true; } @@ -1969,6 +2006,7 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchTrackballEvent(MotionEvent ev) { + onUserInteraction(); if (getWindow().superDispatchTrackballEvent(ev)) { return true; } @@ -2864,6 +2902,35 @@ public class Activity extends ContextThemeWrapper } } + /** + * Control whether this activity's main window is visible. This is intended + * only for the special case of an activity that is not going to show a + * UI itself, but can't just finish prior to onResume() because it needs + * to wait for a service binding or such. Setting this to false allows + * you to prevent your UI from being shown during that time. + * + *

    The default value for this is taken from the + * {@link android.R.attr#windowNoDisplay} attribute of the activity's theme. + */ + public void setVisible(boolean visible) { + if (mVisibleFromClient != visible) { + mVisibleFromClient = visible; + if (mVisibleFromServer) { + if (visible) makeVisible(); + else mDecor.setVisibility(View.INVISIBLE); + } + } + } + + void makeVisible() { + if (!mWindowAdded) { + ViewManager wm = getWindowManager(); + wm.addView(mDecor, getWindow().getAttributes()); + mWindowAdded = true; + } + mDecor.setVisibility(View.VISIBLE); + } + /** * Check to see whether this activity is in the process of finishing, * either because you called {@link #finish} on it or someone else @@ -3482,7 +3549,8 @@ public class Activity extends ContextThemeWrapper } final void performUserLeaving() { - onUserLeaving(); + onUserInteraction(); + onUserLeaveHint(); } final void performStop() { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index f9b92218c56a9..07520c9d65f7d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -19,16 +19,14 @@ package android.app; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.graphics.Bitmap; import android.os.RemoteException; import android.os.Handler; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.os.Parcelable.Creator; import android.text.TextUtils; -import android.util.Log; import java.util.List; /** @@ -617,7 +615,59 @@ public class ActivityManager { public String pkgList[]; + /** + * Constant for {@link #importance}: this process is running the + * foreground UI. + */ + public static final int IMPORTANCE_FOREGROUND = 100; + + /** + * Constant for {@link #importance}: this process is running something + * that is considered to be actively visible to the user. + */ + public static final int IMPORTANCE_VISIBLE = 200; + + /** + * Constant for {@link #importance}: this process is contains services + * that should remain running. + */ + public static final int IMPORTANCE_SERVICE = 300; + + /** + * Constant for {@link #importance}: this process process contains + * background code that is expendable. + */ + public static final int IMPORTANCE_BACKGROUND = 400; + + /** + * Constant for {@link #importance}: this process is empty of any + * actively running code. + */ + public static final int IMPORTANCE_EMPTY = 500; + + /** + * The relative importance level that the system places on this + * process. May be one of {@link #IMPORTANCE_FOREGROUND}, + * {@link #IMPORTANCE_VISIBLE}, {@link #IMPORTANCE_SERVICE}, + * {@link #IMPORTANCE_BACKGROUND}, or {@link #IMPORTANCE_EMPTY}. These + * constants are numbered so that "more important" values are always + * smaller than "less important" values. + */ + public int importance; + + /** + * An additional ordering within a particular {@link #importance} + * category, providing finer-grained information about the relative + * utility of processes within a category. This number means nothing + * except that a smaller values are more recently used (and thus + * more important). Currently an LRU value is only maintained for + * the {@link #IMPORTANCE_BACKGROUND} category, though others may + * be maintained in the future. + */ + public int lru; + public RunningAppProcessInfo() { + importance = IMPORTANCE_FOREGROUND; } public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) { @@ -634,12 +684,16 @@ public class ActivityManager { dest.writeString(processName); dest.writeInt(pid); dest.writeStringArray(pkgList); + dest.writeInt(importance); + dest.writeInt(lru); } public void readFromParcel(Parcel source) { processName = source.readString(); pid = source.readInt(); pkgList = source.readStringArray(); + importance = source.readInt(); + lru = source.readInt(); } public static final Creator CREATOR = @@ -671,4 +725,37 @@ public class ActivityManager { return null; } } + + /** + * Have the system perform a force stop of everything associated with + * the given application package. All processes that share its uid + * will be killed, all services it has running stopped, all activities + * removed, etc. In addition, a {@link Intent#ACTION_PACKAGE_RESTARTED} + * broadcast will be sent, so that any of its registered alarms can + * be stopped, notifications removed, etc. + * + *

    You must hold the permission + * {@link android.Manifest.permission#RESTART_PACKAGES} to be able to + * call this method. + * + * @param packageName The name of the package to be stopped. + */ + public void restartPackage(String packageName) { + try { + ActivityManagerNative.getDefault().restartPackage(packageName); + } catch (RemoteException e) { + } + } + + /** + * Get the device configuration attributes. + */ + public ConfigurationInfo getDeviceConfigurationInfo() { + try { + return ActivityManagerNative.getDefault().getDeviceConfigurationInfo(); + } catch (RemoteException e) { + } + return null; + } + } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index ae9f3bf15029c..f11dbec3b422e 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.res.Configuration; import android.graphics.Bitmap; @@ -82,6 +83,17 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return gDefault; } + /** + * Convenience for checking whether the system is ready. For internal use only. + */ + static public boolean isSystemReady() { + if (!sSystemReady) { + sSystemReady = getDefault().testIsSystemReady(); + } + return sSystemReady; + } + static boolean sSystemReady = false; + /** * Convenience for sending a sticky broadcast. For internal use only. * If you don't care about permission, use null. @@ -959,6 +971,23 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_DEVICE_CONFIGURATION_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + ConfigurationInfo config = getDeviceConfigurationInfo(); + reply.writeNoException(); + config.writeToParcel(reply, 0); + return true; + } + + case PEEK_SERVICE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + Intent service = Intent.CREATOR.createFromParcel(data); + String resolvedType = data.readString(); + IBinder binder = peekService(service, resolvedType); + reply.writeNoException(); + reply.writeStrongBinder(binder); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -1604,6 +1633,20 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + + public IBinder peekService(Intent service, String resolvedType) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + service.writeToParcel(data, 0); + data.writeString(resolvedType); + mRemote.transact(PEEK_SERVICE_TRANSACTION, data, reply, 0); + reply.readException(); + IBinder binder = reply.readStrongBinder(); + reply.recycle(); + data.recycle(); + return binder; + } public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) @@ -2028,6 +2071,11 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + public boolean testIsSystemReady() + { + /* this base class version is never called */ + return true; + } public int handleApplicationError(IBinder app, int flags, String tag, String shortMsg, String longMsg, byte[] crashData) throws RemoteException @@ -2071,5 +2119,17 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(GET_DEVICE_CONFIGURATION_TRANSACTION, data, reply, 0); + reply.readException(); + ConfigurationInfo res = ConfigurationInfo.CREATOR.createFromParcel(reply); + reply.recycle(); + data.recycle(); + return res; + } private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e4c1057e3878d..bf5616e55a372 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -144,13 +144,19 @@ public final class ActivityThread { return sPackageManager; } - DisplayMetrics getDisplayMetricsLocked() { + DisplayMetrics getDisplayMetricsLocked(boolean forceUpdate) { + if (mDisplayMetrics != null && !forceUpdate) { + return mDisplayMetrics; + } if (mDisplay == null) { WindowManager wm = WindowManagerImpl.getDefault(); mDisplay = wm.getDefaultDisplay(); } - DisplayMetrics metrics = new DisplayMetrics(); + DisplayMetrics metrics = mDisplayMetrics = new DisplayMetrics(); mDisplay.getMetrics(metrics); + //Log.i("foo", "New metrics: w=" + metrics.widthPixels + " h=" + // + metrics.heightPixels + " den=" + metrics.density + // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi); return metrics; } @@ -173,7 +179,7 @@ public final class ActivityThread { if (assets.addAssetPath(appDir) == 0) { return null; } - DisplayMetrics metrics = getDisplayMetricsLocked(); + DisplayMetrics metrics = getDisplayMetricsLocked(false); r = new Resources(assets, metrics, getConfiguration()); //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration()); // XXX need to remove entries when weak references go away @@ -235,7 +241,7 @@ public final class ActivityThread { ApplicationContext.createSystemContext(mainThread); mSystemContext.getResources().updateConfiguration( mainThread.getConfiguration(), - mainThread.getDisplayMetricsLocked()); + mainThread.getDisplayMetricsLocked(false)); //Log.i(TAG, "Created system resources " // + mSystemContext.getResources() + ": " // + mSystemContext.getResources().getConfiguration()); @@ -1205,7 +1211,10 @@ public final class ActivityThread { private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%17s %8d"; private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d"; - + + // Formatting for checkin service - update version if row format changes + private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1; + public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges) { queueOrSendMessage( @@ -1462,7 +1471,101 @@ public final class ActivityThread { long dalvikMax = runtime.totalMemory() / 1024; long dalvikFree = runtime.freeMemory() / 1024; long dalvikAllocated = dalvikMax - dalvikFree; - + long viewInstanceCount = ViewDebug.getViewInstanceCount(); + long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount(); + long appContextInstanceCount = ApplicationContext.getInstanceCount(); + long activityInstanceCount = Activity.getInstanceCount(); + int globalAssetCount = AssetManager.getGlobalAssetCount(); + int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount(); + int binderLocalObjectCount = Debug.getBinderLocalObjectCount(); + int binderProxyObjectCount = Debug.getBinderProxyObjectCount(); + int binderDeathObjectCount = Debug.getBinderDeathObjectCount(); + int openSslSocketCount = OpenSSLSocketImpl.getInstanceCount(); + long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024; + SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats(); + SQLiteDebug.getPagerStats(stats); + + // Check to see if we were called by checkin server. If so, print terse format. + boolean doCheckinFormat = false; + if (args != null) { + for (String arg : args) { + if ("-c".equals(arg)) doCheckinFormat = true; + } + } + + // For checkin, we print one long comma-separated list of values + if (doCheckinFormat) { + // NOTE: if you change anything significant below, also consider changing + // ACTIVITY_THREAD_CHECKIN_VERSION. + String processName = (mBoundApplication != null) + ? mBoundApplication.processName : "unknown"; + + // Header + pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); + pw.print(Process.myPid()); pw.print(','); + pw.print(processName); pw.print(','); + + // Heap info - max + pw.print(nativeMax); pw.print(','); + pw.print(dalvikMax); pw.print(','); + pw.print("N/A,"); + pw.print(nativeMax + dalvikMax); pw.print(','); + + // Heap info - allocated + pw.print(nativeAllocated); pw.print(','); + pw.print(dalvikAllocated); pw.print(','); + pw.print("N/A,"); + pw.print(nativeAllocated + dalvikAllocated); pw.print(','); + + // Heap info - free + pw.print(nativeFree); pw.print(','); + pw.print(dalvikFree); pw.print(','); + pw.print("N/A,"); + pw.print(nativeFree + dalvikFree); pw.print(','); + + // Heap info - proportional set size + pw.print(memInfo.nativePss); pw.print(','); + pw.print(memInfo.dalvikPss); pw.print(','); + pw.print(memInfo.otherPss); pw.print(','); + pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(','); + + // Heap info - shared + pw.print(nativeShared); pw.print(','); + pw.print(dalvikShared); pw.print(','); + pw.print(otherShared); pw.print(','); + pw.print(nativeShared + dalvikShared + otherShared); pw.print(','); + + // Heap info - private + pw.print(nativePrivate); pw.print(','); + pw.print(dalvikPrivate); pw.print(','); + pw.print(otherPrivate); pw.print(','); + pw.print(nativePrivate + dalvikPrivate + otherPrivate); pw.print(','); + + // Object counts + pw.print(viewInstanceCount); pw.print(','); + pw.print(viewRootInstanceCount); pw.print(','); + pw.print(appContextInstanceCount); pw.print(','); + pw.print(activityInstanceCount); pw.print(','); + + pw.print(globalAssetCount); pw.print(','); + pw.print(globalAssetManagerCount); pw.print(','); + pw.print(binderLocalObjectCount); pw.print(','); + pw.print(binderProxyObjectCount); pw.print(','); + + pw.print(binderDeathObjectCount); pw.print(','); + pw.print(openSslSocketCount); pw.print(','); + + // SQL + pw.print(sqliteAllocated); pw.print(','); + pw.print(stats.databaseBytes / 1024); pw.print(','); + pw.print(stats.numPagers); pw.print(','); + pw.print((stats.totalBytes - stats.referencedBytes) / 1024); pw.print(','); + pw.print(stats.referencedBytes / 1024); pw.print('\n'); + + return; + } + + // otherwise, show human-readable format printRow(pw, HEAP_COLUMN, "", "native", "dalvik", "other", "total"); printRow(pw, HEAP_COLUMN, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax); printRow(pw, HEAP_COLUMN, "allocated:", nativeAllocated, dalvikAllocated, "N/A", @@ -1480,26 +1583,22 @@ public final class ActivityThread { pw.println(" "); pw.println(" Objects"); - printRow(pw, TWO_COUNT_COLUMNS, "Views:", ViewDebug.getViewInstanceCount(), "ViewRoots:", - ViewDebug.getViewRootInstanceCount()); + printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRoots:", + viewRootInstanceCount); - printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", ApplicationContext.getInstanceCount(), - "Activities:", Activity.getInstanceCount()); + printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount, + "Activities:", activityInstanceCount); - printRow(pw, TWO_COUNT_COLUMNS, "Assets:", AssetManager.getGlobalAssetCount(), - "AssetManagers:", AssetManager.getGlobalAssetManagerCount()); + printRow(pw, TWO_COUNT_COLUMNS, "Assets:", globalAssetCount, + "AssetManagers:", globalAssetManagerCount); - printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", Debug.getBinderLocalObjectCount(), - "Proxy Binders:", Debug.getBinderProxyObjectCount()); - printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", Debug.getBinderDeathObjectCount()); - - printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", OpenSSLSocketImpl.getInstanceCount()); + printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", binderLocalObjectCount, + "Proxy Binders:", binderProxyObjectCount); + printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", binderDeathObjectCount); + printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", openSslSocketCount); + // SQLite mem info - long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024; - SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats(); - SQLiteDebug.getPagerStats(stats); - pw.println(" "); pw.println(" SQL"); printRow(pw, TWO_COUNT_COLUMNS, "heap:", sqliteAllocated, "dbFiles:", @@ -1751,6 +1850,7 @@ public final class ActivityThread { final HashMap> mResourcePackages = new HashMap>(); Display mDisplay = null; + DisplayMetrics mDisplayMetrics = null; HashMap > mActiveResources = new HashMap >(); @@ -1918,7 +2018,7 @@ public final class ActivityThread { PackageInfo info = new PackageInfo(this, "android", context); context.init(info, null, this); context.getResources().updateConfiguration( - getConfiguration(), getDisplayMetricsLocked()); + getConfiguration(), getDisplayMetricsLocked(false)); mSystemContext = context; //Log.i(TAG, "Created system resources " + context.getResources() // + ": " + context.getResources().getConfiguration()); @@ -2557,7 +2657,10 @@ public final class ActivityThread { a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; - wm.addView(decor, l); + if (a.mVisibleFromClient) { + a.mWindowAdded = true; + wm.addView(decor, l); + } // If the window has already been added, but during resume // we started another activity, then don't yet make the @@ -2576,7 +2679,8 @@ public final class ActivityThread { performConfigurationChanged(r.activity, r.newConfig); r.newConfig = null; } - Log.v(TAG, "Resuming " + r + " with isForward=" + isForward); + if (localLOGV) Log.v(TAG, "Resuming " + r + " with isForward=" + + isForward); WindowManager.LayoutParams l = r.window.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) @@ -2588,8 +2692,11 @@ public final class ActivityThread { View decor = r.window.getDecorView(); wm.updateViewLayout(decor, l); } - r.activity.mDecor.setVisibility(View.VISIBLE); + r.activity.mVisibleFromServer = true; mNumVisibleActivities++; + if (r.activity.mVisibleFromClient) { + r.activity.makeVisible(); + } } r.nextIdle = mNewActivities; @@ -2800,18 +2907,22 @@ public final class ActivityThread { View v = r.activity.mDecor; if (v != null) { if (show) { - if (v.getVisibility() != View.VISIBLE) { - v.setVisibility(View.VISIBLE); + if (!r.activity.mVisibleFromServer) { + r.activity.mVisibleFromServer = true; mNumVisibleActivities++; + if (r.activity.mVisibleFromClient) { + r.activity.makeVisible(); + } } if (r.newConfig != null) { performConfigurationChanged(r.activity, r.newConfig); r.newConfig = null; } } else { - if (v.getVisibility() == View.VISIBLE) { - v.setVisibility(View.INVISIBLE); + if (r.activity.mVisibleFromServer) { + r.activity.mVisibleFromServer = false; mNumVisibleActivities--; + v.setVisibility(View.INVISIBLE); } } } @@ -3037,11 +3148,13 @@ public final class ActivityThread { WindowManager wm = r.activity.getWindowManager(); View v = r.activity.mDecor; if (v != null) { - if (v.getVisibility() == View.VISIBLE) { + if (r.activity.mVisibleFromServer) { mNumVisibleActivities--; } IBinder wtoken = v.getWindowToken(); - wm.removeViewImmediate(v); + if (r.activity.mWindowAdded) { + wm.removeViewImmediate(v); + } if (wtoken != null) { WindowManagerImpl.getDefault().closeAll(wtoken, r.activity.getClass().getName(), "Activity"); @@ -3271,6 +3384,7 @@ public final class ActivityThread { mConfiguration = new Configuration(); } mConfiguration.updateFrom(config); + DisplayMetrics dm = getDisplayMetricsLocked(true); // set it for java, this also affects newly created Resources if (config.locale != null) { @@ -3290,7 +3404,7 @@ public final class ActivityThread { WeakReference v = it.next(); Resources r = v.get(); if (r != null) { - r.updateConfiguration(config, null); + r.updateConfiguration(config, dm); //Log.i(TAG, "Updated app resources " + v.getKey() // + " " + r + ": " + r.getConfiguration()); } else { diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index 4236a00e1b58f..394b8e3b361c5 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -1629,6 +1629,15 @@ class ApplicationContext extends Context { throw new NameNotFoundException(className.toString()); } + @Override + public String[] getSystemSharedLibraryNames() { + try { + return mPM.getSystemSharedLibraryNames(); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + @Override public int checkPermission(String permName, String pkgName) { try { @@ -1974,6 +1983,18 @@ class ApplicationContext extends Context { getApplicationInfo(appPackageName, 0)); } + int mCachedSafeMode = -1; + @Override public boolean isSafeMode() { + try { + if (mCachedSafeMode < 0) { + mCachedSafeMode = mPM.isSafeMode() ? 1 : 0; + } + return mCachedSafeMode != 0; + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + static void configurationChanged() { synchronized (sSync) { sIconCache.clear(); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 951b48d5fad6d..b09a57fb27a52 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -122,7 +122,7 @@ public class Dialog implements DialogInterface, Window.Callback, * uses the window manager and theme from this context to * present its UI. * @param theme A style resource describing the theme to use for the - * window. See Style + * window. See Style * and Theme Resources for more information about defining and using * styles. This theme is applied on top of the current theme in * context. If 0, the default dialog theme will be used. @@ -518,7 +518,7 @@ public class Dialog implements DialogInterface, Window.Callback, private boolean isOutOfBounds(MotionEvent event) { final int x = (int) event.getX(); final int y = (int) event.getY(); - final int slop = ViewConfiguration.getWindowTouchSlop(); + final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop(); final View decorView = getWindow().getDecorView(); return (x < -slop) || (y < -slop) || (x > (decorView.getWidth()+slop)) diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 353500e923459..cd3701f49e67c 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -22,6 +22,7 @@ import android.content.ContentProviderNative; import android.content.IContentProvider; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.ProviderInfo; import android.content.res.Configuration; @@ -127,7 +128,8 @@ public interface IActivityManager extends IInterface { boolean doRebind) throws RemoteException; /* oneway */ public void serviceDoneExecuting(IBinder token) throws RemoteException; - + public IBinder peekService(Intent service, String resolvedType) throws RemoteException; + public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) throws RemoteException; @@ -216,6 +218,13 @@ public interface IActivityManager extends IInterface { // Retrieve running application processes in the system public List getRunningAppProcesses() throws RemoteException; + // Get device configuration + public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException; + + /* + * Private non-Binder interfaces + */ + /* package */ boolean testIsSystemReady(); /** Information you can retrieve about a particular application. */ public static class ContentProviderHolder implements Parcelable { @@ -354,4 +363,6 @@ public interface IActivityManager extends IInterface { int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80; int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81; int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82; + int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83; + int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84; } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index f96d78710463a..f6a28b23a3a89 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1267,7 +1267,7 @@ public class Instrumentation { } /** - * Perform calling of an activity's {@link Activity#onUserLeaving} method. + * Perform calling of an activity's {@link Activity#onUserLeaveHint} method. * The default implementation simply calls through to that method. * * @param activity The activity being notified that the user has navigated away diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 8f0a4f58fe5fc..c363f04bd3969 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -21,11 +21,23 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.PaintDrawable; +import android.os.AsyncTask; import android.os.Bundle; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.Window; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; @@ -43,33 +55,59 @@ import java.util.List; * */ public abstract class LauncherActivity extends ListActivity { + + Intent mIntent; + PackageManager mPackageManager; + /** + * An item in the list + */ + public static class ListItem { + public CharSequence label; + //public CharSequence description; + public Drawable icon; + public String packageName; + public String className; + + ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) { + label = resolveInfo.loadLabel(pm); + if (label == null && resolveInfo.activityInfo != null) { + label = resolveInfo.activityInfo.name; + } + + /* + if (resolveInfo.activityInfo != null && + resolveInfo.activityInfo.applicationInfo != null) { + description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm); + } + */ + + icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm)); + packageName = resolveInfo.activityInfo.applicationInfo.packageName; + className = resolveInfo.activityInfo.name; + } + + public ListItem() { + } + } + /** * Adapter which shows the set of activities that can be performed for a given intent. */ private class ActivityAdapter extends BaseAdapter implements Filterable { private final Object lock = new Object(); - private ArrayList mOriginalValues; + private ArrayList mOriginalValues; - protected final Context mContext; - protected final Intent mIntent; protected final LayoutInflater mInflater; - protected List mActivitiesList; + protected List mActivitiesList; private Filter mFilter; - - public ActivityAdapter(Context context, Intent intent) { - mContext = context; - mIntent = new Intent(intent); - mIntent.setComponent(null); - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - PackageManager pm = context.getPackageManager(); - mActivitiesList = pm.queryIntentActivities(intent, 0); - if (mActivitiesList != null) { - Collections.sort(mActivitiesList, new ResolveInfo.DisplayNameComparator(pm)); - } + + public ActivityAdapter() { + mInflater = (LayoutInflater) LauncherActivity.this.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + mActivitiesList = makeListItems(); } public Intent intentForPosition(int position) { @@ -78,8 +116,8 @@ public abstract class LauncherActivity extends ListActivity { } Intent intent = new Intent(mIntent); - ActivityInfo ai = mActivitiesList.get(position).activityInfo; - intent.setClassName(ai.applicationInfo.packageName, ai.name); + ListItem item = mActivitiesList.get(position); + intent.setClassName(item.packageName, item.className); return intent; } @@ -99,7 +137,7 @@ public abstract class LauncherActivity extends ListActivity { View view; if (convertView == null) { view = mInflater.inflate( - com.android.internal.R.layout.simple_list_item_1, parent, false); + com.android.internal.R.layout.activity_list_item_2, parent, false); } else { view = convertView; } @@ -108,7 +146,7 @@ public abstract class LauncherActivity extends ListActivity { } private char getCandidateLetter(ResolveInfo info) { - PackageManager pm = mContext.getPackageManager(); + PackageManager pm = LauncherActivity.this.getPackageManager(); CharSequence label = info.loadLabel(pm); if (label == null) { @@ -118,24 +156,22 @@ public abstract class LauncherActivity extends ListActivity { return Character.toLowerCase(label.charAt(0)); } - private void bindView(View view, ResolveInfo info) { - TextView text = (TextView) view.findViewById(com.android.internal.R.id.text1); - - PackageManager pm = mContext.getPackageManager(); - CharSequence label = info.loadLabel(pm); - text.setText(label != null ? label : info.activityInfo.name); + private void bindView(View view, ListItem item) { + TextView text = (TextView) view; + text.setText(item.label); + text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null); } - + public Filter getFilter() { if (mFilter == null) { mFilter = new ArrayFilter(); } return mFilter; } - + /** - *

    An array filters constrains the content of the array adapter with a prefix. Each item that - * does not start with the supplied prefix is removed from the list.

    + * An array filters constrains the content of the array adapter with a prefix. Each + * item that does not start with the supplied prefix is removed from the list. */ private class ArrayFilter extends Filter { @Override @@ -144,39 +180,36 @@ public abstract class LauncherActivity extends ListActivity { if (mOriginalValues == null) { synchronized (lock) { - mOriginalValues = new ArrayList(mActivitiesList); + mOriginalValues = new ArrayList(mActivitiesList); } } if (prefix == null || prefix.length() == 0) { synchronized (lock) { - ArrayList list = new ArrayList(mOriginalValues); + ArrayList list = new ArrayList(mOriginalValues); results.values = list; results.count = list.size(); } } else { - final PackageManager pm = mContext.getPackageManager(); + final PackageManager pm = LauncherActivity.this.getPackageManager(); final String prefixString = prefix.toString().toLowerCase(); - ArrayList values = mOriginalValues; + ArrayList values = mOriginalValues; int count = values.size(); - ArrayList newValues = new ArrayList(count); + ArrayList newValues = new ArrayList(count); for (int i = 0; i < count; i++) { - ResolveInfo value = values.get(i); + ListItem item = values.get(i); - final CharSequence label = value.loadLabel(pm); - final CharSequence name = label != null ? label : value.activityInfo.name; - - String[] words = name.toString().toLowerCase().split(" "); + String[] words = item.label.toString().toLowerCase().split(" "); int wordCount = words.length; for (int k = 0; k < wordCount; k++) { final String word = words[k]; if (word.startsWith(prefixString)) { - newValues.add(value); + newValues.add(item); break; } } @@ -192,7 +225,7 @@ public abstract class LauncherActivity extends ListActivity { @Override protected void publishResults(CharSequence constraint, FilterResults results) { //noinspection unchecked - mActivitiesList = (List) results.values; + mActivitiesList = (List) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { @@ -201,19 +234,121 @@ public abstract class LauncherActivity extends ListActivity { } } } - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mAdapter = new ActivityAdapter(this, getTargetIntent()); + /** + * Utility class to resize icons to match default icon size. + */ + public class IconResizer { + // Code is borrowed from com.android.launcher.Utilities. + private int mIconWidth = -1; + private int mIconHeight = -1; + + private final Paint mPaint = new Paint(); + private final Rect mBounds = new Rect(); + private final Rect mOldBounds = new Rect(); + private Canvas mCanvas = new Canvas(); + + public IconResizer() { + mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, + Paint.FILTER_BITMAP_FLAG)); + + final Resources resources = LauncherActivity.this.getResources(); + mIconWidth = mIconHeight = (int) resources.getDimension( + android.R.dimen.app_icon_size); + } + + /** + * Returns a Drawable representing the thumbnail of the specified Drawable. + * The size of the thumbnail is defined by the dimension + * android.R.dimen.launcher_application_icon_size. + * + * This method is not thread-safe and should be invoked on the UI thread only. + * + * @param icon The icon to get a thumbnail of. + * + * @return A thumbnail for the specified icon or the icon itself if the + * thumbnail could not be created. + */ + public Drawable createIconThumbnail(Drawable icon) { + int width = mIconWidth; + int height = mIconHeight; + + final int iconWidth = icon.getIntrinsicWidth(); + final int iconHeight = icon.getIntrinsicHeight(); + + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(width); + painter.setIntrinsicHeight(height); + } + + if (width > 0 && height > 0) { + if (width < iconWidth || height < iconHeight) { + final float ratio = (float) iconWidth / iconHeight; + + if (iconWidth > iconHeight) { + height = (int) (width / ratio); + } else if (iconHeight > iconWidth) { + width = (int) (height * ratio); + } + + final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ? + Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); + final Canvas canvas = mCanvas; + canvas.setBitmap(thumb); + // Copy the old bounds to restore them later + // If we were to do oldBounds = icon.getBounds(), + // the call to setBounds() that follows would + // change the same instance and we would lose the + // old bounds + mOldBounds.set(icon.getBounds()); + final int x = (mIconWidth - width) / 2; + final int y = (mIconHeight - height) / 2; + icon.setBounds(x, y, x + width, y + height); + icon.draw(canvas); + icon.setBounds(mOldBounds); + icon = new BitmapDrawable(thumb); + } else if (iconWidth < width && iconHeight < height) { + final Bitmap.Config c = Bitmap.Config.ARGB_8888; + final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); + final Canvas canvas = mCanvas; + canvas.setBitmap(thumb); + mOldBounds.set(icon.getBounds()); + final int x = (width - iconWidth) / 2; + final int y = (height - iconHeight) / 2; + icon.setBounds(x, y, x + iconWidth, y + iconHeight); + icon.draw(canvas); + icon.setBounds(mOldBounds); + icon = new BitmapDrawable(thumb); + } + } + + return icon; + } + } + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mPackageManager = getPackageManager(); + + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setProgressBarIndeterminateVisibility(true); + setContentView(com.android.internal.R.layout.activity_list); + + + mIntent = new Intent(getTargetIntent()); + mIntent.setComponent(null); + mAdapter = new ActivityAdapter(); setListAdapter(mAdapter); getListView().setTextFilterEnabled(true); + + setProgressBarIndeterminateVisibility(false); } - @Override protected void onListItemClick(ListView l, View v, int position, long id) { Intent intent = ((ActivityAdapter)mAdapter).intentForPosition(position); @@ -221,6 +356,42 @@ public abstract class LauncherActivity extends ListActivity { startActivity(intent); } - protected abstract Intent getTargetIntent(); - + /** + * Return the actual Intent for a specific position in our + * {@link android.widget.ListView}. + * @param position The item whose Intent to return + */ + protected Intent intentForPosition(int position) { + ActivityAdapter adapter = (ActivityAdapter) mAdapter; + return adapter.intentForPosition(position); + } + + /** + * Get the base intent to use when running + * {@link PackageManager#queryIntentActivities(Intent, int)}. + */ + protected Intent getTargetIntent() { + return new Intent(); + } + + /** + * Perform the query to determine which results to show and return a list of them. + */ + public List makeListItems() { + // Load all matching activities and sort correctly + List list = mPackageManager.queryIntentActivities(mIntent, + /* no flags */ 0); + Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager)); + + IconResizer resizer = new IconResizer(); + + ArrayList result = new ArrayList(list.size()); + int listSize = list.size(); + for (int i = 0; i < listSize; i++) { + ResolveInfo resolveInfo = list.get(i); + result.add(new ListItem(mPackageManager, resolveInfo, resizer)); + } + + return result; + } } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index ea67cdbd47438..51fddb1145cbb 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -90,8 +90,9 @@ public class Notification implements Parcelable * The intent to execute when the expanded status entry is clicked. If * this is an activity, it must include the * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires - * that you take care of task management as described in the - * application model document. + * that you take care of task management as described in the Activities and Tasks + * section of the Application + * Fundamentals document. */ public PendingIntent contentIntent; @@ -420,8 +421,8 @@ public class Notification implements Parcelable * @param contentIntent The intent to launch when the user clicks the expanded notification. * If this is an activity, it must include the * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires - * that you take care of task management as described in the - * application model document. + * that you take care of task management as described in + * Application Fundamentals: Activities and Tasks. */ public void setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 2e2a1a17a52e8..64f1ba2214744 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -16,11 +16,14 @@ package android.app; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; @@ -34,6 +37,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.server.search.SearchableInfo; +import android.speech.RecognizerIntent; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; @@ -50,6 +54,7 @@ import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.CursorAdapter; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ListView; import android.widget.SimpleCursorAdapter; @@ -59,6 +64,7 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; import java.lang.ref.WeakReference; +import java.util.List; import java.util.concurrent.atomic.AtomicLong; /** @@ -94,6 +100,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private TextView mBadgeLabel; private AutoCompleteTextView mSearchTextField; private Button mGoButton; + private ImageButton mVoiceButton; // interaction with searchable application private ComponentName mLaunchComponent; @@ -115,6 +122,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private Uri mSuggestionData = null; private String mSuggestionQuery = null; + // For voice searching + private Intent mVoiceWebSearchIntent; + private Intent mVoiceAppSearchIntent; + // support for AutoCompleteTextView suggestions display private SuggestionsAdapter mSuggestionsAdapter; @@ -153,12 +164,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchTextField = (AutoCompleteTextView) findViewById(com.android.internal.R.id.search_src_text); mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); + mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); // attach listeners mSearchTextField.addTextChangedListener(mTextWatcher); mSearchTextField.setOnKeyListener(mTextKeyListener); mGoButton.setOnClickListener(mGoButtonClickListener); mGoButton.setOnKeyListener(mButtonsKeyListener); + mVoiceButton.setOnClickListener(mVoiceButtonClickListener); + mVoiceButton.setOnKeyListener(mButtonsKeyListener); // pre-hide all the extraneous elements mBadgeLabel.setVisibility(View.GONE); @@ -169,13 +183,19 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS setCanceledOnTouchOutside(true); // Set up broadcast filters - mCloseDialogsFilter = new - IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + mCloseDialogsFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); mPackageFilter = new IntentFilter(); mPackageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); mPackageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); mPackageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); mPackageFilter.addDataScheme("package"); + + // Save voice intent for later queries/launching + mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); + mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, + RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); + + mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); } /** @@ -261,16 +281,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Dismiss the search dialog. + * The search dialog is being dismissed, so handle all of the local shutdown operations. * - * This function is designed to be idempotent so it can be safely called at any time + * This function is designed to be idempotent so that dismiss() can be safely called at any time * (even if already closed) and more likely to really dump any memory. No leaks! */ @Override - public void dismiss() { - if (isShowing()) { - super.dismiss(); - } + public void onStop() { + super.onStop(); + setOnCancelListener(null); setOnDismissListener(null); @@ -281,6 +300,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // This is OK - it just means we didn't have any registered } + // close any leftover cursor + if (mSuggestionsAdapter != null) { + mSuggestionsAdapter.changeCursor(null); + } + // dump extra memory we're hanging on to mLaunchComponent = null; mAppSearchData = null; @@ -408,6 +432,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS updateSearchButton(); updateSearchBadge(); updateQueryHint(); + updateVoiceButton(); // In order to properly configure the input method (if one is being used), we // need to let it know if we'll be providing suggestions. Although it would be @@ -499,6 +524,30 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } } + /** + * Update the visibility of the voice button. There are actually two voice search modes, + * either of which will activate the button. + */ + private void updateVoiceButton() { + int visibility = View.GONE; + if (mSearchable.getVoiceSearchEnabled()) { + Intent testIntent = null; + if (mSearchable.getVoiceSearchLaunchWebSearch()) { + testIntent = mVoiceWebSearchIntent; + } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { + testIntent = mVoiceAppSearchIntent; + } + if (testIntent != null) { + List list = getContext().getPackageManager(). + queryIntentActivities(testIntent, PackageManager.MATCH_DEFAULT_ONLY); + if (list.size() > 0) { + visibility = View.VISIBLE; + } + } + } + mVoiceButton.setVisibility(visibility); + } + /** * Listeners of various types */ @@ -641,12 +690,98 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } }; + /** + * React to a click in the voice search button. + */ + View.OnClickListener mVoiceButtonClickListener = new View.OnClickListener() { + public void onClick(View v) { + try { + if (mSearchable.getVoiceSearchLaunchWebSearch()) { + getContext().startActivity(mVoiceWebSearchIntent); + dismiss(); + } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { + Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent); + getContext().startActivity(appSearchIntent); + dismiss(); + } + } catch (ActivityNotFoundException e) { + // Should not happen, since we check the availability of + // voice search before showing the button. But just in case... + Log.w(LOG_TAG, "Could not find voice search activity"); + } + } + }; + + /** + * Create and return an Intent that can launch the voice search activity, perform a specific + * voice transcription, and forward the results to the searchable activity. + * + * @param baseIntent The voice app search intent to start from + * @return A completely-configured intent ready to send to the voice search activity + */ + private Intent createVoiceAppSearchIntent(Intent baseIntent) { + // create the necessary intent to set up a search-and-forward operation + // in the voice search system. We have to keep the bundle separate, + // because it becomes immutable once it enters the PendingIntent + Intent queryIntent = new Intent(Intent.ACTION_SEARCH); + queryIntent.setComponent(mSearchable.mSearchActivity); + PendingIntent pending = PendingIntent.getActivity( + getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT); + + // Now set up the bundle that will be inserted into the pending intent + // when it's time to do the search. We always build it here (even if empty) + // because the voice search activity will always need to insert "QUERY" into + // it anyway. + Bundle queryExtras = new Bundle(); + if (mAppSearchData != null) { + queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData); + } + + // Now build the intent to launch the voice search. Add all necessary + // extras to launch the voice recognizer, and then all the necessary extras + // to forward the results to the searchable activity + Intent voiceIntent = new Intent(baseIntent); + + // Add all of the configuration options supplied by the searchable's metadata + String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM; + String prompt = null; + String language = null; + int maxResults = 1; + Resources resources = mActivityContext.getResources(); + if (mSearchable.getVoiceLanguageModeId() != 0) { + languageModel = resources.getString(mSearchable.getVoiceLanguageModeId()); + } + if (mSearchable.getVoicePromptTextId() != 0) { + prompt = resources.getString(mSearchable.getVoicePromptTextId()); + } + if (mSearchable.getVoiceLanguageId() != 0) { + language = resources.getString(mSearchable.getVoiceLanguageId()); + } + if (mSearchable.getVoiceMaxResults() != 0) { + maxResults = mSearchable.getVoiceMaxResults(); + } + voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel); + voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); + voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); + voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); + + // Add the values that configure forwarding the results + voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending); + voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras); + + return voiceIntent; + } + /** * React to the user typing "enter" or other hardwired keys while typing in the search box. * This handles these special keys while the edit box has focus. */ View.OnKeyListener mTextKeyListener = new View.OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + cancel(); + return true; + } // also guard against possible race conditions (late arrival after dismiss) if (mSearchable != null && TextUtils.getTrimmedLength(mSearchTextField.getText()) > 0) { @@ -792,24 +927,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } }; - /** - * UI-thread handling of dialog dismiss. Called by mBroadcastReceiver.onReceive(). - * - * TODO: This is a really heavyweight solution for something that should be so simple. - * For example, we already have a handler, in our superclass, why aren't we sharing that? - * I think we need to investigate simplifying this entire methodology, or perhaps boosting - * it up into the Dialog class. - */ - private static final int MESSAGE_DISMISS = 0; - private Handler mDismissHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - if (msg.what == MESSAGE_DISMISS) { - dismiss(); - } - } - }; - /** * Various ways to launch searches */ diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 0a37e81320229..2cc6de9515324 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -168,7 +168,8 @@ import android.view.KeyEvent; *

    Managing focus and knowing if Search is active. The search UI is not a separate * activity, and when the UI is invoked or dismissed, your activity will not typically be paused, * resumed, or otherwise notified by the methods defined in - * Activity Lifecycle. The search UI is + * Application Fundamentals: + * Activity Lifecycle. The search UI is * handled in the same way as other system UI elements which may appear from time to time, such as * notifications, screen locks, or other system alerts: *

    When the search UI appears, your activity will lose input focus. @@ -212,11 +213,11 @@ import android.view.KeyEvent; * {@link #QUERY getStringExtra(SearchManager.QUERY)}. *

  1. To identify and support your searchable activity, you'll need to * provide an XML file providing searchability configuration parameters, a reference to that - * in your searchable activity's manifest + * in your searchable activity's manifest * entry, and an intent-filter declaring that you can * receive ACTION_SEARCH intents. This is described in more detail in the * Searchability Metadata section.
  2. - *
  3. Your manifest also needs a metadata entry + *
  4. Your manifest also needs a metadata entry * providing a global reference to the searchable activity. This is the "glue" directing the search * UI, when invoked from any of your other activities, to use your application as the * default search context. This is also described in more detail in the @@ -359,7 +360,7 @@ import android.view.KeyEvent; *
  5. Implement a Content Provider that provides suggestions. If you already have one, and it * has access to your suggestions data. If not, you'll have to create one. * You'll also provide information about your Content Provider in your - * package's manifest.
  6. + * package's manifest. *
  7. Update your searchable activity's XML configuration file. There are two categories of * information used for suggestions: *
    • The first is (required) data that the search manager will @@ -634,7 +635,7 @@ import android.view.KeyEvent; * *

      Metadata for searchable activity. As with your search implementations described * above, you must first identify which of your activities is searchable. In the - * manifest entry for this activity, you must + * manifest entry for this activity, you must * provide two elements: *

      • An intent-filter specifying that you can receive and process the * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} {@link android.content.Intent Intent}. @@ -643,7 +644,7 @@ import android.view.KeyEvent; * remaining configuration information for how your application implements search.
      * *

      Here is a snippet showing the necessary elements in the - * manifest entry for your searchable activity. + * manifest entry for your searchable activity. *

        *        <!-- Search Activity - searchable -->
        *        <activity android:name="MySearchActivity" 
      @@ -765,9 +766,8 @@ import android.view.KeyEvent;
        * 
    • .../res/values/strings.xml
    * *

    For more complete documentation on this capability, see - * Resources and - * Internationalization: Supporting Alternate Resources for Alternate Languages and Configurations - * . + * Resources and + * Internationalization: Alternate Resources. * *

    Metadata for non-searchable activities. Activities which are part of a searchable * application, but don't implement search itself, require a bit of "glue" in order to cause @@ -775,7 +775,7 @@ import android.view.KeyEvent; * provided, then searches from these activities will use the system default search context. * *

    The simplest way to specify this is to add a search reference element to the - * application entry in the manifest file. + * application entry in the manifest file. * The value of this reference can be either of: *

    • The name of your searchable activity. * It is typically prefixed by '.' to indicate that it's in the same package.
    • @@ -803,7 +803,7 @@ import android.view.KeyEvent; * to generate search suggestions, you'll need to publish it to the system, and you'll need to * provide a bit of additional XML metadata in order to configure communications with it. * - *

      First, in your manifest, you'll add the + *

      First, in your manifest, you'll add the * following lines. *

        *        <!-- Content provider for search suggestions -->
      @@ -832,7 +832,7 @@ import android.view.KeyEvent;
        *     
        *     android:searchSuggestAuthority
        *         This value must match the authority string provided in the provider section 
      - *             of your manifest.
      + *             of your manifest.
        *         Yes
        *     
        *     
      diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
      index 6c08e750c7d7a..72692f4ebbf94 100644
      --- a/core/java/android/app/Service.java
      +++ b/core/java/android/app/Service.java
      @@ -42,12 +42,12 @@ import java.io.PrintWriter;
        * thread of their hosting process.  This means that, if your service is going
        * to do any CPU intensive (such as MP3 playback) or blocking (such as
        * networking) operations, it should spawn its own thread in which to do that
      - * work.  More information on this can be found in the
      - * Threading section of the
      - * Application Model overview.

      + * work. More information on this can be found in + * Application Fundamentals: + * Processes and Threads.

      * *

      The Service class is an important part of an - * application's overall lifecycle.

      + * application's overall lifecycle.

      * *

      Topics covered here: *

        @@ -79,7 +79,7 @@ import java.io.PrintWriter; * to the service. The service will remain running as long as the connection * is established (whether or not the client retains a reference on the * service's IBinder). Usually the IBinder returned is for a complex - * interface that has been written + * interface that has been written * in aidl. * *

        A service can be both started and have connections bound to it. In such @@ -106,7 +106,7 @@ import java.io.PrintWriter; * {@link #checkCallingPermission} * method before executing the implementation of that call. * - *

        See the Security Model + *

        See the Security and Permissions * document for more information on permissions and security in general. * * @@ -201,14 +201,14 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * Return the communication channel to the service. May return null if * clients can not bind to the service. The returned * {@link android.os.IBinder} is usually for a complex interface - * that has been described using + * that has been described using * aidl. * *

        Note that unlike other application components, calls on to the * IBinder interface returned here may not happen on the main thread * of the process. More information about this can be found - * in the Threading section - * of the Application Model overview.

        + * in Application Fundamentals: + * Processes and Threads.

        * * @param intent The Intent that was used to bind to this service, * as given to {@link android.content.Context#bindService diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index d613e1cfba27e..56b231fa6b13f 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -31,10 +31,15 @@ import java.io.UnsupportedEncodingException; * @hide */ public class BluetoothDevice { - public static final int MODE_UNKNOWN = -1; - public static final int MODE_OFF = 0; - public static final int MODE_CONNECTABLE = 1; - public static final int MODE_DISCOVERABLE = 2; + /** Inquiry scan and page scan are both off. + * Device is neither discoverable nor connectable */ + public static final int SCAN_MODE_NONE = 0; + /** Page scan is on, inquiry scan is off. + * Device is connectable, but not discoverable */ + public static final int SCAN_MODE_CONNECTABLE = 1; + /** Page scan and inquiry scan are on. + * Device is connectable and discoverable */ + public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; public static final int RESULT_FAILURE = -1; public static final int RESULT_SUCCESS = 0; @@ -54,10 +59,10 @@ public class BluetoothDevice { /** A bond attempt failed because the other side explicilty rejected * bonding */ public static final int UNBOND_REASON_AUTH_REJECTED = 2; - /** A bond attempt failed because we cancelled the bonding process */ - public static final int UNBOND_REASON_CANCELLED = 3; + /** A bond attempt failed because we canceled the bonding process */ + public static final int UNBOND_REASON_AUTH_CANCELED = 3; /** A bond attempt failed because we could not contact the remote device */ - public static final int UNBOND_REASON_AUTH_REMOTE_DEVICE_DOWN = 4; + public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** An existing bond was explicitly revoked */ public static final int UNBOND_REASON_REMOVED = 5; @@ -174,18 +179,6 @@ public class BluetoothDevice { return false; } - public String getMajorClass() { - try { - return mService.getMajorClass(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getMinorClass() { - try { - return mService.getMinorClass(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } public String getVersion() { try { return mService.getVersion(); @@ -211,15 +204,26 @@ public class BluetoothDevice { return null; } - public int getMode() { + /** + * Get the current scan mode. + * Used to determine if the local device is connectable and/or discoverable + * @return Scan mode, one of SCAN_MODE_* or an error code + */ + public int getScanMode() { try { - return mService.getMode(); + return mService.getScanMode(); } catch (RemoteException e) {Log.e(TAG, "", e);} - return MODE_UNKNOWN; + return BluetoothError.ERROR_IPC; } - public void setMode(int mode) { + + /** + * Set the current scan mode. + * Used to make the local device connectable and/or discoverable + * @param scanMode One of SCAN_MODE_* + */ + public void setScanMode(int scanMode) { try { - mService.setMode(mode); + mService.setScanMode(scanMode); } catch (RemoteException e) {Log.e(TAG, "", e);} } @@ -435,24 +439,6 @@ public class BluetoothDevice { return null; } - public String getRemoteAlias(String address) { - try { - return mService.getRemoteAlias(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public boolean setRemoteAlias(String address, String alias) { - try { - return mService.setRemoteAlias(address, alias); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean clearRemoteAlias(String address) { - try { - return mService.clearRemoteAlias(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } public String getRemoteVersion(String address) { try { return mService.getRemoteVersion(address); @@ -477,24 +463,6 @@ public class BluetoothDevice { } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - public String getRemoteMajorClass(String address) { - try { - return mService.getRemoteMajorClass(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteMinorClass(String address) { - try { - return mService.getRemoteMinorClass(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String[] getRemoteServiceClasses(String address) { - try { - return mService.getRemoteServiceClasses(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } /** * Returns the RFCOMM channel associated with the 16-byte UUID on @@ -512,12 +480,19 @@ public class BluetoothDevice { return false; } + /** + * Get the major, minor and servics classes of a remote device. + * These classes are encoded as a 32-bit integer. See BluetoothClass. + * @param address remote device + * @return 32-bit class suitable for use with BluetoothClass. + */ public int getRemoteClass(String address) { try { return mService.getRemoteClass(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return BluetoothClass.ERROR; } + public byte[] getRemoteFeatures(String address) { try { return mService.getRemoteFeatures(address); @@ -576,8 +551,8 @@ public class BluetoothDevice { } - /* Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ private static final int ADDRESS_LENGTH = 17; + /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ public static boolean checkBluetoothAddress(String address) { if (address == null || address.length() != ADDRESS_LENGTH) { return false; diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index c3152719bbb61..34196bf8dea27 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -69,8 +69,8 @@ public class BluetoothHeadset { public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; - /** Connection cancelled before completetion. */ - public static final int RESULT_CANCELLED = 2; + /** Connection canceled before completetion. */ + public static final int RESULT_CANCELED = 2; /** Default priority for headsets that should be auto-connected */ public static final int PRIORITY_AUTO = 100; @@ -318,6 +318,12 @@ public class BluetoothHeadset { * @return True if this device might support HSP or HFP. */ public static boolean doesClassMatch(int btClass) { + // The render service class is required by the spec for HFP, so is a + // pretty good signal + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + return true; + } + // Just in case they forgot the render service class switch (BluetoothClass.Device.getDevice(btClass)) { case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: diff --git a/core/java/android/bluetooth/BluetoothIntent.java b/core/java/android/bluetooth/BluetoothIntent.java index 57c46f98c2999..b66b06ef59ce8 100644 --- a/core/java/android/bluetooth/BluetoothIntent.java +++ b/core/java/android/bluetooth/BluetoothIntent.java @@ -29,8 +29,8 @@ import android.annotation.SdkConstant.SdkConstantType; * @hide */ public interface BluetoothIntent { - public static final String MODE = - "android.bluetooth.intent.MODE"; + public static final String SCAN_MODE = + "android.bluetooth.intent.SCAN_MODE"; public static final String ADDRESS = "android.bluetooth.intent.ADDRESS"; public static final String NAME = @@ -62,9 +62,14 @@ public interface BluetoothIntent { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String NAME_CHANGED_ACTION = "android.bluetooth.intent.action.NAME_CHANGED"; + + /** + * Broadcast when the scan mode changes. Always contains an int extra + * named SCAN_MODE that contains the new scan mode. + */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String MODE_CHANGED_ACTION = - "android.bluetooth.intent.action.MODE_CHANGED"; + public static final String SCAN_MODE_CHANGED_ACTION = + "android.bluetooth.intent.action.SCAN_MODE_CHANGED"; @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String DISCOVERY_STARTED_ACTION = @@ -104,12 +109,6 @@ public interface BluetoothIntent { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String REMOTE_NAME_FAILED_ACTION = "android.bluetooth.intent.action.REMOTE_NAME_FAILED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_ALIAS_CHANGED_ACTION = - "android.bluetooth.intent.action.REMOTE_ALIAS_CHANGED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_ALIAS_CLEARED_ACTION = - "android.bluetooth.intent.action.REMOTE_ALIAS_CLEARED"; /** * Broadcast when the bond state of a remote device changes. diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java index bce3388b6e9c6..fd2d2ab88481b 100644 --- a/core/java/android/bluetooth/HeadsetBase.java +++ b/core/java/android/bluetooth/HeadsetBase.java @@ -21,13 +21,10 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; -import java.io.IOException; -import java.lang.Thread; - /** * The Android Bluetooth API is not finalized, and *will* change. Use at your * own risk. - * + * * The base RFCOMM (service) connection for a headset or handsfree device. * * In the future this class will be removed. @@ -90,7 +87,7 @@ public class HeadsetBase { /* Create from an already exisiting rfcomm connection */ public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd, - int rfcommChannel, Handler handler) { + int rfcommChannel, Handler handler) { mDirection = DIRECTION_INCOMING; mConnectTimestamp = System.currentTimeMillis(); mBluetooth = bluetooth; @@ -132,30 +129,8 @@ public class HeadsetBase { */ protected void initializeAtParser() { mAtParser = new AtParser(); - - // Microphone Gain - mAtParser.register("+VGM", new AtCommandHandler() { - @Override - public AtCommandResult handleSetCommand(Object[] args) { - // AT+VGM= in range [0,15] - // Headset/Handsfree is reporting its current gain setting - //TODO: sync to android UI - //TODO: Send unsolicited +VGM when volume changed on AG - return new AtCommandResult(AtCommandResult.OK); - } - }); - - // Speaker Gain - mAtParser.register("+VGS", new AtCommandHandler() { - @Override - public AtCommandResult handleSetCommand(Object[] args) { - // AT+VGS= in range [0,15] - // Headset/Handsfree is reporting its current gain to Android - //TODO: sync to AG UI - //TODO: Send unsolicited +VGS when volume changed on AG - return new AtCommandResult(AtCommandResult.OK); - } - }); + //TODO(): Get rid of this as there are no parsers registered. But because of dependencies, + //it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree } public AtParser getAtParser() { diff --git a/core/java/android/bluetooth/IBluetoothDevice.aidl b/core/java/android/bluetooth/IBluetoothDevice.aidl index 59f679f3cd6a4..4351d2ebc3863 100644 --- a/core/java/android/bluetooth/IBluetoothDevice.aidl +++ b/core/java/android/bluetooth/IBluetoothDevice.aidl @@ -32,15 +32,13 @@ interface IBluetoothDevice String getAddress(); String getName(); boolean setName(in String name); - String getMajorClass(); - String getMinorClass(); String getVersion(); String getRevision(); String getManufacturer(); String getCompany(); - int getMode(); - boolean setMode(int mode); + int getScanMode(); + boolean setScanMode(int mode); int getDiscoverableTimeout(); boolean setDiscoverableTimeout(int timeout); @@ -64,17 +62,11 @@ interface IBluetoothDevice int getBondState(in String address); String getRemoteName(in String address); - String getRemoteAlias(in String address); - boolean setRemoteAlias(in String address, in String alias); - boolean clearRemoteAlias(in String address); String getRemoteVersion(in String address); String getRemoteRevision(in String address); int getRemoteClass(in String address); String getRemoteManufacturer(in String address); String getRemoteCompany(in String address); - String getRemoteMajorClass(in String address); - String getRemoteMinorClass(in String address); - String[] getRemoteServiceClasses(in String address); boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); byte[] getRemoteFeatures(in String adddress); String lastSeen(in String address); diff --git a/core/java/android/content/AsyncQueryHandler.java b/core/java/android/content/AsyncQueryHandler.java index 2d651a756111c..ac851ccbcc211 100644 --- a/core/java/android/content/AsyncQueryHandler.java +++ b/core/java/android/content/AsyncQueryHandler.java @@ -146,6 +146,20 @@ public abstract class AsyncQueryHandler extends Handler { * @param token A token passed into {@link #onQueryComplete} to identify * the query. * @param cookie An object that gets passed into {@link #onQueryComplete} + * @param uri The URI, using the content:// scheme, for the content to + * retrieve. + * @param projection A list of which columns to return. Passing null will + * return all columns, which is discouraged to prevent reading data + * from storage that isn't going to be used. + * @param selection A filter declaring which rows to return, formatted as an + * SQL WHERE clause (excluding the WHERE itself). Passing null will + * return all rows for the given URI. + * @param selectionArgs You may include ?s in selection, which will be + * replaced by the values from selectionArgs, in the order that they + * appear in the selection. The values will be bound as Strings. + * @param orderBy How to order the rows, formatted as an SQL ORDER BY + * clause (excluding the ORDER BY itself). Passing null will use the + * default sort order, which may be unordered. */ public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index cd92002b72ada..ee08eea56fa48 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -16,7 +16,11 @@ package android.content; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; import android.util.Log; /** @@ -175,13 +179,35 @@ public abstract class BroadcastReceiver { * return a result to you asynchronously -- in particular, for interacting * with services, you should use * {@link Context#startService(Intent)} instead of - * {@link Context#bindService(Intent, ServiceConnection, int)}. + * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish + * to interact with a service that is already running, you can use + * {@link #peekService}. * * @param context The Context in which the receiver is running. * @param intent The Intent being received. */ public abstract void onReceive(Context context, Intent intent); + /** + * Provide a binder to an already-running service. This method is synchronous + * and will not start the target service if it is not present, so it is safe + * to call from {@link #onReceive}. + * + * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)} + * @param service The Intent indicating the service you wish to use. See {@link + * Context#startService(Intent)} for more information. + */ + public IBinder peekService(Context myContext, Intent service) { + IActivityManager am = ActivityManagerNative.getDefault(); + IBinder binder = null; + try { + binder = am.peekService(service, service.resolveTypeIfNeeded( + myContext.getContentResolver())); + } catch (RemoteException e) { + } + return binder; + } + /** * Change the current result code of this broadcast; only works with * broadcasts sent through diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3908aa1bf80fc..e0fe533a93813 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -127,7 +127,7 @@ public abstract class Context { * current process. */ public abstract Context getApplicationContext(); - + /** * Return a localized, styled CharSequence from the application's package's * default string table. @@ -428,7 +428,7 @@ public abstract class Context { * cursor when query is called. * * @return The contents of a newly created database with the given name. - * @throws SQLiteException if the database file could not be opened. + * @throws android.database.sqlite.SQLiteException if the database file could not be opened. * * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE @@ -1064,7 +1064,7 @@ public abstract class Context { * @see #AUDIO_SERVICE * @see android.media.AudioManager * @see #TELEPHONY_SERVICE - * @see android.internal.TelephonyManager + * @see android.telephony.TelephonyManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e2d357694bba4..52aae0d3b4379 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -504,6 +504,8 @@ import java.util.Set; *
      1. {@link #ACTION_PACKAGE_ADDED} *
      2. {@link #ACTION_PACKAGE_CHANGED} *
      3. {@link #ACTION_PACKAGE_REMOVED} + *
      4. {@link #ACTION_PACKAGE_RESTARTED} + *
      5. {@link #ACTION_PACKAGE_DATA_CLEARED} *
      6. {@link #ACTION_UID_REMOVED} *
      7. {@link #ACTION_BATTERY_CHANGED} *
    @@ -1107,6 +1109,10 @@ public class Intent implements Parcelable { /** * Broadcast Action: A new application package has been installed on the * device. The data contains the name of the package. + *

    My include the following extras: + *

      + *
    • {@link #EXTRA_UID} containing the integer uid assigned to the new package. + *
    */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; @@ -1114,22 +1120,48 @@ public class Intent implements Parcelable { * Broadcast Action: An existing application package has been removed from * the device. The data contains the name of the package. The package * that is being installed does not receive this Intent. + *
      + *
    • {@link #EXTRA_UID} containing the integer uid previously assigned + * to the package. + *
    • {@link #EXTRA_DATA_REMOVED} is set to true if the entire + * application -- data and code -- is being removed. + *
    • {@link #EXTRA_REPLACING} is set to true if this will be followed + * by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package. + *
    */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED"; /** * Broadcast Action: An existing application package has been changed (e.g. a component has been * enabled or disabled. The data contains the name of the package. + *
      + *
    • {@link #EXTRA_UID} containing the integer uid assigned to the package. + *
    */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; /** - * Broadcast Action: The user has restarted a package, all runtime state + * Broadcast Action: The user has restarted a package, and all of its + * processes have been killed. All runtime state * associated with it (processes, alarms, notifications, etc) should - * be remove. The data contains the name of the package. + * be removed. The data contains the name of the package. + *
      + *
    • {@link #EXTRA_UID} containing the integer uid assigned to the package. + *
    */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED"; + /** + * Broadcast Action: The user has cleared the data of a package. This should + * be preceded by {@link #ACTION_PACKAGE_RESTARTED}, after which all of + * its persistent data is erased and this broadcast sent. The data contains + * the name of the package. + *
      + *
    • {@link #EXTRA_UID} containing the integer uid assigned to the package. + *
    + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; /** * Broadcast Action: A user ID has been removed from the system. The user * ID number is stored in the extra data under {@link #EXTRA_UID}. @@ -1227,7 +1259,6 @@ public class Intent implements Parcelable { /** * Broadcast Action: External media is present, and being disk-checked * The path to the mount point for the checking media is contained in the Intent.mData field. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING"; @@ -1235,7 +1266,6 @@ public class Intent implements Parcelable { /** * Broadcast Action: External media is present, but is using an incompatible fs (or is blank) * The path to the mount point for the checking media is contained in the Intent.mData field. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MEDIA_NOFS = "android.intent.action.MEDIA_NOFS"; @@ -1655,6 +1685,22 @@ public class Intent implements Parcelable { */ public static final String EXTRA_UID = "android.intent.extra.UID"; + /** + * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} + * intents to indicate whether this represents a full uninstall (removing + * both the code and its data) or a partial uninstall (leaving its data, + * implying that this is an update). + */ + public static final String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED"; + + /** + * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} + * intents to indicate that this is a replacement of the package, so this + * broadcast will immediately be followed by an add broadcast for a + * different version of the same package. + */ + public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING"; + /** * Used as an int extra field in {@link android.app.AlarmManager} intents * to tell the application being invoked how many pending alarms are being @@ -1770,8 +1816,8 @@ public class Intent implements Parcelable { * Intent, resulting in the stack now being: A, B. * *

    The currently running instance of task B in the above example will - * either receiving the new intent you are starting here in its - * onNewIntent() method, or be itself finished and restarting with the + * either receive the new intent you are starting here in its + * onNewIntent() method, or be itself finished and restarted with the * new intent. If it has declared its launch mode to be "multiple" (the * default) it will be finished and re-created; for all other launch modes * it will receive the Intent in the current instance. @@ -1855,7 +1901,7 @@ public class Intent implements Parcelable { */ public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000; /** - * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaving} + * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaveHint} * callback from occurring on the current frontmost activity before it is * paused as the newly-started activity is brought to the front. * @@ -1871,12 +1917,39 @@ public class Intent implements Parcelable { * activity does not think the user has acknowledged its notification. */ public static final int FLAG_ACTIVITY_NO_USER_ACTION = 0x00040000; - + /** + * If set in an Intent passed to {@link Context#startActivity Context.startActivity()}, + * this flag will cause the launched activity to be brought to the front of its + * task's history stack if it is already running. + * + *

    For example, consider a task consisting of four activities: A, B, C, D. + * If D calls startActivity() with an Intent that resolves to the component + * of activity B, then B will be brought to the front of the history stack, + * with this resulting order: A, C, D, B. + * + * This flag will be ignored if {@link #FLAG_ACTIVITY_CLEAR_TOP} is also + * specified. + */ + public static final int FLAG_ACTIVITY_REORDER_TO_FRONT = 0X00020000; /** * If set, when sending a broadcast only registered receivers will be * called -- no BroadcastReceiver components will be launched. */ public static final int FLAG_RECEIVER_REGISTERED_ONLY = 0x40000000; + /** + * If set, when sending a broadcast before boot has completed only + * registered receivers will be called -- no BroadcastReceiver components + * will be launched. Sticky intent state will be recorded properly even + * if no receivers wind up being called. If {@link #FLAG_RECEIVER_REGISTERED_ONLY} + * is specified in the broadcast intent, this flag is unnecessary. + * + *

    This flag is only for use by system sevices as a convenience to + * avoid having to implement a more complex mechanism around detection + * of boot completion. + * + * @hide + */ + public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000; // --------------------------------------------------------------------- diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 6bc3774859b0d..96470c3c5756c 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -123,7 +123,7 @@ class SyncManager { private static final String SYNC_WAKE_LOCK = "SyncManagerSyncWakeLock"; private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock"; - + private Context mContext; private ContentResolver mContentResolver; @@ -249,7 +249,7 @@ class SyncManager { mSyncQueue = new SyncQueue(mSyncStorageEngine); mContext = context; - + mSyncThread = new HandlerThread("SyncHandlerThread", Process.THREAD_PRIORITY_BACKGROUND); mSyncThread.start(); mSyncHandler = new SyncHandler(mSyncThread.getLooper()); @@ -489,7 +489,7 @@ class SyncManager { // Require the precise value "yes" to discourage accidental activation. return "yes".equals(SystemProperties.get("ro.config.sync")); } - + /** * Initiate a sync. This can start a sync for all providers * (pass null to url, set onlyTicklable to false), only those @@ -515,7 +515,7 @@ class SyncManager { * syncs of a specific provider. Can be null. Is ignored * if the url is null. * @param delay how many milliseconds in the future to wait before performing this - * sync. -1 means to make this the next sync to perform. + * sync. -1 means to make this the next sync to perform. */ public void scheduleSync(Uri url, Bundle extras, long delay) { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); @@ -694,7 +694,7 @@ class SyncManager { class SyncHandlerMessagePayload { public final ActiveSyncContext activeSyncContext; public final SyncResult syncResult; - + SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) { this.activeSyncContext = syncContext; this.syncResult = syncResult; @@ -740,7 +740,7 @@ class SyncManager { if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) { newDelayInMs = maxSyncRetryTimeInSeconds * 1000; } - + SyncOperation rescheduledSyncOperation = new SyncOperation(syncOperation); rescheduledSyncOperation.setDelay(newDelayInMs); scheduleSyncOperation(rescheduledSyncOperation); @@ -786,7 +786,7 @@ class SyncManager { // key than the one we are scheduling. if (!activeIsExpedited && !hasSameKey) { rescheduleImmediately(activeSyncContext.mSyncOperation); - sendSyncFinishedOrCanceledMessage(activeSyncContext, + sendSyncFinishedOrCanceledMessage(activeSyncContext, null /* no result since this is a cancel */); } } @@ -1323,7 +1323,7 @@ class SyncManager { public SyncHandler(Looper looper) { super(looper); } - + public void handleMessage(Message msg) { handleSyncHandlerMessage(msg); } @@ -1462,6 +1462,9 @@ class SyncManager { // start it, otherwise just get out. SyncOperation syncOperation; final Sync.Settings.QueryMap syncSettings = getSyncSettings(); + final ConnectivityManager connManager = (ConnectivityManager) + mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + final boolean backgroundDataSetting = connManager.getBackgroundDataSetting(); synchronized (mSyncQueue) { while (true) { syncOperation = mSyncQueue.head(); @@ -1484,10 +1487,10 @@ class SyncManager { // skip the sync if it isn't a force and the settings are off for this provider final boolean force = syncOperation.extras.getBoolean( ContentResolver.SYNC_EXTRAS_FORCE, false); - if (!force && (!syncSettings.getBackgroundData() + if (!force && (!backgroundDataSetting || !syncSettings.getListenForNetworkTickles() || !syncSettings.getSyncProviderAutomatically( - syncOperation.authority))) { + syncOperation.authority))) { if (isLoggable) { Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation); } @@ -1669,7 +1672,7 @@ class SyncManager { * @param syncResult the SyncResult from which to read * @return the most "serious" error set in the SyncResult * @throws IllegalStateException if the SyncResult does not indicate any errors. - * If SyncResult.error() is true then it is safe to call this. + * If SyncResult.error() is true then it is safe to call this. */ private int syncResultToErrorNumber(SyncResult syncResult) { if (syncResult.syncAlreadyInProgress) return History.ERROR_SYNC_ALREADY_IN_PROGRESS; @@ -1679,7 +1682,8 @@ class SyncManager { if (syncResult.stats.numConflictDetectedExceptions > 0) return History.ERROR_CONFLICT; if (syncResult.tooManyDeletions) return History.ERROR_TOO_MANY_DELETIONS; if (syncResult.tooManyRetries) return History.ERROR_TOO_MANY_RETRIES; - throw new IllegalStateException("we are not in an error state, " + toString()); + if (syncResult.databaseError) return History.ERROR_INTERNAL; + throw new IllegalStateException("we are not in an error state, " + syncResult); } private void manageSyncNotification() { @@ -1717,7 +1721,7 @@ class SyncManager { if (mSyncNotificationInfo.isActive) { shouldInstall = shouldCancel; } else { - final boolean timeToShowNotification = + final boolean timeToShowNotification = now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY; final boolean syncIsForced = syncOperation.extras .getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false); @@ -1769,7 +1773,7 @@ class SyncManager { if (!mDataConnectionIsConnected) return; if (mAccounts == null) return; if (mStorageIsLow) return; - + // Compute the alarm fire time: // - not syncing: time of the next sync operation // - syncing, no notification: time from sync start to notification create time @@ -1850,12 +1854,12 @@ class SyncManager { clickIntent.putExtra("account", account); clickIntent.putExtra("provider", authority); clickIntent.putExtra("numDeletes", numDeletes); - + if (!isActivityAvailable(clickIntent)) { Log.w(TAG, "No activity found to handle too many deletes."); return; } - + final PendingIntent pendingIntent = PendingIntent .getActivity(mContext, 0, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT); @@ -1877,7 +1881,7 @@ class SyncManager { /** * Checks whether an activity exists on the system image for the given intent. - * + * * @param intent The intent for an activity. * @return Whether or not an activity exists. */ @@ -1892,10 +1896,10 @@ class SyncManager { return true; } } - + return false; } - + public long insertStartSyncEvent(SyncOperation syncOperation) { final int source = syncOperation.syncSource; final long now = System.currentTimeMillis(); diff --git a/core/java/android/content/TempProviderSyncAdapter.java b/core/java/android/content/TempProviderSyncAdapter.java index 78510aad987a0..eb3a5da4a5b12 100644 --- a/core/java/android/content/TempProviderSyncAdapter.java +++ b/core/java/android/content/TempProviderSyncAdapter.java @@ -1,11 +1,11 @@ package android.content; -import com.google.android.net.NetStats; - import android.database.SQLException; import android.os.Bundle; import android.os.Debug; +import android.os.NetStat; import android.os.Parcelable; +import android.os.Process; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Config; @@ -177,7 +177,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter { private final Bundle mExtras; private final SyncContext mSyncContext; private volatile boolean mIsCanceled = false; - private long[] mNetStats; + private long mInitialTxBytes; + private long mInitialRxBytes; private final SyncResult mResult; SyncThread(SyncContext syncContext, String account, Bundle extras) { @@ -193,15 +194,18 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter { if (mAdapterSyncStarted) onSyncCanceled(); if (mProviderSyncStarted) mProvider.onSyncCanceled(); // We may lose the last few sync events when canceling. Oh well. - long[] newNetStats = NetStats.getStats(); - logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult); + int uid = Process.myUid(); + logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes, + NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult); } @Override public void run() { - android.os.Process.setThreadPriority(android.os.Process.myTid(), - android.os.Process.THREAD_PRIORITY_BACKGROUND); - mNetStats = NetStats.getStats(); + Process.setThreadPriority(Process.myTid(), + Process.THREAD_PRIORITY_BACKGROUND); + int uid = Process.myUid(); + mInitialTxBytes = NetStat.getUidTxBytes(uid); + mInitialRxBytes = NetStat.getUidRxBytes(uid); try { sync(mSyncContext, mAccount, mExtras); } catch (SQLException e) { @@ -210,8 +214,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter { } finally { mSyncThread = null; if (!mIsCanceled) { - long[] newNetStats = NetStats.getStats(); - logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult); + logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes, + NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult); mSyncContext.onFinished(mResult); } } diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java index 9115225c4415e..dcc746331b0b8 100755 --- a/core/java/android/content/pm/ConfigurationInfo.java +++ b/core/java/android/content/pm/ConfigurationInfo.java @@ -16,7 +16,6 @@ package android.content.pm; -import android.content.res.Configuration; import android.os.Parcel; import android.os.Parcelable; @@ -60,12 +59,12 @@ public class ConfigurationInfo implements Parcelable { /** * Value for {@link #reqInputFeatures}: if set, indicates that the application - * requires a hard keyboard + * requires a five way navigation device */ public static final int INPUT_FEATURE_FIVE_WAY_NAV = 0x00000002; /** - * Flags associated with the application. Any combination of + * Flags associated with the input features. Any combination of * {@link #INPUT_FEATURE_HARD_KEYBOARD}, * {@link #INPUT_FEATURE_FIVE_WAY_NAV} */ diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index ea86188d2bf9b..d3f6f3c564c89 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -255,8 +255,15 @@ interface IPackageManager { * retrieval of information is complete. */ void getPackageSizeInfo(in String packageName, IPackageStatsObserver observer); + + /** + * Get a list of shared libraries that are available on the + * system. + */ + String[] getSystemSharedLibraryNames(); void enterSafeMode(); + boolean isSafeMode(); void systemReady(); boolean hasSystemUidErrors(); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 4b902e9fe944f..698f27fc0a3fc 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -851,6 +851,16 @@ public abstract class PackageManager { * @see #GET_UNINSTALLED_PACKAGES */ public abstract List getInstalledApplications(int flags); + + /** + * Get a list of shared libraries that are available on the + * system. + * + * @return An array of shared library names that are + * available on the system, or null if none are installed. + * + */ + public abstract String[] getSystemSharedLibraryNames(); /** * Determine the best action to perform for a given Intent. This is how @@ -1608,4 +1618,9 @@ public abstract class PackageManager { * the manifest as found in {@link ComponentInfo}. */ public abstract int getApplicationEnabledSetting(String packageName); + + /** + * Return whether the device has been booted into safe mode. + */ + public abstract boolean isSafeMode(); } diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 4b1e67843608c..17cb68745d8be 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -304,6 +304,11 @@ public class ColorStateList implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { + final int N = mStateSpecs.length; + dest.writeInt(N); + for (int i=0; i mPrograms; private final RuntimeException mLeakedException; + + // package visible, since callers will access directly to minimize overhead in the case + // that logging is not enabled. + /* package */ final boolean mLogStats; + /** * @param closable */ @@ -436,7 +443,14 @@ public class SQLiteDatabase extends SQLiteClosable { if (mTransactionIsSuccessful) { execSQL("COMMIT;"); } else { - execSQL("ROLLBACK;"); + try { + execSQL("ROLLBACK;"); + } catch (SQLException e) { + if (Config.LOGD) { + Log.d(TAG, "exception during rollback, maybe the DB previously " + + "performed an auto-rollback"); + } + } } } finally { unlockForced(); @@ -1472,10 +1486,8 @@ public class SQLiteDatabase extends SQLiteClosable { * @throws SQLException If the SQL string is invalid for some reason */ public void execSQL(String sql) throws SQLException { - long timeStart = 0; - if (Config.LOGV) { - timeStart = System.currentTimeMillis(); - } + boolean logStats = mLogStats; + long timeStart = logStats ? SystemClock.elapsedRealtime() : 0; lock(); try { native_execSQL(sql); @@ -1485,9 +1497,8 @@ public class SQLiteDatabase extends SQLiteClosable { } finally { unlock(); } - if (Config.LOGV) { - long timeEnd = System.currentTimeMillis(); - Log.v(TAG, "Executed (" + (timeEnd - timeStart) + " ms):" + sql); + if (logStats) { + logTimeStat(false /* not a read */, timeStart, SystemClock.elapsedRealtime()); } } @@ -1504,10 +1515,9 @@ public class SQLiteDatabase extends SQLiteClosable { if (bindArgs == null) { throw new IllegalArgumentException("Empty bindArgs"); } - long timeStart = 0; - if (Config.LOGV) { - timeStart = System.currentTimeMillis(); - } + + boolean logStats = mLogStats; + long timeStart = logStats ? SystemClock.elapsedRealtime() : 0; lock(); SQLiteStatement statement = null; try { @@ -1528,9 +1538,8 @@ public class SQLiteDatabase extends SQLiteClosable { } unlock(); } - if (Config.LOGV) { - long timeEnd = System.currentTimeMillis(); - Log.v(TAG, "Executed (" + (timeEnd - timeStart) + " ms):" + sql); + if (logStats) { + logTimeStat(false /* not a read */, timeStart, SystemClock.elapsedRealtime()); } } @@ -1550,7 +1559,7 @@ public class SQLiteDatabase extends SQLiteClosable { } /** - * Private constructor. See {@link createDatabase} and {@link openDatabase}. + * Private constructor. See {@link #create} and {@link #openDatabase}. * * @param path The full path to the database * @param factory The factory to use when creating cursors, may be NULL. @@ -1563,6 +1572,8 @@ public class SQLiteDatabase extends SQLiteClosable { } mFlags = flags; mPath = path; + mLogStats = "1".equals(android.os.SystemProperties.get("db.logstats")); + mLeakedException = new IllegalStateException(path + " SQLiteDatabase created and never closed"); mFactory = factory; @@ -1605,6 +1616,10 @@ public class SQLiteDatabase extends SQLiteClosable { return mPath; } + /* package */ void logTimeStat(boolean read, long begin, long end) { + EventLog.writeEvent(DB_OPERATION_EVENT, mPath, read ? 0 : 1, end - begin); + } + /** * Sets the locale for this database. Does nothing if this database has * the NO_LOCALIZED_COLLATORS flag set or was opened read only. @@ -1629,7 +1644,7 @@ public class SQLiteDatabase extends SQLiteClosable { private native void dbopen(String path, int flags); /** - * Native call to execute a raw SQL statement. {@link mLock} must be held + * Native call to execute a raw SQL statement. {@link #lock} must be held * when calling this method. * * @param sql The raw SQL string @@ -1638,7 +1653,7 @@ public class SQLiteDatabase extends SQLiteClosable { /* package */ native void native_execSQL(String sql) throws SQLException; /** - * Native call to set the locale. {@link mLock} must be held when calling + * Native call to set the locale. {@link #lock} must be held when calling * this method. * @throws SQLException */ diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 35bf6456ed97c..52aac3a822b3f 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -26,6 +26,8 @@ import android.util.Log; * optionally {@link #onOpen}, and this class takes care of opening the database * if it exists, creating it if it does not, and upgrading it as necessary. * Transactions are used to make sure the database is always in a sensible state. + *

    For an example, see the NotePadProvider class in the NotePad sample application, + * in the samples/ directory of the SDK.

    */ public abstract class SQLiteOpenHelper { private static final String TAG = SQLiteOpenHelper.class.getSimpleName(); diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java index 22c53abca37e2..5bfa0e85025f5 100644 --- a/core/java/android/database/sqlite/SQLiteQuery.java +++ b/core/java/android/database/sqlite/SQLiteQuery.java @@ -17,6 +17,7 @@ package android.database.sqlite; import android.database.CursorWindow; +import android.os.SystemClock; /** * A SQLite program that represents a query that reads the resulting rows into a CursorWindow. @@ -55,12 +56,14 @@ public class SQLiteQuery extends SQLiteProgram { * Reads rows into a buffer. This method acquires the database lock. * * @param window The window to fill into - * @param startPos The position to start reading rows from * @return number of total rows in the query */ /* package */ int fillWindow(CursorWindow window, int maxRead, int lastPos) { mDatabase.lock(); + + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; try { acquireReference(); try { @@ -68,8 +71,13 @@ public class SQLiteQuery extends SQLiteProgram { // if the start pos is not equal to 0, then most likely window is // too small for the data set, loading by another thread // is not safe in this situation. the native code will ignore maxRead - return native_fill_window(window, window.getStartPosition(), mOffsetIndex, + int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex, maxRead, lastPos); + if (logStats) { + mDatabase.logTimeStat(true /* read */, startTime, + SystemClock.elapsedRealtime()); + } + return numRows; } catch (IllegalStateException e){ // simply ignore it return 0; diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java index bf9361dccf891..d169259566995 100644 --- a/core/java/android/database/sqlite/SQLiteStatement.java +++ b/core/java/android/database/sqlite/SQLiteStatement.java @@ -16,6 +16,8 @@ package android.database.sqlite; +import android.os.SystemClock; + /** * A pre-compiled statement against a {@link SQLiteDatabase} that can be reused. * The statement cannot return multiple rows, but 1x1 result sets are allowed. @@ -43,10 +45,16 @@ public class SQLiteStatement extends SQLiteProgram */ public void execute() { mDatabase.lock(); + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; + acquireReference(); try { native_execute(); - } finally { + if (logStats) { + mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); + } + } finally { releaseReference(); mDatabase.unlock(); } @@ -64,9 +72,15 @@ public class SQLiteStatement extends SQLiteProgram */ public long executeInsert() { mDatabase.lock(); + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; + acquireReference(); try { native_execute(); + if (logStats) { + mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); + } return mDatabase.lastInsertRow(); } finally { releaseReference(); @@ -84,9 +98,16 @@ public class SQLiteStatement extends SQLiteProgram */ public long simpleQueryForLong() { mDatabase.lock(); + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; + acquireReference(); try { - return native_1x1_long(); + long retValue = native_1x1_long(); + if (logStats) { + mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); + } + return retValue; } finally { releaseReference(); mDatabase.unlock(); @@ -103,9 +124,16 @@ public class SQLiteStatement extends SQLiteProgram */ public String simpleQueryForString() { mDatabase.lock(); + boolean logStats = mDatabase.mLogStats; + long startTime = logStats ? SystemClock.elapsedRealtime() : 0; + acquireReference(); try { - return native_1x1_string(); + String retValue = native_1x1_string(); + if (logStats) { + mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); + } + return retValue; } finally { releaseReference(); mDatabase.unlock(); diff --git a/core/java/android/database/sqlite/package.html b/core/java/android/database/sqlite/package.html index c03a8dc02830f..ff0f9f58f3571 100644 --- a/core/java/android/database/sqlite/package.html +++ b/core/java/android/database/sqlite/package.html @@ -6,7 +6,7 @@ classes that an application would use to manage its own private database. Applications use these classes to maange private databases. If creating a content provider, you will probably have to use these classes to create and manage your own database to store content. See Storing, Retrieving and Exposing Data to learn +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers to learn the conventions for implementing a content provider. See the NotePadProvider class in the NotePad sample application in the SDK for an example of a content provider. Android ships with SQLite version 3.4.0 diff --git a/core/java/android/gadget/GadgetHost.java b/core/java/android/gadget/GadgetHost.java index 418f2aa17998b..9176d18298534 100644 --- a/core/java/android/gadget/GadgetHost.java +++ b/core/java/android/gadget/GadgetHost.java @@ -17,14 +17,67 @@ package android.gadget; import android.content.Context; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; import android.widget.RemoteViews; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import com.android.internal.gadget.IGadgetHost; +import com.android.internal.gadget.IGadgetService; + /** * GadgetHost provides the interaction with the Gadget Service for apps, * like the home screen, that want to embed gadgets in their UI. */ public class GadgetHost { + + static final int HANDLE_UPDATE = 1; + + static Object sServiceLock = new Object(); + static IGadgetService sService; + + Context mContext; + String mPackageName; + + class Callbacks extends IGadgetHost.Stub { + public void updateGadget(int gadgetId, RemoteViews views) { + Message msg = mHandler.obtainMessage(HANDLE_UPDATE); + msg.arg1 = gadgetId; + msg.obj = views; + msg.sendToTarget(); + } + } + + Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + switch (msg.what) { + case HANDLE_UPDATE: { + updateGadgetView(msg.arg1, (RemoteViews)msg.obj); + break; + } + } + } + }; + + int mHostId; + Callbacks mCallbacks = new Callbacks(); + HashMap mViews = new HashMap(); + public GadgetHost(Context context, int hostId) { + mContext = context; + mHostId = hostId; + synchronized (sServiceLock) { + if (sService == null) { + IBinder b = ServiceManager.getService(Context.GADGET_SERVICE); + sService = IGadgetService.Stub.asInterface(b); + } + } } /** @@ -32,6 +85,23 @@ public class GadgetHost { * becomes visible, i.e. from onStart() in your Activity. */ public void startListening() { + int[] updatedIds = null; + ArrayList updatedViews = new ArrayList(); + + try { + if (mPackageName == null) { + mPackageName = mContext.getPackageName(); + } + updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + + final int N = updatedIds.length; + for (int i=0; i + *
  8. Call this when initializing your database, as it might be because of a data wipe.
  9. + *
  10. Call this to have the gadget manager release all resources associated with your + * host. Any future calls about this host will cause the records to be re-allocated.
  11. + * + */ + public void deleteHost() { + try { + sService.deleteHost(mHostId); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Remove all records about all hosts for your package. + *
      + *
    • Call this when initializing your database, as it might be because of a data wipe.
    • + *
    • Call this to have the gadget manager release all resources associated with your + * host. Any future calls about this host will cause the records to be re-allocated.
    • + *
    + */ + public static void deleteAllHosts() { + try { + sService.deleteAllHosts(); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } } public final GadgetHostView createView(Context context, int gadgetId, GadgetInfo gadget) { GadgetHostView view = onCreateView(context, gadgetId, gadget); view.setGadget(gadgetId, gadget); - view.updateGadget(null); + synchronized (mViews) { + mViews.put(gadgetId, view); + } + RemoteViews views = null; + try { + views = sService.getGadgetViews(gadgetId); + } catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + view.updateGadget(views); return view; } @@ -68,5 +206,16 @@ public class GadgetHost { protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) { return new GadgetHostView(context); } + + void updateGadgetView(int gadgetId, RemoteViews views) { + GadgetHostView v; + synchronized (mViews) { + v = mViews.get(gadgetId); + } + if (v != null) { + v.updateGadget(views); + } + } } + diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/gadget/GadgetHostView.java index e2bef8c12e40b..d92c1236f078b 100644 --- a/core/java/android/gadget/GadgetHostView.java +++ b/core/java/android/gadget/GadgetHostView.java @@ -19,16 +19,21 @@ package android.gadget; import android.content.Context; import android.content.pm.PackageManager; import android.gadget.GadgetInfo; -import android.util.AttributeSet; +import android.graphics.Color; +import android.util.Config; import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.RemoteViews; import android.widget.TextView; +import android.widget.ViewAnimator; -public class GadgetHostView extends FrameLayout { +public class GadgetHostView extends ViewAnimator { static final String TAG = "GadgetHostView"; + static final boolean LOGD = Config.LOGD || true; // When we're inflating the initialLayout for a gadget, we only allow // views that are allowed in RemoteViews. @@ -40,63 +45,139 @@ public class GadgetHostView extends FrameLayout { int mGadgetId; GadgetInfo mInfo; - View mContentView; + View mActiveView; + View mStaleView; + + protected int mDefaultGravity = Gravity.CENTER; public GadgetHostView(Context context) { super(context); } public void setGadget(int gadgetId, GadgetInfo info) { + if (LOGD) Log.d(TAG, "setGadget is incoming with info=" + info); if (mInfo != null) { // TODO: remove the old view, or whatever } mGadgetId = gadgetId; mInfo = info; + + View defaultView = getDefaultView(); + flipUpdate(defaultView); + } + + /** + * Trigger actual animation between current and new content in the + * {@link ViewAnimator}. + */ + protected void flipUpdate(View newContent) { + if (LOGD) Log.d(TAG, "pushing an update to surface"); + + // Take requested dimensions from parent, but apply default gravity. + ViewGroup.LayoutParams requested = newContent.getLayoutParams(); + if (requested == null) { + requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.FILL_PARENT); + } + + FrameLayout.LayoutParams params = + new FrameLayout.LayoutParams(requested.width, requested.height); + params.gravity = mDefaultGravity; + newContent.setLayoutParams(params); + + // Add new content and animate to it + addView(newContent); + showNext(); + + // Dispose old stale view + removeView(mStaleView); + mStaleView = mActiveView; + mActiveView = newContent; } + /** + * Process a set of {@link RemoteViews} coming in as an update from the + * gadget provider. Will animate into these new views as needed. + */ public void updateGadget(RemoteViews remoteViews) { - Context context = getContext(); - - View contentView = null; + if (LOGD) Log.d(TAG, "updateGadget() with remoteViews = " + remoteViews); + + View newContent = null; Exception exception = null; + try { if (remoteViews == null) { // there is no remoteViews (yet), so use the initial layout - Context theirContext = context.createPackageContext(mInfo.provider.getPackageName(), - 0); - LayoutInflater inflater = (LayoutInflater)theirContext.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - inflater = inflater.cloneInContext(theirContext); - inflater.setFilter(sInflaterFilter); - contentView = inflater.inflate(mInfo.initialLayout, this, false); + newContent = getDefaultView(); } else { // use the RemoteViews - contentView = remoteViews.apply(mContext, this); + // TODO: try applying RemoteViews to existing staleView if available + newContent = remoteViews.apply(mContext, this); } - } - catch (PackageManager.NameNotFoundException e) { + } catch (RuntimeException e) { exception = e; } - catch (RuntimeException e) { - exception = e; - } - if (contentView == null) { + + if (exception != null && LOGD) { Log.w(TAG, "Error inflating gadget " + mInfo, exception); + } + + if (newContent == null) { // TODO: Should we throw an exception here for the host activity to catch? // Maybe we should show a generic error widget. - TextView tv = new TextView(context); - tv.setText("Error inflating gadget"); - contentView = tv; + if (LOGD) Log.d(TAG, "updateGadget couldn't find any view, so inflating error"); + newContent = getErrorView(); } - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - FrameLayout.LayoutParams.WRAP_CONTENT); - - mContentView = contentView; - this.addView(contentView, lp); - - // TODO: do an animation (maybe even one provided by the gadget). + flipUpdate(newContent); + } + + /** + * Inflate and return the default layout requested by gadget provider. + */ + protected View getDefaultView() { + View defaultView = null; + Exception exception = null; + + try { + if (mInfo != null) { + Context theirContext = mContext.createPackageContext( + mInfo.provider.getPackageName(), 0 /* no flags */); + LayoutInflater inflater = (LayoutInflater) + theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater = inflater.cloneInContext(theirContext); + inflater.setFilter(sInflaterFilter); + defaultView = inflater.inflate(mInfo.initialLayout, this, false); + } else { + Log.w(TAG, "can't inflate defaultView because mInfo is missing"); + } + } catch (PackageManager.NameNotFoundException e) { + exception = e; + } catch (RuntimeException e) { + exception = e; + } + + if (exception != null && LOGD) { + Log.w(TAG, "Error inflating gadget " + mInfo, exception); + } + + if (defaultView == null) { + if (LOGD) Log.d(TAG, "getDefaultView couldn't find any view, so inflating error"); + defaultView = getErrorView(); + } + + return defaultView; + } + + /** + * Inflate and return a view that represents an error state. + */ + protected View getErrorView() { + TextView tv = new TextView(mContext); + // TODO: move this error string and background color into resources + tv.setText("Error inflating gadget"); + tv.setBackgroundColor(Color.argb(127, 0, 0, 0)); + return tv; } } diff --git a/core/java/android/gadget/GadgetInfo.java b/core/java/android/gadget/GadgetInfo.java index 1a7a9a0ef0443..5ac3da9cf2d11 100644 --- a/core/java/android/gadget/GadgetInfo.java +++ b/core/java/android/gadget/GadgetInfo.java @@ -58,6 +58,16 @@ public class GadgetInfo implements Parcelable { */ public ComponentName configure; + /** + * The label to display to the user. + */ + public String label; + + /** + * The icon to display for this gadget in the picker list. + */ + public int icon; + public GadgetInfo() { } @@ -75,6 +85,8 @@ public class GadgetInfo implements Parcelable { if (0 != in.readInt()) { this.configure = new ComponentName(in); } + this.label = in.readString(); + this.icon = in.readInt(); } @@ -95,6 +107,8 @@ public class GadgetInfo implements Parcelable { } else { out.writeInt(0); } + out.writeString(this.label); + out.writeInt(this.icon); } public int describeContents() { diff --git a/core/java/android/gadget/GadgetManager.java b/core/java/android/gadget/GadgetManager.java index 088dc86f9d588..20f40140cbdbe 100644 --- a/core/java/android/gadget/GadgetManager.java +++ b/core/java/android/gadget/GadgetManager.java @@ -30,6 +30,10 @@ import java.lang.ref.WeakReference; import java.util.List; import java.util.WeakHashMap; +/** + * Updates gadget state; gets information about installed gadget providers and other + * gadget related state. + */ public class GadgetManager { static final String TAG = "GadgetManager"; @@ -40,9 +44,8 @@ public class GadgetManager { * The system will respond with an onActivityResult call with the following extras in * the intent: *
      - *
    • gadgetId
    • - *
    • gadgetId
    • - *
    • gadgetId
    • + *
    • gadgetIds
    • + *
    • hostId
    • *
    * TODO: Add constants for these. * TODO: Where does this go? @@ -50,21 +53,42 @@ public class GadgetManager { public static final String GADGET_PICK_ACTION = "android.gadget.action.PICK_GADGET"; public static final String EXTRA_GADGET_ID = "gadgetId"; + public static final String EXTRA_GADGET_IDS = "gadgetIds"; + public static final String EXTRA_HOST_ID = "hostId"; /** * Sent when it is time to update your gadget. + * + *

    This may be sent in response to a new instance for this gadget provider having + * been instantiated, the requested {@link GadgetInfo#updatePeriodMillis update interval} + * having lapsed, or the system booting. */ public static final String GADGET_UPDATE_ACTION = "android.gadget.action.GADGET_UPDATE"; /** - * Sent when the gadget is added to a host for the first time. TODO: Maybe we don't want this. + * Sent when it is time to configure your gadget. This action is not sent as a broadcast + * to the gadget provider, but as a startActivity to the activity specified in the + * {@link GadgetInfo GadgetInfo meta-data}. + * + *

    The {@link #EXTRA_GADGET_ID} extra contains the gadget ID. */ - public static final String GADGET_ENABLE_ACTION = "android.gadget.action.GADGET_ENABLE"; + public static final String GADGET_CONFIGURE_ACTION = "android.gadget.action.GADGET_CONFIGURE"; /** - * Sent when the gadget is removed from the last host. TODO: Maybe we don't want this. + * Sent when the gadget is added to a host for the first time. This broadcast is sent at + * boot time if there is a gadget host installed with an instance for this provider. */ - public static final String GADGET_DISABLE_ACTION = "android.gadget.action.GADGET_DISABLE"; + public static final String GADGET_ENABLED_ACTION = "android.gadget.action.GADGET_ENABLED"; + + /** + * Sent when an instances of a gadget is deleted from the host. + */ + public static final String GADGET_DELETED_ACTION = "android.gadget.action.GADGET_DELETED"; + + /** + * Sent when the gadget is removed from the last host. + */ + public static final String GADGET_DISABLED_ACTION = "android.gadget.action.GADGET_DISABLED"; /** * Field for the manifest meta-data tag. @@ -106,10 +130,36 @@ public class GadgetManager { *

    * This method will only work when called from the uid that owns the gadget provider. * - * @param gadgetId The gadget instance for which to set the RemoteViews. + * @param gadgetIds The gadget instances for which to set the RemoteViews. * @param views The RemoteViews object to show. */ - public void updateGadget(int gadgetId, RemoteViews views) { + public void updateGadget(int[] gadgetIds, RemoteViews views) { + try { + sService.updateGadgetIds(gadgetIds, views); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Call this with the new RemoteViews for your gadget whenever you need to. + * + *

    + * This method will only work when called from the uid that owns the gadget provider. + * + * @param provider The {@link ComponentName} for the {@link + * android.content.BroadcastReceiver BroadcastReceiver} provider + * for your gadget. + * @param views The RemoteViews object to show. + */ + public void updateGadget(ComponentName provider, RemoteViews views) { + try { + sService.updateGadgetProvider(provider, views); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } } /** @@ -139,32 +189,6 @@ public class GadgetManager { } } - /** - * Get a gadgetId for a host in the calling process. - * - * @return a gadgetId - */ - public int allocateGadgetId(String hostPackage) { - try { - return sService.allocateGadgetId(hostPackage); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } - - /** - * Delete the gadgetId. Same as removeGadget on GadgetHost. - */ - public void deleteGadgetId(int gadgetId) { - try { - sService.deleteGadgetId(gadgetId); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } - /** * Set the component for a given gadgetId. You need the GADGET_LIST permission. */ diff --git a/core/java/android/gadget/GadgetProvider.java b/core/java/android/gadget/GadgetProvider.java new file mode 100755 index 0000000000000..1ddfe3f0babf3 --- /dev/null +++ b/core/java/android/gadget/GadgetProvider.java @@ -0,0 +1,175 @@ +/* + * 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.gadget; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +/** + * A conveience class to aid in implementing a gadget provider. + * Everything you can do with GadgetProvider, you can do with a regular {@link BroadcastReceiver}. + * GadgetProvider merely parses the relevant fields out of the Intent that is received in + * {@link #onReceive(Context,Intent) onReceive(Context,Intent)}, and calls hook methods + * with the received extras. + * + *

    Extend this class and override one or more of the {@link #onUpdate}, {@link #onDeleted}, + * {@link #onEnabled} or {@link #onDisabled} methods to implement your own gadget functionality. + * + *

    Sample Code

    + * For an example of how to write a gadget provider, see the + * android.gadget + * package overview. + */ +public class GadgetProvider extends BroadcastReceiver { + /** + * Constructor to initialize GadgetProvider. + */ + public GadgetProvider() { + } + + /** + * Implements {@link BroadcastReceiver#onReceive} to dispatch calls to the various + * other methods on GadgetProvider. + * + * @param context The Context in which the receiver is running. + * @param intent The Intent being received. + */ + // BEGIN_INCLUDE(onReceive) + public void onReceive(Context context, Intent intent) { + // Protect against rogue update broadcasts (not really a security issue, + // just filter bad broacasts out so subclasses are less likely to crash). + String action = intent.getAction(); + if (GadgetManager.GADGET_UPDATE_ACTION.equals(action)) { + Bundle extras = intent.getExtras(); + if (extras != null) { + int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); + if (gadgetIds != null && gadgetIds.length > 0) { + this.onUpdate(context, GadgetManager.getInstance(context), gadgetIds); + } + } + } + else if (GadgetManager.GADGET_DELETED_ACTION.equals(action)) { + Bundle extras = intent.getExtras(); + if (extras != null) { + int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); + if (gadgetIds != null && gadgetIds.length > 0) { + this.onDeleted(context, gadgetIds); + } + } + } + else if (GadgetManager.GADGET_ENABLED_ACTION.equals(action)) { + this.onEnabled(context); + } + else if (GadgetManager.GADGET_DISABLED_ACTION.equals(action)) { + this.onDisabled(context); + } + } + // END_INCLUDE(onReceive) + + /** + * Called in response to the {@link GadgetManager#GADGET_UPDATE_ACTION} broadcast when + * this gadget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews} + * for a set of gadgets. Override this method to implement your own gadget functionality. + * + * {@more} + *

    If you want this method called, you must declare in an intent-filter in + * your AndroidManifest.xml file that you accept the GADGET_UPDATE_ACTION intent action. + * For example: + * TODO: SAMPLE CODE GOES HERE + *

    + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * @param gadgetManager A {@link GadgetManager} object you can call {@link + * GadgetManager#updateGadgets} on. + * @param gadgetIds The gadgetsIds for which an update is needed. Note that this + * may be all of the gadget instances for this provider, or just + * a subset of them. + * + * @see GadgetManager#GADGET_UPDATE_ACTION + */ + public void onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) { + } + + /** + * Called in response to the {@link GadgetManager#GADGET_DELETED_ACTION} broadcast when + * one or more gadget instances have been deleted. Override this method to implement + * your own gadget functionality. + * + * {@more} + *

    If you want this method called, you must declare in an intent-filter in + * your AndroidManifest.xml file that you accept the GADGET_DELETED_ACTION intent action. + * For example: + * TODO: SAMPLE CODE GOES HERE + *

    + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * @param gadgetIds The gadgetsIds that have been deleted from their host. + * + * @see GadgetManager#GADGET_DELETED_ACTION + */ + public void onDeleted(Context context, int[] gadgetIds) { + } + + /** + * Called in response to the {@link GadgetManager#GADGET_ENABLED_ACTION} broadcast when + * the a gadget for this provider is instantiated. Override this method to implement your + * own gadget functionality. + * + * {@more} + * When the last gadget for this provider is deleted, + * {@link GadgetManager#GADGET_DISABLED_ACTION} is sent and {@link #onDisabled} + * is called. If after that, a gadget for this provider is created again, onEnabled() will + * be called again. + * + *

    If you want this method called, you must declare in an intent-filter in + * your AndroidManifest.xml file that you accept the GADGET_ENABLED_ACTION intent action. + * For example: + * TODO: SAMPLE CODE GOES HERE + *

    + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * + * @see GadgetManager#GADGET_ENABLED_ACTION + */ + public void onEnabled(Context context) { + } + + /** + * Called in response to the {@link GadgetManager#GADGET_DISABLED_ACTION} broadcast, which + * is sent when the last gadget instance for this provider is deleted. Override this method + * to implement your own gadget functionality. + * + * {@more} + *

    If you want this method called, you must declare in an intent-filter in + * your AndroidManifest.xml file that you accept the GADGET_DISABLED_ACTION intent action. + * For example: + * TODO: SAMPLE CODE GOES HERE + *

    + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * + * @see GadgetManager#GADGET_DISABLED_ACTION + */ + public void onDisabled(Context context) { + } +} diff --git a/core/java/android/gadget/package.html b/core/java/android/gadget/package.html index 280ccfb4512d4..4b8b9d9c3d6b2 100644 --- a/core/java/android/gadget/package.html +++ b/core/java/android/gadget/package.html @@ -1,4 +1,51 @@ -@hide +{@hide} +

    Android allows applications to publish views to be embedded in other applications. These +views are called gadgets, and are published by "gadget providers." The component that can +contain gadgets is called a "gadget host." See the links below for more information. +

    +

    Gadget Providers

    + +

    Gadget Hosts

    + +{@more} +

    Gadget Providers

    +

    Any application can publish gadgets. All an application needs to do to publish a gadget is +to have a {@link android.content.BroadcastReceiver} that receives the {@link +android.gadget.GadgetManager#GADGET_UPDATE_ACTION GadgetManager.GADGET_UPDATE_ACTION} intent, +and provide some meta-data about the gadget. + +

    Declaring a gadget in the AndroidManifest

    + +

    Adding the {@link android.gadget.GadgetInfo GadgetInfo} meta-data

    + +

    Using the {@link android.gadget.GadgetProvider GadgetProvider} class

    + +

    Gadget Configuration UI

    + +

    Gadget Broadcast Intents

    + +

    {@link GadgetProvider} is just a convenience class. If you would like to receive the +gadget broadcasts directly, you can. By way of example, the implementation of +{@link GadgetProvider.onReceive} is quite simple:

    + +{@sample frameworks/base/core/java/android/gadget/GadgetProvider.java onReceive} + + +

    Gadget Hosts

    +

    Gadget hosts are the containers in which gadgets can be placed. Most of the look and feel +details are left up to the gadget hosts. For example, the home screen has one way of viewing +gadgets, but the lock screen could also contain gadgets, and it would have a different way of +adding, removing and otherwise managing gadgets.

    +

    For more information on implementing your own gadget host, see the +{@link android.gadget.GadgetHost GadgetHost} class.

    diff --git a/core/java/android/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java new file mode 100644 index 0000000000000..b4c04b1c023f4 --- /dev/null +++ b/core/java/android/hardware/GeomagneticField.java @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2009 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.hardware; + +import java.util.GregorianCalendar; + +/** + * This class is used to estimated estimate magnetic field at a given point on + * Earth, and in particular, to compute the magnetic declination from true + * north. + * + *

    This uses the World Magnetic Model produced by the United States National + * Geospatial-Intelligence Agency. More details about the model can be found at + * http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml. + * This class currently uses WMM-2005 which is valid until 2010, but should + * produce acceptable results for several years after that. + */ +public class GeomagneticField { + // The magnetic field at a given point, in nonoteslas in geodetic + // coordinates. + private float mX; + private float mY; + private float mZ; + + // Geocentric coordinates -- set by computeGeocentricCoordinates. + private float mGcLatitudeRad; + private float mGcLongitudeRad; + private float mGcRadiusKm; + + // Constants from WGS84 (the coordinate system used by GPS) + static private final float EARTH_SEMI_MAJOR_AXIS_KM = 6378.137f; + static private final float EARTH_SEMI_MINOR_AXIS_KM = 6356.7523f; + static private final float EARTH_REFERENCE_RADIUS_KM = 6371.2f; + + // These coefficients and the formulae used below are from: + // NOAA Technical Report: The US/UK World Magnetic Model for 2005-2010 + static private final float[][] G_COEFF = new float[][] { + { 0f }, + { -29556.8f, -1671.7f }, + { -2340.6f, 3046.9f, 1657.0f }, + { 1335.4f, -2305.1f, 1246.7f, 674.0f }, + { 919.8f, 798.1f, 211.3f, -379.4f, 100.0f }, + { -227.4f, 354.6f, 208.7f, -136.5f, -168.3f, -14.1f }, + { 73.2f, 69.7f, 76.7f, -151.2f, -14.9f, 14.6f, -86.3f }, + { 80.1f, -74.5f, -1.4f, 38.5f, 12.4f, 9.5f, 5.7f, 1.8f }, + { 24.9f, 7.7f, -11.6f, -6.9f, -18.2f, 10.0f, 9.2f, -11.6f, -5.2f }, + { 5.6f, 9.9f, 3.5f, -7.0f, 5.1f, -10.8f, -1.3f, 8.8f, -6.7f, -9.1f }, + { -2.3f, -6.3f, 1.6f, -2.6f, 0.0f, 3.1f, 0.4f, 2.1f, 3.9f, -0.1f, -2.3f }, + { 2.8f, -1.6f, -1.7f, 1.7f, -0.1f, 0.1f, -0.7f, 0.7f, 1.8f, 0.0f, 1.1f, 4.1f }, + { -2.4f, -0.4f, 0.2f, 0.8f, -0.3f, 1.1f, -0.5f, 0.4f, -0.3f, -0.3f, -0.1f, + -0.3f, -0.1f } }; + + static private final float[][] H_COEFF = new float[][] { + { 0f }, + { 0.0f, 5079.8f }, + { 0.0f, -2594.7f, -516.7f }, + { 0.0f, -199.9f, 269.3f, -524.2f }, + { 0.0f, 281.5f, -226.0f, 145.8f, -304.7f }, + { 0.0f, 42.4f, 179.8f, -123.0f, -19.5f, 103.6f }, + { 0.0f, -20.3f, 54.7f, 63.6f, -63.4f, -0.1f, 50.4f }, + { 0.0f, -61.5f, -22.4f, 7.2f, 25.4f, 11.0f, -26.4f, -5.1f }, + { 0.0f, 11.2f, -21.0f, 9.6f, -19.8f, 16.1f, 7.7f, -12.9f, -0.2f }, + { 0.0f, -20.1f, 12.9f, 12.6f, -6.7f, -8.1f, 8.0f, 2.9f, -7.9f, 6.0f }, + { 0.0f, 2.4f, 0.2f, 4.4f, 4.8f, -6.5f, -1.1f, -3.4f, -0.8f, -2.3f, -7.9f }, + { 0.0f, 0.3f, 1.2f, -0.8f, -2.5f, 0.9f, -0.6f, -2.7f, -0.9f, -1.3f, -2.0f, -1.2f }, + { 0.0f, -0.4f, 0.3f, 2.4f, -2.6f, 0.6f, 0.3f, 0.0f, 0.0f, 0.3f, -0.9f, -0.4f, + 0.8f } }; + + static private final float[][] DELTA_G = new float[][] { + { 0f }, + { 8.0f, 10.6f }, + { -15.1f, -7.8f, -0.8f }, + { 0.4f, -2.6f, -1.2f, -6.5f }, + { -2.5f, 2.8f, -7.0f, 6.2f, -3.8f }, + { -2.8f, 0.7f, -3.2f, -1.1f, 0.1f, -0.8f }, + { -0.7f, 0.4f, -0.3f, 2.3f, -2.1f, -0.6f, 1.4f }, + { 0.2f, -0.1f, -0.3f, 1.1f, 0.6f, 0.5f, -0.4f, 0.6f }, + { 0.1f, 0.3f, -0.4f, 0.3f, -0.3f, 0.2f, 0.4f, -0.7f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } }; + + static private final float[][] DELTA_H = new float[][] { + { 0f }, + { 0.0f, -20.9f }, + { 0.0f, -23.2f, -14.6f }, + { 0.0f, 5.0f, -7.0f, -0.6f }, + { 0.0f, 2.2f, 1.6f, 5.8f, 0.1f }, + { 0.0f, 0.0f, 1.7f, 2.1f, 4.8f, -1.1f }, + { 0.0f, -0.6f, -1.9f, -0.4f, -0.5f, -0.3f, 0.7f }, + { 0.0f, 0.6f, 0.4f, 0.2f, 0.3f, -0.8f, -0.2f, 0.1f }, + { 0.0f, -0.2f, 0.1f, 0.3f, 0.4f, 0.1f, -0.2f, 0.4f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } }; + + static private final long BASE_TIME = + new GregorianCalendar(2005, 1, 1).getTimeInMillis(); + + // The ratio between the Gauss-normalized associated Legendre functions and + // the Schmid quasi-normalized ones. Compute these once staticly since they + // don't depend on input variables at all. + static private final float[][] SCHMIDT_QUASI_NORM_FACTORS = + computeSchmidtQuasiNormFactors(G_COEFF.length); + + /** + * Estimate the magnetic field at a given point and time. + * + * @param gdLatitudeDeg + * Latitude in WGS84 geodetic coordinates -- positive is east. + * @param gdLongitudeDeg + * Longitude in WGS84 geodetic coordinates -- positive is north. + * @param altitudeMeters + * Altitude in WGS84 geodetic coordinates, in meters. + * @param timeMillis + * Time at which to evaluate the declination, in milliseconds + * since January 1, 1970. (approximate is fine -- the declination + * changes very slowly). + */ + public GeomagneticField(float gdLatitudeDeg, + float gdLongitudeDeg, + float altitudeMeters, + long timeMillis) { + final int MAX_N = G_COEFF.length; // Maximum degree of the coefficients. + + // We don't handle the north and south poles correctly -- pretend that + // we're not quite at them to avoid crashing. + gdLatitudeDeg = Math.min(90.0f - 1e-5f, + Math.max(-90.0f + 1e-5f, gdLatitudeDeg)); + computeGeocentricCoordinates(gdLatitudeDeg, + gdLongitudeDeg, + altitudeMeters); + + assert G_COEFF.length == H_COEFF.length; + + // Note: LegendreTable computes associated Legendre functions for + // cos(theta). We want the associated Legendre functions for + // sin(latitude), which is the same as cos(PI/2 - latitude), except the + // derivate will be negated. + LegendreTable legendre = + new LegendreTable(MAX_N - 1, + (float) (Math.PI / 2.0 - mGcLatitudeRad)); + + // Compute a table of (EARTH_REFERENCE_RADIUS_KM / radius)^n for i in + // 0..MAX_N-2 (this is much faster than calling Math.pow MAX_N+1 times). + float[] relativeRadiusPower = new float[MAX_N + 2]; + relativeRadiusPower[0] = 1.0f; + relativeRadiusPower[1] = EARTH_REFERENCE_RADIUS_KM / mGcRadiusKm; + for (int i = 2; i < relativeRadiusPower.length; ++i) { + relativeRadiusPower[i] = relativeRadiusPower[i - 1] * + relativeRadiusPower[1]; + } + + // Compute tables of sin(lon * m) and cos(lon * m) for m = 0..MAX_N -- + // this is much faster than calling Math.sin and Math.com MAX_N+1 times. + float[] sinMLon = new float[MAX_N]; + float[] cosMLon = new float[MAX_N]; + sinMLon[0] = 0.0f; + cosMLon[0] = 1.0f; + sinMLon[1] = (float) Math.sin(mGcLongitudeRad); + cosMLon[1] = (float) Math.cos(mGcLongitudeRad); + + for (int m = 2; m < MAX_N; ++m) { + // Standard expansions for sin((m-x)*theta + x*theta) and + // cos((m-x)*theta + x*theta). + int x = m >> 1; + sinMLon[m] = sinMLon[m-x] * cosMLon[x] + cosMLon[m-x] * sinMLon[x]; + cosMLon[m] = cosMLon[m-x] * cosMLon[x] - sinMLon[m-x] * sinMLon[x]; + } + + float inverseCosLatitude = 1.0f / (float) Math.cos(mGcLatitudeRad); + float yearsSinceBase = + (timeMillis - BASE_TIME) / (365f * 24f * 60f * 60f * 1000f); + + // We now compute the magnetic field strength given the geocentric + // location. The magnetic field is the derivative of the potential + // function defined by the model. See NOAA Technical Report: The US/UK + // World Magnetic Model for 2005-2010 for the derivation. + float gcX = 0.0f; // Geocentric northwards component. + float gcY = 0.0f; // Geocentric eastwards component. + float gcZ = 0.0f; // Geocentric downwards component. + + for (int n = 1; n < MAX_N; n++) { + for (int m = 0; m <= n; m++) { + // Adjust the coefficients for the current date. + float g = G_COEFF[n][m] + yearsSinceBase * DELTA_G[n][m]; + float h = H_COEFF[n][m] + yearsSinceBase * DELTA_H[n][m]; + + // Negative derivative with respect to latitude, divided by + // radius. This looks like the negation of the version in the + // NOAA Techincal report because that report used + // P_n^m(sin(theta)) and we use P_n^m(cos(90 - theta)), so the + // derivative with respect to theta is negated. + gcX += relativeRadiusPower[n+2] + * (g * cosMLon[m] + h * sinMLon[m]) + * legendre.mPDeriv[n][m] + * SCHMIDT_QUASI_NORM_FACTORS[n][m]; + + // Negative derivative with respect to longitude, divided by + // radius. + gcY += relativeRadiusPower[n+2] * m + * (g * sinMLon[m] - h * cosMLon[m]) + * legendre.mP[n][m] + * SCHMIDT_QUASI_NORM_FACTORS[n][m] + * inverseCosLatitude; + + // Negative derivative with respect to radius. + gcZ -= (n + 1) * relativeRadiusPower[n+2] + * (g * cosMLon[m] + h * sinMLon[m]) + * legendre.mP[n][m] + * SCHMIDT_QUASI_NORM_FACTORS[n][m]; + } + } + + // Convert back to geodetic coordinates. This is basically just a + // rotation around the Y-axis by the difference in latitudes between the + // geocentric frame and the geodetic frame. + double latDiffRad = Math.toRadians(gdLatitudeDeg) - mGcLatitudeRad; + mX = (float) (gcX * Math.cos(latDiffRad) + + gcZ * Math.sin(latDiffRad)); + mY = gcY; + mZ = (float) (- gcX * Math.sin(latDiffRad) + + gcZ * Math.cos(latDiffRad)); + } + + /** + * @return The X (northward) component of the magnetic field in nanoteslas. + */ + public float getX() { + return mX; + } + + /** + * @return The Y (eastward) component of the magnetic field in nanoteslas. + */ + public float getY() { + return mY; + } + + /** + * @return The Z (downward) component of the magnetic field in nanoteslas. + */ + public float getZ() { + return mZ; + } + + /** + * @return The declination of the horizontal component of the magnetic + * field from true north, in degrees (i.e. positive means the + * magnetic field is rotated east that much from true north). + */ + public float getDeclination() { + return (float) Math.toDegrees(Math.atan2(mY, mX)); + } + + /** + * @return The inclination of the magnetic field in degrees -- positive + * means the magnetic field is rotated downwards. + */ + public float getInclination() { + return (float) Math.toDegrees(Math.atan2(mZ, + getHorizontalStrength())); + } + + /** + * @return Horizontal component of the field strength in nonoteslas. + */ + public float getHorizontalStrength() { + return (float) Math.sqrt(mX * mX + mY * mY); + } + + /** + * @return Total field strength in nanoteslas. + */ + public float getFieldStrength() { + return (float) Math.sqrt(mX * mX + mY * mY + mZ * mZ); + } + + /** + * @param gdLatitudeDeg + * Latitude in WGS84 geodetic coordinates. + * @param gdLongitudeDeg + * Longitude in WGS84 geodetic coordinates. + * @param altitudeMeters + * Altitude above sea level in WGS84 geodetic coordinates. + * @return Geocentric latitude (i.e. angle between closest point on the + * equator and this point, at the center of the earth. + */ + private void computeGeocentricCoordinates(float gdLatitudeDeg, + float gdLongitudeDeg, + float altitudeMeters) { + float altitudeKm = altitudeMeters / 1000.0f; + float a2 = EARTH_SEMI_MAJOR_AXIS_KM * EARTH_SEMI_MAJOR_AXIS_KM; + float b2 = EARTH_SEMI_MINOR_AXIS_KM * EARTH_SEMI_MINOR_AXIS_KM; + double gdLatRad = Math.toRadians(gdLatitudeDeg); + float clat = (float) Math.cos(gdLatRad); + float slat = (float) Math.sin(gdLatRad); + float tlat = slat / clat; + float latRad = + (float) Math.sqrt(a2 * clat * clat + b2 * slat * slat); + + mGcLatitudeRad = (float) Math.atan(tlat * (latRad * altitudeKm + b2) + / (latRad * altitudeKm + a2)); + + mGcLongitudeRad = (float) Math.toRadians(gdLongitudeDeg); + + float radSq = altitudeKm * altitudeKm + + 2 * altitudeKm * (float) Math.sqrt(a2 * clat * clat + + b2 * slat * slat) + + (a2 * a2 * clat * clat + b2 * b2 * slat * slat) + / (a2 * clat * clat + b2 * slat * slat); + mGcRadiusKm = (float) Math.sqrt(radSq); + } + + + /** + * Utility class to compute a table of Gauss-normalized associated Legendre + * functions P_n^m(cos(theta)) + */ + static private class LegendreTable { + // These are the Gauss-normalized associated Legendre functions -- that + // is, they are normal Legendre functions multiplied by + // (n-m)!/(2n-1)!! (where (2n-1)!! = 1*3*5*...*2n-1) + public final float[][] mP; + + // Derivative of mP, with respect to theta. + public final float[][] mPDeriv; + + /** + * @param maxN + * The maximum n- and m-values to support + * @param thetaRad + * Returned functions will be Gauss-normalized + * P_n^m(cos(thetaRad)), with thetaRad in radians. + */ + public LegendreTable(int maxN, float thetaRad) { + // Compute the table of Gauss-normalized associated Legendre + // functions using standard recursion relations. Also compute the + // table of derivatives using the derivative of the recursion + // relations. + float cos = (float) Math.cos(thetaRad); + float sin = (float) Math.sin(thetaRad); + + mP = new float[maxN + 1][]; + mPDeriv = new float[maxN + 1][]; + mP[0] = new float[] { 1.0f }; + mPDeriv[0] = new float[] { 0.0f }; + for (int n = 1; n <= maxN; n++) { + mP[n] = new float[n + 1]; + mPDeriv[n] = new float[n + 1]; + for (int m = 0; m <= n; m++) { + if (n == m) { + mP[n][m] = sin * mP[n - 1][m - 1]; + mPDeriv[n][m] = cos * mP[n - 1][m - 1] + + sin * mPDeriv[n - 1][m - 1]; + } else if (n == 1 || m == n - 1) { + mP[n][m] = cos * mP[n - 1][m]; + mPDeriv[n][m] = -sin * mP[n - 1][m] + + cos * mPDeriv[n - 1][m]; + } else { + assert n > 1 && m < n - 1; + float k = ((n - 1) * (n - 1) - m * m) + / (float) ((2 * n - 1) * (2 * n - 3)); + mP[n][m] = cos * mP[n - 1][m] - k * mP[n - 2][m]; + mPDeriv[n][m] = -sin * mP[n - 1][m] + + cos * mPDeriv[n - 1][m] - k * mPDeriv[n - 2][m]; + } + } + } + } + } + + /** + * Compute the ration between the Gauss-normalized associated Legendre + * functions and the Schmidt quasi-normalized version. This is equivalent to + * sqrt((m==0?1:2)*(n-m)!/(n+m!))*(2n-1)!!/(n-m)! + */ + private static float[][] computeSchmidtQuasiNormFactors(int maxN) { + float[][] schmidtQuasiNorm = new float[maxN + 1][]; + schmidtQuasiNorm[0] = new float[] { 1.0f }; + for (int n = 1; n <= maxN; n++) { + schmidtQuasiNorm[n] = new float[n + 1]; + schmidtQuasiNorm[n][0] = + schmidtQuasiNorm[n - 1][0] * (2 * n - 1) / (float) n; + for (int m = 1; m <= n; m++) { + schmidtQuasiNorm[n][m] = schmidtQuasiNorm[n][m - 1] + * (float) Math.sqrt((n - m + 1) * (m == 1 ? 2 : 1) + / (float) (n + m)); + } + } + return schmidtQuasiNorm; + } +} \ No newline at end of file diff --git a/core/java/android/hardware/ISensorService.aidl b/core/java/android/hardware/ISensorService.aidl index 8aad9b465bda0..04af2aec45239 100644 --- a/core/java/android/hardware/ISensorService.aidl +++ b/core/java/android/hardware/ISensorService.aidl @@ -25,5 +25,5 @@ import android.os.ParcelFileDescriptor; interface ISensorService { ParcelFileDescriptor getDataChanel(); - boolean enableSensor(IBinder listener, int sensor, int enable); + boolean enableSensor(IBinder listener, String name, int sensor, int enable); } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index f02094eed63d1..3981f2784ef40 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -741,7 +741,7 @@ public class SensorManager extends IRotationWatcher.Stub */ @Deprecated public void unregisterListener(SensorListener listener) { - unregisterListener(listener, SENSOR_ALL); + unregisterListener(listener, SENSOR_ALL | SENSOR_ORIENTATION_RAW); } /** @@ -829,9 +829,11 @@ public class SensorManager extends IRotationWatcher.Stub } } + String name = sensor.getName(); + int handle = sensor.getHandle(); if (l == null) { l = new ListenerDelegate(listener, sensor, handler); - result = mSensorService.enableSensor(l, sensor.getHandle(), delay); + result = mSensorService.enableSensor(l, name, handle, delay); if (result) { sListeners.add(l); sListeners.notify(); @@ -840,7 +842,7 @@ public class SensorManager extends IRotationWatcher.Stub sSensorThread.startLocked(mSensorService); } } else { - result = mSensorService.enableSensor(l, sensor.getHandle(), delay); + result = mSensorService.enableSensor(l, name, handle, delay); if (result) { l.addSensor(sensor); } @@ -861,8 +863,9 @@ public class SensorManager extends IRotationWatcher.Stub ListenerDelegate l = sListeners.get(i); if (l.getListener() == listener) { // disable these sensors + String name = sensor.getName(); int handle = sensor.getHandle(); - mSensorService.enableSensor(l, handle, SENSOR_DISABLE); + mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE); // if we have no more sensors enabled on this listener, // take it off the list. if (l.removeSensor(sensor) == 0) { @@ -886,7 +889,9 @@ public class SensorManager extends IRotationWatcher.Stub if (l.getListener() == listener) { // disable all sensors for this listener for (Sensor sensor : l.getSensors()) { - mSensorService.enableSensor(l, sensor.getHandle(), SENSOR_DISABLE); + String name = sensor.getName(); + int handle = sensor.getHandle(); + mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE); } sListeners.remove(i); break; diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 7d02f6588db4c..eedcc354e6133 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -24,6 +24,9 @@ import android.view.MotionEvent; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * AbstractInputMethodService provides a abstract base class for input methods. * Normal input method implementations will not derive from this directly, @@ -156,6 +159,13 @@ public abstract class AbstractInputMethodService extends Service */ public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface(); + /** + * Implement this to handle {@link android.os.Binder#dump Binder.dump()} + * calls on your input method. + */ + protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + } + @Override final public IBinder onBind(Intent intent) { if (mInputMethod == null) { diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index e59f38b368db0..d9f10a96aac0f 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -2,6 +2,7 @@ package android.inputmethodservice; import android.content.Context; import android.util.AttributeSet; +import android.view.inputmethod.ExtractedText; import android.widget.EditText; /*** @@ -9,6 +10,9 @@ import android.widget.EditText; * extracted text in a full-screen input method. */ public class ExtractEditText extends EditText { + private InputMethodService mIME; + private int mSettingExtractedText; + public ExtractEditText(Context context) { super(context, null); } @@ -20,4 +24,101 @@ public class ExtractEditText extends EditText { public ExtractEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } + + void setIME(InputMethodService ime) { + mIME = ime; + } + + /** + * Start making changes that will not be reported to the client. That + * is, {@link #onSelectionChanged(int, int)} will not result in sending + * the new selection to the client + */ + public void startInternalChanges() { + mSettingExtractedText += 1; + } + + /** + * Finish making changes that will not be reported to the client. That + * is, {@link #onSelectionChanged(int, int)} will not result in sending + * the new selection to the client + */ + public void finishInternalChanges() { + mSettingExtractedText -= 1; + } + + /** + * Implement just to keep track of when we are setting text from the + * client (vs. seeing changes in ourself from the user). + */ + @Override public void setExtractedText(ExtractedText text) { + try { + mSettingExtractedText++; + super.setExtractedText(text); + } finally { + mSettingExtractedText--; + } + } + + /** + * Report to the underlying text editor about selection changes. + */ + @Override protected void onSelectionChanged(int selStart, int selEnd) { + if (mSettingExtractedText == 0 && mIME != null && selStart >= 0 && selEnd >= 0) { + mIME.onExtractedSelectionChanged(selStart, selEnd); + } + } + + /** + * Redirect clicks to the IME for handling there. First allows any + * on click handler to run, though. + */ + @Override public boolean performClick() { + if (!super.performClick() && mIME != null) { + mIME.onExtractedTextClicked(); + return true; + } + return false; + } + + @Override public boolean onTextContextMenuItem(int id) { + if (mIME != null) { + if (mIME.onExtractTextContextMenuItem(id)) { + return true; + } + } + return super.onTextContextMenuItem(id); + } + + /** + * We are always considered to be an input method target. + */ + public boolean isInputMethodTarget() { + return true; + } + + /** + * Pretend like the window this view is in always has focus, so its + * highlight and cursor will be displayed. + */ + @Override public boolean hasWindowFocus() { + return true; + } + + /** + * Pretend like this view always has focus, so its + * highlight and cursor will be displayed. + */ + @Override public boolean isFocused() { + return true; + } + + /** + * Pretend like this view always has focus, so its + * highlight and cursor will be displayed. + */ + @Override public boolean hasFocus() { + return true; + } + } diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 9abc23ba67032..a2c75b501e090 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -8,6 +8,8 @@ import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputConnectionWrapper; import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Binder; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; @@ -18,6 +20,11 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** * Implements the internal IInputMethod interface to convert incoming calls * on to it back to calls on the public InputMethod interface, scheduling @@ -28,6 +35,7 @@ class IInputMethodWrapper extends IInputMethod.Stub private static final String TAG = "InputMethodWrapper"; private static final boolean DEBUG = false; + private static final int DO_DUMP = 1; private static final int DO_ATTACH_TOKEN = 10; private static final int DO_SET_INPUT_CONTEXT = 20; private static final int DO_UNSET_INPUT_CONTEXT = 30; @@ -39,9 +47,14 @@ class IInputMethodWrapper extends IInputMethod.Stub private static final int DO_SHOW_SOFT_INPUT = 60; private static final int DO_HIDE_SOFT_INPUT = 70; + final AbstractInputMethodService mTarget; final HandlerCaller mCaller; final InputMethod mInputMethod; + static class Notifier { + boolean notified; + } + // NOTE: we should have a cache of these. static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { final Context mContext; @@ -64,7 +77,9 @@ class IInputMethodWrapper extends IInputMethod.Stub } } - public IInputMethodWrapper(Context context, InputMethod inputMethod) { + public IInputMethodWrapper(AbstractInputMethodService context, + InputMethod inputMethod) { + mTarget = context; mCaller = new HandlerCaller(context, this); mInputMethod = inputMethod; } @@ -75,6 +90,20 @@ class IInputMethodWrapper extends IInputMethod.Stub public void executeMessage(Message msg) { switch (msg.what) { + case DO_DUMP: { + HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; + try { + mTarget.dump((FileDescriptor)args.arg1, + (PrintWriter)args.arg2, (String[])args.arg3); + } catch (RuntimeException e) { + ((PrintWriter)args.arg2).println("Exception: " + e); + } + synchronized (args.arg4) { + ((CountDownLatch)args.arg4).countDown(); + } + return; + } + case DO_ATTACH_TOKEN: { mInputMethod.attachToken((IBinder)msg.obj); return; @@ -86,12 +115,22 @@ class IInputMethodWrapper extends IInputMethod.Stub case DO_UNSET_INPUT_CONTEXT: mInputMethod.unbindInput(); return; - case DO_START_INPUT: - mInputMethod.startInput((EditorInfo)msg.obj); + case DO_START_INPUT: { + HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; + IInputContext inputContext = (IInputContext)args.arg1; + InputConnection ic = inputContext != null + ? new InputConnectionWrapper(inputContext) : null; + mInputMethod.startInput(ic, (EditorInfo)args.arg2); return; - case DO_RESTART_INPUT: - mInputMethod.restartInput((EditorInfo)msg.obj); + } + case DO_RESTART_INPUT: { + HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; + IInputContext inputContext = (IInputContext)args.arg1; + InputConnection ic = inputContext != null + ? new InputConnectionWrapper(inputContext) : null; + mInputMethod.restartInput(ic, (EditorInfo)args.arg2); return; + } case DO_CREATE_SESSION: { mInputMethod.createSession(new InputMethodSessionCallbackWrapper( mCaller.mContext, (IInputMethodCallback)msg.obj)); @@ -105,8 +144,7 @@ class IInputMethodWrapper extends IInputMethod.Stub mInputMethod.revokeSession((InputMethodSession)msg.obj); return; case DO_SHOW_SOFT_INPUT: - mInputMethod.showSoftInput( - msg.arg1 != 0 ? InputMethod.SHOW_EXPLICIT : 0); + mInputMethod.showSoftInput(msg.arg1); return; case DO_HIDE_SOFT_INPUT: mInputMethod.hideSoftInput(); @@ -115,6 +153,28 @@ class IInputMethodWrapper extends IInputMethod.Stub Log.w(TAG, "Unhandled message code: " + msg.what); } + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + if (mTarget.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + + fout.println("Permission Denial: can't dump InputMethodManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + CountDownLatch latch = new CountDownLatch(1); + mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP, + fd, fout, args, latch)); + try { + if (!latch.await(5, TimeUnit.SECONDS)) { + fout.println("Timeout waiting for dump"); + } + } catch (InterruptedException e) { + fout.println("Interrupted waiting for dump"); + } + } + public void attachToken(IBinder token) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token)); } @@ -130,12 +190,14 @@ class IInputMethodWrapper extends IInputMethod.Stub mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); } - public void startInput(EditorInfo attribute) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_START_INPUT, attribute)); + public void startInput(IInputContext inputContext, EditorInfo attribute) { + mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT, + inputContext, attribute)); } - public void restartInput(EditorInfo attribute) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RESTART_INPUT, attribute)); + public void restartInput(IInputContext inputContext, EditorInfo attribute) { + mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT, + inputContext, attribute)); } public void createSession(IInputMethodCallback callback) { @@ -163,9 +225,9 @@ class IInputMethodWrapper extends IInputMethod.Stub } } - public void showSoftInput(boolean explicit) { + public void showSoftInput(int flags) { mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_SHOW_SOFT_INPUT, - explicit ? 1 : 0)); + flags)); } public void hideSoftInput() { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 3a9b26a9c6f54..ea5f7414d63de 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -26,7 +26,13 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.IBinder; +import android.provider.Settings; +import android.text.Layout; +import android.text.Spannable; +import android.text.method.MovementMethod; import android.util.Log; +import android.util.PrintWriterPrinter; +import android.util.Printer; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -45,12 +51,31 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.EditorInfo; import android.widget.FrameLayout; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * InputMethodService provides a standard implementation of an InputMethod, * which final implementations can derive from and customize. See the * base class {@link AbstractInputMethodService} and the {@link InputMethod} * interface for more information on the basics of writing input methods. * + *

    In addition to the normal Service lifecycle methods, this class + * introduces some new specific callbacks that most subclasses will want + * to make use of:

    + *
      + *
    • {@link #onInitializeInterface()} for user-interface initialization, + * in particular to deal with configuration changes while the service is + * running. + *
    • {@link #onBindInput} to find out about switching to a new client. + *
    • {@link #onStartInput} to deal with an input session starting with + * the client. + *
    • {@link #onCreateInputView()}, {@link #onCreateCandidatesView()}, + * and {@link #onCreateExtractTextView()} for non-demand generation of the UI. + *
    • {@link #onStartInputView(EditorInfo, boolean)} to deal with input + * starting within the input area of the IME. + *
    + * *

    An input method has significant discretion in how it goes about its * work: the {@link android.inputmethodservice.InputMethodService} provides * a basic framework for standard UI elements (input view, candidates view, @@ -184,6 +209,7 @@ public class InputMethodService extends AbstractInputMethodService { LayoutInflater mInflater; View mRootView; SoftInputWindow mWindow; + boolean mInitialized; boolean mWindowCreated; boolean mWindowAdded; boolean mWindowVisible; @@ -196,11 +222,16 @@ public class InputMethodService extends AbstractInputMethodService { InputBinding mInputBinding; InputConnection mInputConnection; boolean mInputStarted; + boolean mInputViewStarted; + boolean mCandidatesViewStarted; + InputConnection mStartedInputConnection; EditorInfo mInputEditorInfo; boolean mShowInputRequested; boolean mLastShowInputRequested; - boolean mShowCandidatesRequested; + int mCandidatesVisibility; + + boolean mShowInputForced; boolean mFullscreenApplied; boolean mIsFullscreen; @@ -262,6 +293,7 @@ public class InputMethodService extends AbstractInputMethodService { mInputConnection = binding.getConnection(); if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding + " ic=" + mInputConnection); + initialize(); onBindInput(); } @@ -277,14 +309,14 @@ public class InputMethodService extends AbstractInputMethodService { mInputConnection = null; } - public void startInput(EditorInfo attribute) { + public void startInput(InputConnection ic, EditorInfo attribute) { if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute); - doStartInput(attribute, false); + doStartInput(ic, attribute, false); } - public void restartInput(EditorInfo attribute) { + public void restartInput(InputConnection ic, EditorInfo attribute) { if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute); - doStartInput(attribute, true); + doStartInput(ic, attribute, true); } /** @@ -293,6 +325,7 @@ public class InputMethodService extends AbstractInputMethodService { public void hideSoftInput() { if (DEBUG) Log.v(TAG, "hideSoftInput()"); mShowInputRequested = false; + mShowInputForced = false; hideWindow(); } @@ -316,8 +349,7 @@ public class InputMethodService extends AbstractInputMethodService { return; } if (DEBUG) Log.v(TAG, "finishInput() in " + this); - onFinishInput(); - mInputStarted = false; + doFinishInput(); } /** @@ -444,17 +476,37 @@ public class InputMethodService extends AbstractInputMethodService { mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); } + /** + * This is a hook that subclasses can use to perform initialization of + * their interface. It is called for you prior to any of your UI objects + * being created, both after the service is first created and after a + * configuration change happens. + */ + public void onInitializeInterface() { + } + + void initialize() { + if (!mInitialized) { + mInitialized = true; + onInitializeInterface(); + } + } + void initViews() { - mWindowVisible = false; + mInitialized = false; mWindowCreated = false; mShowInputRequested = false; - mShowCandidatesRequested = false; + mShowInputForced = false; mRootView = mInflater.inflate( com.android.internal.R.layout.input_method, null); mWindow.setContentView(mRootView); mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); - + if (Settings.System.getInt(getContentResolver(), + Settings.System.FANCY_IME_ANIMATIONS, 0) != 0) { + mWindow.getWindow().setWindowAnimations( + com.android.internal.R.style.Animation_InputMethodFancy); + } mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea); mExtractView = null; mExtractEditText = null; @@ -466,7 +518,8 @@ public class InputMethodService extends AbstractInputMethodService { mIsInputViewShown = false; mExtractFrame.setVisibility(View.GONE); - mCandidatesFrame.setVisibility(View.INVISIBLE); + mCandidatesVisibility = getCandidatesHiddenVisibility(); + mCandidatesFrame.setVisibility(mCandidatesVisibility); mInputFrame.setVisibility(View.GONE); } @@ -486,19 +539,36 @@ public class InputMethodService extends AbstractInputMethodService { * regenerating the input method UI as a result of the configuration * change, so you can rely on your {@link #onCreateInputView} and * other methods being called as appropriate due to a configuration change. + * + *

    When a configuration change does happen, + * {@link #onInitializeInterface()} is guaranteed to be called the next + * time prior to any of the other input or UI creation callbacks. The + * following will be called immediately depending if appropriate for current + * state: {@link #onStartInput} if input is active, and + * {@link #onCreateInputView} and {@link #onStartInputView} and related + * appropriate functions if the UI is displayed. */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); boolean visible = mWindowVisible; boolean showingInput = mShowInputRequested; - boolean showingCandidates = mShowCandidatesRequested; + boolean showingForced = mShowInputForced; + boolean showingCandidates = mCandidatesVisibility == View.VISIBLE; initViews(); + mInputViewStarted = false; + mCandidatesViewStarted = false; + if (mInputStarted) { + doStartInput(getCurrentInputConnection(), + getCurrentInputEditorInfo(), true); + } if (visible) { - if (showingCandidates) { - setCandidatesViewShown(true); - } - if (showingInput) { + if (showingForced) { + // If we are showing the full soft keyboard, then go through + // this path to take care of current decisions about fullscreen + // etc. + onShowRequested(InputMethod.SHOW_FORCED|InputMethod.SHOW_EXPLICIT); + } else if (showingInput) { // If we are showing the full soft keyboard, then go through // this path to take care of current decisions about fullscreen // etc. @@ -507,6 +577,9 @@ public class InputMethodService extends AbstractInputMethodService { // Otherwise just put it back for its candidates. showWindow(false); } + if (showingCandidates) { + setCandidatesViewShown(true); + } } } @@ -568,6 +641,10 @@ public class InputMethodService extends AbstractInputMethodService { * the input method, or null if there is none. */ public InputConnection getCurrentInputConnection() { + InputConnection ic = mStartedInputConnection; + if (ic != null) { + return ic; + } return mInputConnection; } @@ -594,6 +671,7 @@ public class InputMethodService extends AbstractInputMethodService { changed = true; mIsFullscreen = isFullscreen; mFullscreenApplied = true; + initialize(); Drawable bg = onCreateBackgroundDrawable(); if (bg == null) { // We need to give the window a real drawable, so that it @@ -708,6 +786,7 @@ public class InputMethodService extends AbstractInputMethodService { mIsInputViewShown = isShown; mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE); if (mInputView == null) { + initialize(); View v = onCreateInputView(); if (v != null) { setInputView(v); @@ -716,13 +795,20 @@ public class InputMethodService extends AbstractInputMethodService { } } + /** + * Returns true if we have been asked to show our input view. + */ + public boolean isShowInputRequested() { + return mShowInputRequested; + } + /** * Return whether the soft input view is currently shown to the * user. This is the state that was last determined and * applied by {@link #updateInputViewShown()}. */ public boolean isInputViewShown() { - return mIsInputViewShown; + return mIsInputViewShown && mWindowVisible; } /** @@ -744,9 +830,10 @@ public class InputMethodService extends AbstractInputMethodService { * it is hidden. */ public void setCandidatesViewShown(boolean shown) { - if (mShowCandidatesRequested != shown) { - mCandidatesFrame.setVisibility(shown ? View.VISIBLE : View.INVISIBLE); - mShowCandidatesRequested = shown; + int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility(); + if (mCandidatesVisibility != vis) { + mCandidatesFrame.setVisibility(vis); + mCandidatesVisibility = vis; } if (!mShowInputRequested && mWindowVisible != shown) { // If we are being asked to show the candidates view while the app @@ -760,10 +847,24 @@ public class InputMethodService extends AbstractInputMethodService { } } + /** + * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE} + * or {@link View#GONE View.GONE}) of the candidates view when it is not + * shown. The default implementation returns GONE when in fullscreen mode, + * otherwise VISIBLE. Be careful if you change this to return GONE in + * other situations -- if showing or hiding the candidates view causes + * your window to resize, this can cause temporary drawing artifacts as + * the resize takes place. + */ + public int getCandidatesHiddenVisibility() { + return isFullscreenMode() ? View.GONE : View.INVISIBLE; + } + public void setStatusIcon(int iconResId) { mStatusIcon = iconResId; - if (mInputConnection != null && mWindowVisible) { - mInputConnection.showStatusIcon(getPackageName(), iconResId); + InputConnection ic = getCurrentInputConnection(); + if (ic != null && mWindowVisible) { + ic.showStatusIcon(getPackageName(), iconResId); } } @@ -783,11 +884,12 @@ public class InputMethodService extends AbstractInputMethodService { mExtractFrame.removeAllViews(); mExtractFrame.addView(view, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); + ViewGroup.LayoutParams.FILL_PARENT)); mExtractView = view; if (view != null) { mExtractEditText = (ExtractEditText)view.findViewById( com.android.internal.R.id.inputExtractEditText); + mExtractEditText.setIME(this); startExtractingText(); } else { mExtractEditText = null; @@ -889,6 +991,72 @@ public class InputMethodService extends AbstractInputMethodService { public void onStartInputView(EditorInfo info, boolean restarting) { } + /** + * Called when the input view is being hidden from the user. This will + * be called either prior to hiding the window, or prior to switching to + * another target for editing. + * + *

    The default + * implementation uses the InputConnection to clear any active composing + * text; you can override this (not calling the base class implementation) + * to perform whatever behavior you would like. + * + * @boolean finishingInput If true, {@link #onFinishInput} will be + * called immediately after. + */ + public void onFinishInputView(boolean finishingInput) { + if (!finishingInput) { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.finishComposingText(); + } + } + } + + /** + * Called when only the candidates view has been shown for showing + * processing as the user enters text through a hard keyboard. + * This will always be called after {@link #onStartInput}, + * allowing you to do your general setup there and just view-specific + * setup here. You are guaranteed that {@link #onCreateCandidatesView()} + * will have been called some time before this function is called. + * + *

    Note that this will not be called when the input method + * is running in full editing mode, and thus receiving + * {@link #onStartInputView} to initiate that operation. This is only + * for the case when candidates are being shown while the input method + * editor is hidden but wants to show its candidates UI as text is + * entered through some other mechanism. + * + * @param info Description of the type of text being edited. + * @param restarting Set to true if we are restarting input on the + * same text field as before. + */ + public void onStartCandidatesView(EditorInfo info, boolean restarting) { + } + + /** + * Called when the candidates view is being hidden from the user. This will + * be called either prior to hiding the window, or prior to switching to + * another target for editing. + * + *

    The default + * implementation uses the InputConnection to clear any active composing + * text; you can override this (not calling the base class implementation) + * to perform whatever behavior you would like. + * + * @boolean finishingInput If true, {@link #onFinishInput} will be + * called immediately after. + */ + public void onFinishCandidatesView(boolean finishingInput) { + if (!finishingInput) { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.finishComposingText(); + } + } + } + /** * The system has decided that it may be time to show your input method. * This is called due to a corresponding call to your @@ -905,10 +1073,22 @@ public class InputMethodService extends AbstractInputMethodService { if (!onEvaluateInputViewShown()) { return; } - if ((flags&InputMethod.SHOW_EXPLICIT) == 0 && onEvaluateFullscreenMode()) { - // Don't show if this is not explicit requested by the user and - // the input method is fullscreen. That would be too disruptive. - return; + if ((flags&InputMethod.SHOW_EXPLICIT) == 0) { + if (onEvaluateFullscreenMode()) { + // Don't show if this is not explicitly requested by the user and + // the input method is fullscreen. That would be too disruptive. + return; + } + Configuration config = getResources().getConfiguration(); + if (config.keyboard != Configuration.KEYBOARD_NOKEYS) { + // And if the device has a hard keyboard, even if it is + // currently hidden, don't show the input method implicitly. + // These kinds of devices don't need it that much. + return; + } + } + if ((flags&InputMethod.SHOW_FORCED) != 0) { + mShowInputForced = true; } showWindow(true); } @@ -922,6 +1102,7 @@ public class InputMethodService extends AbstractInputMethodService { + " mInputStarted=" + mInputStarted); boolean doShowInput = false; boolean wasVisible = mWindowVisible; + boolean wasCreated = mWindowCreated; mWindowVisible = true; if (!mShowInputRequested) { if (mInputStarted) { @@ -935,41 +1116,66 @@ public class InputMethodService extends AbstractInputMethodService { } if (DEBUG) Log.v(TAG, "showWindow: updating UI"); + initialize(); updateFullscreenMode(); updateInputViewShown(); if (!mWindowAdded || !mWindowCreated) { mWindowAdded = true; mWindowCreated = true; + initialize(); + if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView"); View v = onCreateCandidatesView(); if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v); if (v != null) { setCandidatesView(v); } } - if (doShowInput) { - if (mInputStarted) { - if (DEBUG) Log.v(TAG, "showWindow: starting input view"); + if (mShowInputRequested) { + if (!mInputViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); + mInputViewStarted = true; onStartInputView(mInputEditorInfo, false); } + } else if (!mCandidatesViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); + mCandidatesViewStarted = true; + onStartCandidatesView(mInputEditorInfo, false); + } + + if (doShowInput) { startExtractingText(); } if (!wasVisible) { if (DEBUG) Log.v(TAG, "showWindow: showing!"); mWindow.show(); - if (mInputConnection != null) { - mInputConnection.showStatusIcon(getPackageName(), mStatusIcon); + } + + if (!wasVisible || !wasCreated) { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.showStatusIcon(getPackageName(), mStatusIcon); } } } public void hideWindow() { + if (mInputViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); + onFinishInputView(false); + } else if (mCandidatesViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView"); + onFinishCandidatesView(false); + } + mInputViewStarted = false; + mCandidatesViewStarted = false; if (mWindowVisible) { mWindow.hide(); mWindowVisible = false; - if (mInputConnection != null) { - mInputConnection.hideStatusIcon(); + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.hideStatusIcon(); } } } @@ -1008,18 +1214,45 @@ public class InputMethodService extends AbstractInputMethodService { public void onStartInput(EditorInfo attribute, boolean restarting) { } - void doStartInput(EditorInfo attribute, boolean restarting) { - if (mInputStarted && !restarting) { + void doFinishInput() { + if (mInputViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); + onFinishInputView(true); + } else if (mCandidatesViewStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView"); + onFinishCandidatesView(true); + } + mInputViewStarted = false; + mCandidatesViewStarted = false; + if (mInputStarted) { + if (DEBUG) Log.v(TAG, "CALL: onFinishInput"); onFinishInput(); } + mInputStarted = false; + mStartedInputConnection = null; + } + + void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) { + if (!restarting) { + doFinishInput(); + } mInputStarted = true; + mStartedInputConnection = ic; mInputEditorInfo = attribute; + initialize(); + if (DEBUG) Log.v(TAG, "CALL: onStartInput"); onStartInput(attribute, restarting); if (mWindowVisible) { if (mShowInputRequested) { + if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); + mInputViewStarted = true; onStartInputView(mInputEditorInfo, restarting); + startExtractingText(); + } else if (mCandidatesVisibility == View.VISIBLE) { + if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); + mCandidatesViewStarted = true; + onStartCandidatesView(mInputEditorInfo, restarting); } - startExtractingText(); } } @@ -1029,8 +1262,17 @@ public class InputMethodService extends AbstractInputMethodService { * {@link #onStartInput(EditorInfo, boolean)} to perform input in a * new editor, or the input method may be left idle. This method is * not called when input restarts in the same editor. + * + *

    The default + * implementation uses the InputConnection to clear any active composing + * text; you can override this (not calling the base class implementation) + * to perform whatever behavior you would like. */ public void onFinishInput() { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.finishComposingText(); + } } /** @@ -1073,9 +1315,12 @@ public class InputMethodService extends AbstractInputMethodService { public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { - if (mExtractEditText != null && mExtractedText != null) { + final ExtractEditText eet = mExtractEditText; + if (eet != null && mExtractedText != null) { final int off = mExtractedText.startOffset; - mExtractEditText.setSelection(newSelStart-off, newSelEnd-off); + eet.startInternalChanges(); + eet.setSelection(newSelStart-off, newSelEnd-off); + eet.finishInternalChanges(); } } @@ -1100,6 +1345,18 @@ public class InputMethodService extends AbstractInputMethodService { .hideSoftInputFromInputMethod(mToken, flags); } + /** + * Override this to intercept key down events before they are processed by the + * application. If you return true, the application will not itself + * process the event. If you return true, the normal application processing + * will occur as if the IME had not seen the event at all. + * + *

    The default implementation intercepts {@link KeyEvent#KEYCODE_BACK + * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In + * additional, in fullscreen mode only, it will consume DPAD movement + * events to move the cursor in the extracted text view, not allowing + * them to perform navigation in the underlying application. + */ public boolean onKeyDown(int keyCode, KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { @@ -1108,25 +1365,54 @@ public class InputMethodService extends AbstractInputMethodService { // consume the back key. dismissSoftInput(0); return true; - } - if (mShowCandidatesRequested) { - // If the candidates are shown, we just want to make sure - // they are now hidden but otherwise let the app execute - // the back. - // XXX this needs better interaction with the soft input - // implementation. - //setCandidatesViewShown(false); + } else if (mWindowVisible) { + if (mCandidatesVisibility == View.VISIBLE) { + // If we are showing candidates even if no input area, then + // hide them. + setCandidatesViewShown(false); + return true; + } else { + // If we have the window visible for some other reason -- + // most likely to show candidates -- then just get rid + // of it. This really shouldn't happen, but just in case... + hideWindow(); + return true; + } } } - return false; + + return doMovementKey(keyCode, event, MOVEMENT_DOWN); } + /** + * Override this to intercept special key multiple events before they are + * processed by the + * application. If you return true, the application will not itself + * process the event. If you return true, the normal application processing + * will occur as if the IME had not seen the event at all. + * + *

    The default implementation always returns false, except when + * in fullscreen mode, where it will consume DPAD movement + * events to move the cursor in the extracted text view, not allowing + * them to perform navigation in the underlying application. + */ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { - return false; + return doMovementKey(keyCode, event, count); } + /** + * Override this to intercept key up events before they are processed by the + * application. If you return true, the application will not itself + * process the event. If you return true, the normal application processing + * will occur as if the IME had not seen the event at all. + * + *

    The default implementation always returns false, except when + * in fullscreen mode, where it will consume DPAD movement + * events to move the cursor in the extracted text view, not allowing + * them to perform navigation in the underlying application. + */ public boolean onKeyUp(int keyCode, KeyEvent event) { - return false; + return doMovementKey(keyCode, event, MOVEMENT_UP); } public boolean onTrackballEvent(MotionEvent event) { @@ -1136,21 +1422,176 @@ public class InputMethodService extends AbstractInputMethodService { public void onAppPrivateCommand(String action, Bundle data) { } + static final int MOVEMENT_DOWN = -1; + static final int MOVEMENT_UP = -2; + + boolean doMovementKey(int keyCode, KeyEvent event, int count) { + final ExtractEditText eet = mExtractEditText; + if (isFullscreenMode() && isInputViewShown() && eet != null) { + // If we are in fullscreen mode, the cursor will move around + // the extract edit text, but should NOT cause focus to move + // to other fields. + MovementMethod movement = eet.getMovementMethod(); + Layout layout = eet.getLayout(); + if (movement != null && layout != null) { + // We want our own movement method to handle the key, so the + // cursor will properly move in our own word wrapping. + if (count == MOVEMENT_DOWN) { + if (movement.onKeyDown(eet, + (Spannable)eet.getText(), keyCode, event)) { + return true; + } + } else if (count == MOVEMENT_UP) { + if (movement.onKeyUp(eet, + (Spannable)eet.getText(), keyCode, event)) { + return true; + } + } else { + KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN); + if (movement.onKeyDown(eet, + (Spannable)eet.getText(), keyCode, down)) { + KeyEvent up = new KeyEvent(event, KeyEvent.ACTION_UP); + movement.onKeyUp(eet, + (Spannable)eet.getText(), keyCode, up); + while (--count > 0) { + movement.onKeyDown(eet, + (Spannable)eet.getText(), keyCode, down); + movement.onKeyUp(eet, + (Spannable)eet.getText(), keyCode, up); + } + } + } + } + // Regardless of whether the movement method handled the key, + // we never allow DPAD navigation to the application. + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_DPAD_RIGHT: + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_DPAD_DOWN: + return true; + } + } + + return false; + } + + /** + * This is called when the user has moved the cursor in the extracted + * text view, when running in fullsreen mode. The default implementation + * performs the corresponding selection change on the underlying text + * editor. + */ + public void onExtractedSelectionChanged(int start, int end) { + InputConnection conn = getCurrentInputConnection(); + if (conn != null) { + conn.setSelection(start, end); + } + } + + /** + * This is called when the user has clicked on the extracted text view, + * when running in fullscreen mode. The default implementation hides + * the candidates view when this happens. Re-implement this to provide + * whatever behavior you want. + */ + public void onExtractedTextClicked() { + setCandidatesViewShown(false); + } + + /** + * This is called when the user has selected a context menu item from the + * extracted text view, when running in fullscreen mode. The default + * implementation sends this action to the current InputConnection's + * {@link InputConnection#performContextMenuAction(int)}, for it + * to be processed in underlying "real" editor. Re-implement this to + * provide whatever behavior you want. + */ + public boolean onExtractTextContextMenuItem(int id) { + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.performContextMenuAction(id); + } + return true; + } + void startExtractingText() { - if (mExtractEditText != null && getCurrentInputStarted() + final ExtractEditText eet = mExtractEditText; + if (eet != null && getCurrentInputStarted() && isFullscreenMode()) { mExtractedToken++; ExtractedTextRequest req = new ExtractedTextRequest(); req.token = mExtractedToken; + req.flags = InputConnection.GET_TEXT_WITH_STYLES; req.hintMaxLines = 10; req.hintMaxChars = 10000; - mExtractedText = mInputConnection.getExtractedText(req, - InputConnection.EXTRACTED_TEXT_MONITOR); - if (mExtractedText != null) { - mExtractEditText.setExtractedText(mExtractedText); + mExtractedText = getCurrentInputConnection().getExtractedText(req, + InputConnection.GET_EXTRACTED_TEXT_MONITOR); + try { + eet.startInternalChanges(); + int inputType = getCurrentInputEditorInfo().inputType; + if ((inputType&EditorInfo.TYPE_MASK_CLASS) + == EditorInfo.TYPE_CLASS_TEXT) { + if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) { + inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; + } + } + eet.setInputType(inputType); + eet.setHint(mInputEditorInfo.hintText); + if (mExtractedText != null) { + eet.setExtractedText(mExtractedText); + } + } finally { + eet.finishInternalChanges(); } - mExtractEditText.setInputType(getCurrentInputEditorInfo().inputType); - mExtractEditText.setHint(mInputEditorInfo.hintText); } } + + /** + * Performs a dump of the InputMethodService's internal state. Override + * to add your own information to the dump. + */ + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + final Printer p = new PrintWriterPrinter(fout); + p.println("Input method service state for " + this + ":"); + p.println(" mWindowCreated=" + mWindowCreated + + " mWindowAdded=" + mWindowAdded + + " mWindowVisible=" + mWindowVisible); + p.println(" Configuration=" + getResources().getConfiguration()); + p.println(" mToken=" + mToken); + p.println(" mInputBinding=" + mInputBinding); + p.println(" mInputConnection=" + mInputConnection); + p.println(" mStartedInputConnection=" + mStartedInputConnection); + p.println(" mInputStarted=" + mInputStarted + + " mInputViewStarted=" + mInputViewStarted + + " mCandidatesViewStarted=" + mCandidatesViewStarted); + + if (mInputEditorInfo != null) { + p.println(" mInputEditorInfo:"); + mInputEditorInfo.dump(p, " "); + } else { + p.println(" mInputEditorInfo: null"); + } + + p.println(" mShowInputRequested=" + mShowInputRequested + + " mLastShowInputRequested=" + mLastShowInputRequested + + " mShowInputForced=" + mShowInputForced); + p.println(" mCandidatesVisibility=" + mCandidatesVisibility + + " mFullscreenApplied=" + mFullscreenApplied + + " mIsFullscreen=" + mIsFullscreen); + + if (mExtractedText != null) { + p.println(" mExtractedText:"); + p.println(" text=" + mExtractedText.text.length() + " chars" + + " startOffset=" + mExtractedText.startOffset); + p.println(" selectionStart=" + mExtractedText.selectionStart + + " selectionEnd=" + mExtractedText.selectionEnd + + " flags=0x" + Integer.toHexString(mExtractedText.flags)); + } else { + p.println(" mExtractedText: null"); + } + p.println(" mExtractedToken=" + mExtractedToken); + p.println(" mIsInputViewShown=" + mIsInputViewShown + + " mStatusIcon=" + mStatusIcon); + } } diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java index cfd3188bec4df..228acbee97e0f 100755 --- a/core/java/android/inputmethodservice/Keyboard.java +++ b/core/java/android/inputmethodservice/Keyboard.java @@ -177,13 +177,13 @@ public class Keyboard { parent.mDisplayWidth, parent.mDefaultWidth); defaultHeight = getDimensionOrFraction(a, com.android.internal.R.styleable.Keyboard_keyHeight, - parent.mDisplayWidth, parent.mDefaultHeight); - defaultHorizontalGap = getDimensionOrFraction(a, + parent.mDisplayHeight, parent.mDefaultHeight); + defaultHorizontalGap = getDimensionOrFraction(a, com.android.internal.R.styleable.Keyboard_horizontalGap, parent.mDisplayWidth, parent.mDefaultHorizontalGap); verticalGap = getDimensionOrFraction(a, com.android.internal.R.styleable.Keyboard_verticalGap, - parent.mDisplayWidth, parent.mDefaultVerticalGap); + parent.mDisplayHeight, parent.mDefaultVerticalGap); a.recycle(); a = res.obtainAttributes(Xml.asAttributeSet(parser), com.android.internal.R.styleable.Keyboard_Row); @@ -540,7 +540,6 @@ public class Keyboard { row.defaultHorizontalGap = mDefaultHorizontalGap; row.verticalGap = mDefaultVerticalGap; row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM; - final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns; for (int i = 0; i < characters.length(); i++) { char c = characters.charAt(i); diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 2f3b54bee7161..b2c74f24bf00e 100755 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -149,6 +149,7 @@ public class KeyboardView extends View implements View.OnClickListener { private int mMiniKeyboardOffsetY; private Map mMiniKeyboardCache; private int[] mWindowOffset; + private Key[] mKeys; /** Listener for {@link OnKeyboardActionListener}. */ private OnKeyboardActionListener mKeyboardActionListener; @@ -163,7 +164,7 @@ public class KeyboardView extends View implements View.OnClickListener { private boolean mPreviewCentered = false; private boolean mShowPreview = true; - private boolean mShowTouchPoints = false; + private boolean mShowTouchPoints = true; private int mPopupPreviewX; private int mPopupPreviewY; @@ -334,7 +335,7 @@ public class KeyboardView extends View implements View.OnClickListener { } private void initGestureDetector() { - mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { + mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { @@ -386,6 +387,8 @@ public class KeyboardView extends View implements View.OnClickListener { showPreview(NOT_A_KEY); } mKeyboard = keyboard; + List keys = mKeyboard.getKeys(); + mKeys = keys.toArray(new Key[keys.size()]); requestLayout(); invalidate(); computeProximityThreshold(keyboard); @@ -521,16 +524,16 @@ public class KeyboardView extends View implements View.OnClickListener { */ private void computeProximityThreshold(Keyboard keyboard) { if (keyboard == null) return; - List keys = keyboard.getKeys(); + final Key[] keys = mKeys; if (keys == null) return; - int length = keys.size(); + int length = keys.length; int dimensionSum = 0; for (int i = 0; i < length; i++) { - Key key = keys.get(i); - dimensionSum += key.width + key.gap + key.height; + Key key = keys[i]; + dimensionSum += Math.min(key.width, key.height) + key.gap; } if (dimensionSum < 0 || length == 0) return; - mProximityThreshold = dimensionSum / (length * 2); + mProximityThreshold = (int) (dimensionSum * 1.5f / length); mProximityThreshold *= mProximityThreshold; // Square it } @@ -545,7 +548,7 @@ public class KeyboardView extends View implements View.OnClickListener { final Rect padding = mPadding; final int kbdPaddingLeft = mPaddingLeft; final int kbdPaddingTop = mPaddingTop; - final List keys = mKeyboard.getKeys(); + final Key[] keys = mKeys; final Key invalidKey = mInvalidatedKey; //canvas.translate(0, mKeyboardPaddingTop); paint.setAlpha(255); @@ -565,9 +568,9 @@ public class KeyboardView extends View implements View.OnClickListener { drawSingleKey = true; } } - final int keyCount = keys.size(); + final int keyCount = keys.length; for (int i = 0; i < keyCount; i++) { - final Key key = keys.get(i); + final Key key = keys[i]; if (drawSingleKey && invalidKey != key) { continue; } @@ -638,15 +641,15 @@ public class KeyboardView extends View implements View.OnClickListener { } private int getKeyIndices(int x, int y, int[] allKeys) { - final List keys = mKeyboard.getKeys(); + final Key[] keys = mKeys; final boolean shifted = mKeyboard.isShifted(); int primaryIndex = NOT_A_KEY; int closestKey = NOT_A_KEY; int closestKeyDist = mProximityThreshold + 1; java.util.Arrays.fill(mDistances, Integer.MAX_VALUE); - final int keyCount = keys.size(); + final int keyCount = keys.length; for (int i = 0; i < keyCount; i++) { - final Key key = keys.get(i); + final Key key = keys[i]; int dist = 0; boolean isInside = key.isInside(x,y); if (((mProximityCorrectOn @@ -694,7 +697,7 @@ public class KeyboardView extends View implements View.OnClickListener { private void detectAndSendKey(int x, int y, long eventTime) { int index = mCurrentKey; if (index != NOT_A_KEY) { - final Key key = mKeyboard.getKeys().get(index); + final Key key = mKeys[index]; if (key.text != null) { for (int i = 0; i < key.text.length(); i++) { mKeyboardActionListener.onKey(key.text.charAt(i), key.codes); @@ -743,14 +746,14 @@ public class KeyboardView extends View implements View.OnClickListener { mCurrentKeyIndex = keyIndex; // Release the old key and press the new key - final List keys = mKeyboard.getKeys(); + final Key[] keys = mKeys; if (oldKeyIndex != mCurrentKeyIndex) { - if (oldKeyIndex != NOT_A_KEY && keys.size() > oldKeyIndex) { - keys.get(oldKeyIndex).onReleased(mCurrentKeyIndex == NOT_A_KEY); + if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) { + keys[oldKeyIndex].onReleased(mCurrentKeyIndex == NOT_A_KEY); invalidateKey(oldKeyIndex); } - if (mCurrentKeyIndex != NOT_A_KEY && keys.size() > mCurrentKeyIndex) { - keys.get(mCurrentKeyIndex).onPressed(); + if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) { + keys[mCurrentKeyIndex].onPressed(); invalidateKey(mCurrentKeyIndex); } } @@ -764,7 +767,7 @@ public class KeyboardView extends View implements View.OnClickListener { } } if (keyIndex != NOT_A_KEY) { - Key key = keys.get(keyIndex); + Key key = keys[keyIndex]; if (key.icon != null) { mPreviewText.setCompoundDrawables(null, null, null, key.iconPreview != null ? key.iconPreview : key.icon); @@ -774,8 +777,10 @@ public class KeyboardView extends View implements View.OnClickListener { mPreviewText.setText(getPreviewText(key)); if (key.label.length() > 1 && key.codes.length < 2) { mPreviewText.setTextSize(mLabelTextSize); + mPreviewText.setTypeface(Typeface.DEFAULT_BOLD); } else { mPreviewText.setTextSize(mPreviewTextSizeLarge); + mPreviewText.setTypeface(Typeface.DEFAULT); } } mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), @@ -788,8 +793,6 @@ public class KeyboardView extends View implements View.OnClickListener { lp.width = popupWidth; lp.height = popupHeight; } - previewPopup.setWidth(popupWidth); - previewPopup.setHeight(popupHeight); if (!mPreviewCentered) { mPopupPreviewX = key.x - mPreviewText.getPaddingLeft() + mPaddingLeft; mPopupPreviewY = key.y - popupHeight + mPreviewOffset; @@ -809,25 +812,27 @@ public class KeyboardView extends View implements View.OnClickListener { mPreviewText.getBackground().setState( key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); if (previewPopup.isShowing()) { - previewPopup.update(mPopupParent, mPopupPreviewX + mOffsetInWindow[0], + previewPopup.update(mPopupPreviewX + mOffsetInWindow[0], mPopupPreviewY + mOffsetInWindow[1], popupWidth, popupHeight); } else { + previewPopup.setWidth(popupWidth); + previewPopup.setHeight(popupHeight); previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY, mPopupPreviewX + mOffsetInWindow[0], mPopupPreviewY + mOffsetInWindow[1]); } - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0), + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0), ViewConfiguration.getTapTimeout()); } } } private void invalidateKey(int keyIndex) { - if (keyIndex < 0 || keyIndex >= mKeyboard.getKeys().size()) { + if (keyIndex < 0 || keyIndex >= mKeys.length) { return; } - final Key key = mKeyboard.getKeys().get(keyIndex); + final Key key = mKeys[keyIndex]; mInvalidatedKey = key; invalidate(key.x + mPaddingLeft, key.y + mPaddingTop, key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop); @@ -838,11 +843,11 @@ public class KeyboardView extends View implements View.OnClickListener { if (mPopupLayout == 0) { return false; } - if (mCurrentKey < 0 || mCurrentKey >= mKeyboard.getKeys().size()) { + if (mCurrentKey < 0 || mCurrentKey >= mKeys.length) { return false; } - Key popupKey = mKeyboard.getKeys().get(mCurrentKey); + Key popupKey = mKeys[mCurrentKey]; boolean result = onLongPress(popupKey); if (result) { mAbortKey = true; @@ -968,8 +973,8 @@ public class KeyboardView extends View implements View.OnClickListener { mLastMoveTime = mDownTime; checkMultiTap(eventTime, keyIndex); mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ? - mKeyboard.getKeys().get(keyIndex).codes[0] : 0); - if (mCurrentKey >= 0 && mKeyboard.getKeys().get(mCurrentKey).repeatable) { + mKeys[keyIndex].codes[0] : 0); + if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) { mRepeatKeyIndex = mCurrentKey; repeatKey(); Message msg = mHandler.obtainMessage(MSG_REPEAT); @@ -1054,7 +1059,7 @@ public class KeyboardView extends View implements View.OnClickListener { } private boolean repeatKey() { - Key key = mKeyboard.getKeys().get(mRepeatKeyIndex); + Key key = mKeys[mRepeatKeyIndex]; detectAndSendKey(key.x, key.y, mLastTapTime); return true; } @@ -1113,7 +1118,7 @@ public class KeyboardView extends View implements View.OnClickListener { private void checkMultiTap(long eventTime, int keyIndex) { if (keyIndex == NOT_A_KEY) return; - Key key = mKeyboard.getKeys().get(keyIndex); + Key key = mKeys[keyIndex]; if (key.codes.length > 1) { mInMultiTap = true; if (eventTime < mLastTapTime + MULTITAP_INTERVAL diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index 9ff1665988dad..da67c6d168a6e 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -18,6 +18,7 @@ package android.inputmethodservice; import android.app.Dialog; import android.content.Context; +import android.content.pm.ActivityInfo; import android.os.IBinder; import android.view.Gravity; import android.view.WindowManager; @@ -139,6 +140,8 @@ class SoftInputWindow extends Dialog { lp.gravity = Gravity.BOTTOM; lp.width = -1; + // Let the input method window's orientation follow sensor based rotation + lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; getWindow().setAttributes(lp); getWindow().setFlags( diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 213813a81493d..1429bc1aff4d1 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -16,6 +16,8 @@ package android.net; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.os.RemoteException; /** @@ -100,6 +102,18 @@ public class ConnectivityManager */ public static final String EXTRA_EXTRA_INFO = "extraInfo"; + /** + * Broadcast Action: The setting for background data usage has changed + * values. Use {@link #getBackgroundDataSetting()} to get the current value. + *

    + * If an application uses the network in the background, it should listen + * for this broadcast and stop using the background data if the value is + * false. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = + "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; + public static final int TYPE_MOBILE = 0; public static final int TYPE_WIFI = 1; @@ -223,6 +237,43 @@ public class ConnectivityManager } } + /** + * Returns the value of the setting for background data usage. If false, + * applications should not use the network if the application is not in the + * foreground. Developers should respect this setting, and check the value + * of this before performing any background data operations. + *

    + * All applications that have background services that use the network + * should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}. + * + * @return Whether background data usage is allowed. + */ + public boolean getBackgroundDataSetting() { + try { + return mService.getBackgroundDataSetting(); + } catch (RemoteException e) { + // Err on the side of safety + return false; + } + } + + /** + * Sets the value of the setting for background data usage. + * + * @param allowBackgroundData Whether an application should use data while + * it is in the background. + * + * @attr ref android.Manifest.permission#CHANGE_BACKGROUND_DATA_SETTING + * @see #getBackgroundDataSetting() + * @hide + */ + public void setBackgroundDataSetting(boolean allowBackgroundData) { + try { + mService.setBackgroundDataSetting(allowBackgroundData); + } catch (RemoteException e) { + } + } + /** * Don't allow use of default constructor. */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index e1d921f4d9a0c..de6859809d82e 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -44,4 +44,8 @@ interface IConnectivityManager int stopUsingNetworkFeature(int networkType, in String feature); boolean requestRouteToHost(int networkType, int hostAddress); + + boolean getBackgroundDataSetting(); + + void setBackgroundDataSetting(boolean allowBackgroundData); } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 32a26e42d6157..c23df217a856d 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -2235,12 +2235,13 @@ public abstract class Uri implements Parcelable, Comparable { } /** - * Creates a new Uri by encoding and appending a path segment to a base Uri. + * Creates a new Uri by appending an already-encoded path segment to a + * base Uri. * * @param baseUri Uri to append path segment to - * @param pathSegment to encode and append - * @return a new Uri based on baseUri with the given segment encoded and - * appended to the path + * @param pathSegment encoded path segment to append + * @return a new Uri based on baseUri with the given segment appended to + * the path * @throws NullPointerException if baseUri is null */ public static Uri withAppendedPath(Uri baseUri, String pathSegment) { diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java index 01442aecaac13..4fb14991d9fd5 100644 --- a/core/java/android/net/http/AndroidHttpClient.java +++ b/core/java/android/net/http/AndroidHttpClient.java @@ -26,7 +26,6 @@ import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.entity.AbstractHttpEntity; import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.ClientProtocolException; @@ -56,7 +55,6 @@ import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.net.URI; -import java.util.concurrent.atomic.AtomicInteger; import android.util.Log; import android.content.ContentResolver; @@ -346,6 +344,13 @@ public final class AndroidHttpClient implements HttpClient { return Log.isLoggable(tag, level); } + /** + * Returns true if auth logging is turned on for this configuration. + */ + private boolean isAuthLoggable() { + return Log.isLoggable(tag + "-auth", level); + } + /** * Prints a message using this configuration. */ @@ -392,7 +397,8 @@ public final class AndroidHttpClient implements HttpClient { if (configuration != null && configuration.isLoggable() && request instanceof HttpUriRequest) { - configuration.println(toCurl((HttpUriRequest) request)); + configuration.println(toCurl((HttpUriRequest) request, + configuration.isAuthLoggable())); } } } @@ -400,12 +406,17 @@ public final class AndroidHttpClient implements HttpClient { /** * Generates a cURL command equivalent to the given request. */ - private static String toCurl(HttpUriRequest request) throws IOException { + private static String toCurl(HttpUriRequest request, boolean logAuthToken) throws IOException { StringBuilder builder = new StringBuilder(); builder.append("curl "); for (Header header: request.getAllHeaders()) { + if (!logAuthToken + && (header.getName().equals("Authorization") || + header.getName().equals("Cookie"))) { + continue; + } builder.append("--header \""); builder.append(header.toString().trim()); builder.append("\" "); diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java index 65e6117d2de0e..c4ee5b0da0e2b 100644 --- a/core/java/android/net/http/RequestHandle.java +++ b/core/java/android/net/http/RequestHandle.java @@ -55,7 +55,7 @@ public class RequestHandle { private final static String AUTHORIZATION_HEADER = "Authorization"; private final static String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"; - private final static int MAX_REDIRECT_COUNT = 16; + public final static int MAX_REDIRECT_COUNT = 16; /** * Creates a new request session. @@ -106,6 +106,14 @@ public class RequestHandle { return mRedirectCount >= MAX_REDIRECT_COUNT; } + public int getRedirectCount() { + return mRedirectCount; + } + + public void setRedirectCount(int count) { + mRedirectCount = count; + } + /** * Create and queue a redirect request. * diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index ed7c3663ce285..017b14da98a0e 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -15,19 +15,26 @@ import android.util.SparseArray; public abstract class BatteryStats { /** - * A constant indicating a partial wake lock. + * A constant indicating a partial wake lock timer. */ public static final int WAKE_TYPE_PARTIAL = 0; /** - * A constant indicating a full wake lock. + * A constant indicating a full wake lock timer. */ public static final int WAKE_TYPE_FULL = 1; /** - * A constant indicating a window wake lock. + * A constant indicating a window wake lock timer. */ public static final int WAKE_TYPE_WINDOW = 2; + + /** + * A constant indicating a sensor timer. + * + * {@hide} + */ + public static final int SENSOR = 3; /** * Include all of the data in the stats, including previously saved data. @@ -48,6 +55,21 @@ public abstract class BatteryStats { * Include only the run since the last time the device was unplugged in the stats. */ public static final int STATS_UNPLUGGED = 3; + + /** + * Bump the version on this if the checkin format changes. + */ + private static final int BATTERY_STATS_CHECKIN_VERSION = 1; + + // TODO: Update this list if you add/change any stats above. + private static final String[] STAT_NAMES = { "total", "last", "current", "unplugged" }; + + private static final String APK_DATA = "apk"; + private static final String PROCESS_DATA = "process"; + private static final String SENSOR_DATA = "sensor"; + private static final String WAKELOCK_DATA = "wakelock"; + private static final String NETWORK_DATA = "network"; + private static final String BATTERY_DATA = "battery"; private final StringBuilder mFormatBuilder = new StringBuilder(8); private final Formatter mFormatter = new Formatter(mFormatBuilder); @@ -115,8 +137,28 @@ public abstract class BatteryStats { * @return a Map from Strings to Uid.Pkg objects. */ public abstract Map getPackageStats(); + + /** + * {@hide} + */ + public abstract int getUid(); + + /** + * {@hide} + */ + public abstract long getTcpBytesReceived(int which); + + /** + * {@hide} + */ + public abstract long getTcpBytesSent(int which); public static abstract class Sensor { + /** + * {@hide} + */ + public abstract String getName(); + public abstract Timer getSensorTime(); } @@ -200,6 +242,22 @@ public abstract class BatteryStats { * Returns the number of times the device has been started. */ public abstract int getStartCount(); + + /** + * Returns the time in milliseconds that the screen has been on while the device was + * running on battery. + * + * {@hide} + */ + public abstract long getBatteryScreenOnTime(); + + /** + * Returns the time in milliseconds that the screen has been on while the device was + * plugged in. + * + * {@hide} + */ + public abstract long getPluggedScreenOnTime(); /** * Returns a SparseArray containing the statistics for each uid. @@ -318,11 +376,14 @@ public abstract class BatteryStats { * @param linePrefix a String to be prepended to each line of output. * @return the line prefix */ - private final String printWakeLock(StringBuilder sb, Timer timer, long now, + private static final String printWakeLock(StringBuilder sb, Timer timer, long now, String name, int which, String linePrefix) { + if (timer != null) { // Convert from microseconds to milliseconds with rounding - long totalTimeMillis = (timer.getTotalTime(now, which) + 500) / 1000; + long totalTimeMicros = timer.getTotalTime(now, which); + long totalTimeMillis = (totalTimeMicros + 500) / 1000; + int count = timer.getCount(which); if (totalTimeMillis != 0) { sb.append(linePrefix); @@ -337,6 +398,184 @@ public abstract class BatteryStats { } return linePrefix; } + + /** + * Checkin version of wakelock printer. Prints simple comma-separated list. + * + * @param sb a StringBuilder object. + * @param timer a Timer object contining the wakelock times. + * @param now the current time in microseconds. + * @param name the name of the wakelock. + * @param which which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT. + * @param linePrefix a String to be prepended to each line of output. + * @return the line prefix + */ + private static final String printWakeLockCheckin(StringBuilder sb, Timer timer, long now, + String name, int which, String linePrefix) { + long totalTimeMicros = 0; + int count = 0; + if (timer != null) { + totalTimeMicros = timer.getTotalTime(now, which); + count = timer.getCount(which); + } + sb.append(linePrefix); + sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding + sb.append(','); + sb.append(name); + sb.append(','); + sb.append(count); + return ","; + } + + /** + * Dump a comma-separated line of values for terse checkin mode. + * + * @param pw the PageWriter to dump log to + * @param category category of data (e.g. "total", "last", "unplugged", "current" ) + * @param type type of data (e.g. "wakelock", "sensor", "process", "apk" , "process", "network") + * @param args type-dependent data arguments + */ + private static final void dumpLine(PrintWriter pw, int uid, String category, String type, + Object... args ) { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(uid); pw.print(','); + pw.print(category); pw.print(','); + pw.print(type); + + for (Object arg : args) { + pw.print(','); + pw.print(arg); + } + pw.print('\n'); + } + + /** + * Checkin server version of dump to produce more compact, computer-readable log. + * + * NOTE: all times are expressed in 'ms'. + * @param fd + * @param pw + * @param which + */ + private final void dumpCheckinLocked(FileDescriptor fd, PrintWriter pw, int which) { + long uSecTime = SystemClock.elapsedRealtime() * 1000; + final long uSecNow = getBatteryUptime(uSecTime); + + StringBuilder sb = new StringBuilder(128); + long batteryUptime = computeBatteryUptime(uSecNow, which); + long batteryRealtime = computeBatteryRealtime(getBatteryRealtime(uSecTime), which); + long elapsedRealtime = computeRealtime(uSecTime, which); + long uptime = computeUptime(SystemClock.uptimeMillis() * 1000, which); + + String category = STAT_NAMES[which]; + + // Dump "battery" stat + dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, + which == STATS_TOTAL ? getStartCount() : "N/A", + batteryUptime / 1000, + formatRatioLocked(batteryUptime, elapsedRealtime), + batteryRealtime / 1000, + formatRatioLocked(batteryRealtime, elapsedRealtime), + uptime / 1000, + elapsedRealtime / 1000); + + SparseArray uidStats = getUidStats(); + final int NU = uidStats.size(); + for (int iu = 0; iu < NU; iu++) { + final int uid = uidStats.keyAt(iu); + Uid u = uidStats.valueAt(iu); + // Dump Network stats per uid, if any + long rx = u.getTcpBytesReceived(which); + long tx = u.getTcpBytesSent(which); + if (rx > 0 || tx > 0) dumpLine(pw, uid, category, NETWORK_DATA, rx, tx); + + Map wakelocks = u.getWakelockStats(); + if (wakelocks.size() > 0) { + for (Map.Entry ent + : wakelocks.entrySet()) { + Uid.Wakelock wl = ent.getValue(); + String linePrefix = ""; + sb.setLength(0); + linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL), uSecNow, + "full", which, linePrefix); + linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), uSecNow, + "partial", which, linePrefix); + linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), uSecNow, + "window", which, linePrefix); + + // Only log if we had at lease one wakelock... + if (sb.length() > 0) { + dumpLine(pw, uid, category, WAKELOCK_DATA, ent.getKey(), sb.toString()); + } + } + } + + Map sensors = u.getSensorStats(); + if (sensors.size() > 0) { + for (Map.Entry ent + : sensors.entrySet()) { + Uid.Sensor se = ent.getValue(); + int sensorNumber = ent.getKey(); + Timer timer = se.getSensorTime(); + if (timer != null) { + // Convert from microseconds to milliseconds with rounding + long totalTime = (timer.getTotalTime(uSecNow, which) + 500) / 1000; + int count = timer.getCount(which); + if (totalTime != 0) { + dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count); + } + } + } + } + + Map processStats = u.getProcessStats(); + if (processStats.size() > 0) { + for (Map.Entry ent + : processStats.entrySet()) { + Uid.Proc ps = ent.getValue(); + + long userTime = ps.getUserTime(which); + long systemTime = ps.getSystemTime(which); + int starts = ps.getStarts(which); + + if (userTime != 0 || systemTime != 0 || starts != 0) { + dumpLine(pw, uid, category, PROCESS_DATA, + ent.getKey(), // proc + userTime * 10, // cpu time in ms + systemTime * 10, // user time in ms + starts); // process starts + } + } + } + + Map packageStats = u.getPackageStats(); + if (packageStats.size() > 0) { + for (Map.Entry ent + : packageStats.entrySet()) { + + Uid.Pkg ps = ent.getValue(); + int wakeups = ps.getWakeups(which); + Map serviceStats = ps.getServiceStats(); + for (Map.Entry sent + : serviceStats.entrySet()) { + BatteryStats.Uid.Pkg.Serv ss = sent.getValue(); + long startTime = ss.getStartTime(uSecNow, which); + int starts = ss.getStarts(which); + int launches = ss.getLaunches(which); + if (startTime != 0 || starts != 0 || launches != 0) { + dumpLine(pw, uid, category, APK_DATA, + wakeups, // wakeup alarms + ent.getKey(), // Apk + sent.getKey(), // service + startTime / 1000, // time spent started, in ms + starts, + launches); + } + } + } + } + } + } @SuppressWarnings("unused") private final void dumpLocked(FileDescriptor fd, PrintWriter pw, String prefix, int which) { @@ -344,13 +583,22 @@ public abstract class BatteryStats { final long uSecNow = getBatteryUptime(uSecTime); StringBuilder sb = new StringBuilder(128); - if (which == STATS_TOTAL) { - pw.println(prefix + "Current and Historic Battery Usage Statistics:"); - pw.println(prefix + " System starts: " + getStartCount()); - } else if (which == STATS_LAST) { - pw.println(prefix + "Last Battery Usage Statistics:"); - } else { - pw.println(prefix + "Current Battery Usage Statistics:"); + switch (which) { + case STATS_TOTAL: + pw.println(prefix + "Current and Historic Battery Usage Statistics:"); + pw.println(prefix + " System starts: " + getStartCount()); + break; + case STATS_LAST: + pw.println(prefix + "Last Battery Usage Statistics:"); + break; + case STATS_UNPLUGGED: + pw.println(prefix + "Last Unplugged Battery Usage Statistics:"); + break; + case STATS_CURRENT: + pw.println(prefix + "Current Battery Usage Statistics:"); + break; + default: + throw new IllegalArgumentException("which = " + which); } long batteryUptime = computeBatteryUptime(uSecNow, which); long batteryRealtime = computeBatteryRealtime(getBatteryRealtime(uSecTime), which); @@ -359,7 +607,7 @@ public abstract class BatteryStats { pw.println(prefix + " On battery: " + formatTimeMs(batteryUptime / 1000) + "(" - + formatRatioLocked(batteryUptime, batteryRealtime) + + formatRatioLocked(batteryUptime, elapsedRealtime) + ") uptime, " + formatTimeMs(batteryRealtime / 1000) + "(" + formatRatioLocked(batteryRealtime, elapsedRealtime) @@ -380,6 +628,9 @@ public abstract class BatteryStats { Uid u = uidStats.valueAt(iu); pw.println(prefix + " #" + uid + ":"); boolean uidActivity = false; + + pw.println(prefix + " Network: " + u.getTcpBytesReceived(which) + " bytes received, " + + u.getTcpBytesSent(which) + " bytes sent"); Map wakelocks = u.getWakelockStats(); if (wakelocks.size() > 0) { @@ -512,12 +763,30 @@ public abstract class BatteryStats { */ @SuppressWarnings("unused") public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { + boolean isCheckin = false; + if (args != null) { + for (String arg : args) { + if ("-c".equals(arg)) { + isCheckin = true; + break; + } + } + } synchronized (this) { - dumpLocked(fd, pw, "", STATS_TOTAL); - pw.println(""); - dumpLocked(fd, pw, "", STATS_LAST); - pw.println(""); - dumpLocked(fd, pw, "", STATS_CURRENT); + if (isCheckin) { + dumpCheckinLocked(fd, pw, STATS_TOTAL); + dumpCheckinLocked(fd, pw, STATS_LAST); + dumpCheckinLocked(fd, pw, STATS_UNPLUGGED); + dumpCheckinLocked(fd, pw, STATS_CURRENT); + } else { + dumpLocked(fd, pw, "", STATS_TOTAL); + pw.println(""); + dumpLocked(fd, pw, "", STATS_LAST); + pw.println(""); + dumpLocked(fd, pw, "", STATS_UNPLUGGED); + pw.println(""); + dumpLocked(fd, pw, "", STATS_CURRENT); + } } } } diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 528e6bd6be502..df10c6aef904d 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -33,7 +33,7 @@ import java.lang.reflect.Modifier; * the standard support creating a local implementation of such an object. * *

    Most developers will not implement this class directly, instead using the - * aidl tool to describe the desired + * aidl tool to describe the desired * interface, having it generate the appropriate Binder subclass. You can, * however, derive directly from Binder to implement your own custom RPC * protocol or simply instantiate a raw Binder object directly to use as a @@ -194,18 +194,15 @@ public class Binder implements IBinder { return true; } else if (code == DUMP_TRANSACTION) { ParcelFileDescriptor fd = data.readFileDescriptor(); - FileOutputStream fout = fd != null - ? new FileOutputStream(fd.getFileDescriptor()) : null; - PrintWriter pw = fout != null ? new PrintWriter(fout) : null; - if (pw != null) { - String[] args = data.readStringArray(); - dump(fd.getFileDescriptor(), pw, args); - pw.flush(); - } + String[] args = data.readStringArray(); if (fd != null) { try { - fd.close(); - } catch (IOException e) { + dump(fd.getFileDescriptor(), args); + } finally { + try { + fd.close(); + } catch (IOException e) { + } } } return true; @@ -213,6 +210,20 @@ public class Binder implements IBinder { return false; } + /** + * Implemented to call the more convenient version + * {@link #dump(FileDescriptor, PrintWriter, String[])}. + */ + public void dump(FileDescriptor fd, String[] args) { + FileOutputStream fout = new FileOutputStream(fd); + PrintWriter pw = new PrintWriter(fout); + try { + dump(fd, pw, args); + } finally { + pw.flush(); + } + } + /** * Print the object's state into the given stream. * @@ -302,6 +313,17 @@ final class BinderProxy implements IBinder { throws RemoteException; public native boolean unlinkToDeath(DeathRecipient recipient, int flags); + public void dump(FileDescriptor fd, String[] args) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeFileDescriptor(fd); + data.writeStringArray(args); + try { + transact(DUMP_TRANSACTION, data, null, 0); + } finally { + data.recycle(); + } + } + BinderProxy() { mSelf = new WeakReference(this); } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index cdf907b1ad05d..467c17f7d3a19 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -26,6 +26,9 @@ public class Build { /** Either a changelist number, or a label like "M4-rc20". */ public static final String ID = getString("ro.build.id"); + /** A build ID string meant for displaying to the user */ + public static final String DISPLAY = getString("ro.build.display.id"); + /** The name of the overall product. */ public static final String PRODUCT = getString("ro.product.name"); diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 5f7f91f4b9b07..950bb09d03c4c 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -17,6 +17,7 @@ package android.os; import java.io.FileOutputStream; +import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; @@ -28,12 +29,13 @@ import dalvik.bytecode.Opcodes; import dalvik.system.VMDebug; -/** Provides various debugging functions for Android applications, including +/** + * Provides various debugging functions for Android applications, including * tracing and allocation counts. *

    Logging Trace Files

    *

    Debug can create log files that give details about an application, such as * a call stack and start/stop times for any running methods. See Running the Traceview Debugging Program for +href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for * information about reading trace files. To start logging trace files, call one * of the startMethodTracing() methods. To stop tracing, call * {@link #stopMethodTracing()}. @@ -285,7 +287,7 @@ public final class Debug /** * Start method tracing with default log name and buffer size. See Running the Traceview Debugging Program for +href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for * information about reading these files. Call stopMethodTracing() to stop * tracing. */ @@ -297,7 +299,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra * Start method tracing, specifying the trace log file name. The trace * file will be put under "/sdcard" unless an absolute path is given. * See Running the Traceview Debugging Program for + href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for * information about reading trace files. * * @param traceName Name for the trace log file to create. @@ -313,7 +315,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra * Start method tracing, specifying the trace log file name and the * buffer size. The trace files will be put under "/sdcard" unless an * absolute path is given. See Running the Traceview Debugging Program for + href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for * information about reading trace files. * @param traceName Name for the trace log file to create. * If no name argument is given, this value defaults to "/sdcard/dmtrace.trace". @@ -330,7 +332,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra * Start method tracing, specifying the trace log file name and the * buffer size. The trace files will be put under "/sdcard" unless an * absolute path is given. See Running the Traceview Debugging Program for + href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for * information about reading trace files. * *

    @@ -580,6 +582,18 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra return VMDebug.getLoadedClassCount(); } + /** + * Dump "hprof" data to the specified file. This will cause a GC. + * + * @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof"). + * @throws UnsupportedOperationException if the VM was built without + * HPROF support. + * @throws IOException if an error occurs while opening or writing files. + */ + public static void dumpHprofData(String fileName) throws IOException { + VMDebug.dumpHprofData(fileName); + } + /** * Returns the number of sent transactions from this process. * @return The number of sent transactions or -1 if it could not read t. diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index e37b551903adc..f761e8e3d7138 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -74,6 +74,18 @@ public class Environment { */ public static final String MEDIA_UNMOUNTED = "unmounted"; + /** + * getExternalStorageState() returns MEDIA_CHECKING if the media is present + * and being disk-checked + */ + public static final String MEDIA_CHECKING = "checking"; + + /** + * getExternalStorageState() returns MEDIA_NOFS if the media is present + * but is blank or is using an unsupported filesystem + */ + public static final String MEDIA_NOFS = "nofs"; + /** * getExternalStorageState() returns MEDIA_MOUNTED if the media is present * and mounted at its mount point with read/write access. diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index 3ec0e9b4db675..5c40c9a041ad8 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -16,6 +16,9 @@ package android.os; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * Base interface for a remotable object, the core part of a lightweight * remote procedure call mechanism designed for high performance when @@ -144,6 +147,14 @@ public interface IBinder { */ public IInterface queryLocalInterface(String descriptor); + /** + * Print the object's state into the given stream. + * + * @param fd The raw file descriptor that the dump is being sent to. + * @param args additional arguments to the dump request. + */ + public void dump(FileDescriptor fd, String[] args) throws RemoteException; + /** * Perform a generic operation with the object. * diff --git a/core/java/android/os/ICheckinService.aidl b/core/java/android/os/ICheckinService.aidl index 70ad28e4e4698..11becc4869c7f 100644 --- a/core/java/android/os/ICheckinService.aidl +++ b/core/java/android/os/ICheckinService.aidl @@ -26,6 +26,9 @@ import android.os.IParentalControlCallback; * {@hide} */ interface ICheckinService { + /** Synchronously attempt a checkin with the server, return true on success. */ + boolean checkin(); + /** Direct submission of crash data; returns after writing the crash. */ void reportCrashSync(in byte[] crashData); diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl index 0397446155bb1..88dae8569f181 100644 --- a/core/java/android/os/IMountService.aidl +++ b/core/java/android/os/IMountService.aidl @@ -48,4 +48,19 @@ interface IMountService * Safely unmount external storage at given mount point. */ void unmountMedia(String mountPoint); + + /** + * Format external storage given a mount point + */ + void formatMedia(String mountPoint); + + /** + * Returns true if media notification sounds are enabled. + */ + boolean getPlayNotificationSounds(); + + /** + * Sets whether or not media notification sounds are played. + */ + void setPlayNotificationSounds(boolean value); } diff --git a/core/java/android/os/INetStatService.aidl b/core/java/android/os/INetStatService.aidl index fb840d84fab34..a8f3de0e044c6 100644 --- a/core/java/android/os/INetStatService.aidl +++ b/core/java/android/os/INetStatService.aidl @@ -17,14 +17,19 @@ package android.os; /** - * Retrieves packet and byte counts for the phone data interface. + * Retrieves packet and byte counts for the phone data interface, + * and for all interfaces. * Used for the data activity icon and the phone status in Settings. * * {@hide} */ interface INetStatService { - int getTxPackets(); - int getRxPackets(); - int getTxBytes(); - int getRxBytes(); + long getMobileTxPackets(); + long getMobileRxPackets(); + long getMobileTxBytes(); + long getMobileRxBytes(); + long getTotalTxPackets(); + long getTotalRxPackets(); + long getTotalTxBytes(); + long getTotalRxBytes(); } diff --git a/core/java/android/os/NetStat.java b/core/java/android/os/NetStat.java index 7312236d743eb..733137a32c242 100644 --- a/core/java/android/os/NetStat.java +++ b/core/java/android/os/NetStat.java @@ -16,36 +16,197 @@ package android.os; +import android.util.Log; + +import java.io.File; +import java.io.RandomAccessFile; +import java.io.IOException; + /** @hide */ public class NetStat{ /** - * Get total number of tx packets sent through ppp0 + * Get total number of tx packets sent through rmnet0 or ppp0 * - * @return number of Tx packets through ppp0 + * @return number of Tx packets through rmnet0 or ppp0 */ - - public native static int netStatGetTxPkts(); + public static long getMobileTxPkts() { + return getMobileStat("tx_packets"); + } /** - * Get total number of rx packets received through ppp0 + * Get total number of rx packets received through rmnet0 or ppp0 * - * @return number of Rx packets through ppp0 + * @return number of Rx packets through rmnet0 or ppp0 */ - public native static int netStatGetRxPkts(); + public static long getMobileRxPkts() { + return getMobileStat("rx_packets"); + } /** - * Get total number of tx bytes received through ppp0 + * Get total number of tx bytes received through rmnet0 or ppp0 * - * @return number of Tx bytes through ppp0 + * @return number of Tx bytes through rmnet0 or ppp0 */ - public native static int netStatGetTxBytes(); + public static long getMobileTxBytes() { + return getMobileStat("tx_bytes"); + } /** - * Get total number of rx bytes received through ppp0 + * Get total number of rx bytes received through rmnet0 or ppp0 * - * @return number of Rx bytes through ppp0 + * @return number of Rx bytes through rmnet0 or ppp0 */ - public native static int netStatGetRxBytes(); + public static long getMobileRxBytes() { + return getMobileStat("rx_bytes"); + } + /** + * Get the total number of packets sent through all network interfaces. + * + * @return the number of packets sent through all network interfaces + */ + public static long getTotalTxPkts() { + return getTotalStat("tx_packets"); + } + + /** + * Get the total number of packets received through all network interfaces. + * + * @return the number of packets received through all network interfaces + */ + public static long getTotalRxPkts() { + return getTotalStat("rx_packets"); + } + + /** + * Get the total number of bytes sent through all network interfaces. + * + * @return the number of bytes sent through all network interfaces + */ + public static long getTotalTxBytes() { + return getTotalStat("tx_bytes"); + } + + /** + * Get the total number of bytes received through all network interfaces. + * + * @return the number of bytes received through all network interfaces + */ + public static long getTotalRxBytes() { + return getTotalStat("rx_bytes"); + } + + /** + * Gets network bytes sent for this UID. + * The statistics are across all interfaces. + * The statistics come from /proc/uid_stat. + * + * {@see android.os.Process#myUid()}. + * + * @param uid + * @return byte count + */ + public static long getUidTxBytes(int uid) { + return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_snd"); + } + + /** + * Gets network bytes received for this UID. + * The statistics are across all interfaces. + * The statistics come from /proc/uid_stat. + * + * {@see android.os.Process#myUid()}. + * + * @param uid + * @return byte count + */ + public static long getUidRxBytes(int uid) { + return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_rcv"); + } + + private static String TAG = "netstat"; + private static final byte[] buf = new byte[16]; + + private static long getTotalStat(String whatStat) { + File netdir = new File("/sys/class/net"); + + File[] nets = netdir.listFiles(); + if (nets == null) { + return 0; + } + long total = 0; + StringBuffer strbuf = new StringBuffer(); + for (File net : nets) { + strbuf.append(net.getPath()).append(File.separator).append("statistics") + .append(File.separator).append(whatStat); + total += getNumberFromFilePath(strbuf.toString()); + strbuf.setLength(0); + } + return total; + } + + private static long getMobileStat(String whatStat) { + String filename = "/sys/class/net/rmnet0/statistics/" + whatStat; + RandomAccessFile raf = getFile(filename); + if (raf == null) { + filename = "/sys/class/net/ppp0/statistics/" + whatStat; + raf = getFile(filename); + } + if (raf == null) { + return 0L; + } + return getNumberFromFile(raf, filename); + } + + // File will have format + private static long getNumberFromFilePath(String filename) { + RandomAccessFile raf = getFile(filename); + if (raf == null) { + return 0L; + } + return getNumberFromFile(raf, filename); + } + + private static synchronized long getNumberFromFile(RandomAccessFile raf, String filename) { + try { + raf.read(buf); + raf.close(); + } catch (IOException e) { + Log.w(TAG, "Exception getting TCP bytes from " + filename, e); + return 0L; + } finally { + if (raf != null) { + try { + raf.close(); + } catch (IOException e) { + Log.w(TAG, "Exception closing " + filename, e); + } + } + } + + long num = 0L; + for (int i = 0; i < buf.length; i++) { + if (buf[i] < '0' || buf[i] > '9') { + break; + } + num *= 10; + num += buf[i] - '0'; + } + return num; + } + + private static RandomAccessFile getFile(String filename) { + File f = new File(filename); + if (!f.canRead()) { + return null; + } + + try { + return new RandomAccessFile(f, "r"); + } catch (IOException e) { + Log.w(TAG, "Exception opening TCP statistics file " + filename, e); + return null; + } + } } diff --git a/core/java/android/package.html b/core/java/android/package.html index b6d2999189de4..1f1be2d97e4db 100644 --- a/core/java/android/package.html +++ b/core/java/android/package.html @@ -5,6 +5,6 @@ Contains the resource classes used by standard Android applications. This package contains the resource classes that Android defines to be used in Android applications. Third party developers can use many of them also for their applications. To learn more about how to use these classes, and what a -resource is, see Resources. +resource is, see Resources and Assets. diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 95970eae0276f..837ce916171e4 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -103,8 +103,6 @@ public abstract class PreferenceActivity extends ListActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - setContentView(com.android.internal.R.layout.preference_list_content); mPreferenceManager = onCreatePreferenceManager(); @@ -214,6 +212,11 @@ public abstract class PreferenceActivity extends ListActivity implements public void setPreferenceScreen(PreferenceScreen preferenceScreen) { if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) { postBindPreferences(); + CharSequence title = getPreferenceScreen().getTitle(); + // Set the title of the activity + if (title != null) { + setTitle(title); + } } } diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java index 4258b4123f9c4..d008fd699ed96 100644 --- a/core/java/android/preference/PreferenceGroup.java +++ b/core/java/android/preference/PreferenceGroup.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; import android.os.Parcelable; +import android.text.TextUtils; import android.util.AttributeSet; /** @@ -223,6 +224,9 @@ public abstract class PreferenceGroup extends Preference implements GenericInfla * @return The {@link Preference} with the key, or null. */ public Preference findPreference(CharSequence key) { + if (TextUtils.equals(getKey(), key)) { + return this; + } final int preferenceCount = getPreferenceCount(); for (int i = 0; i < preferenceCount; i++) { final Preference preference = getPreference(i); diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java index ef5ededc24b11..5767c65bb7574 100644 --- a/core/java/android/provider/Checkin.java +++ b/core/java/android/provider/Checkin.java @@ -30,10 +30,13 @@ import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; /** - * Contract class for {@link android.server.checkin.CheckinProvider}. + * Contract class for the checkin provider, used to store events and + * statistics that will be uploaded to a checkin server eventually. * Describes the exposed database schema, and offers methods to add * events and statistics to be uploaded. * + * TODO: Move this to vendor/google when we have a home for it. + * * @hide */ public final class Checkin { @@ -56,6 +59,12 @@ public final class Checkin { /** Valid tag values. Extend as necessary for your needs. */ public enum Tag { + AUTOTEST_FAILURE, + AUTOTEST_SEQUENCE_BEGIN, + AUTOTEST_SUITE_BEGIN, + AUTOTEST_TEST_BEGIN, + AUTOTEST_TEST_FAILURE, + AUTOTEST_TEST_SUCCESS, BROWSER_BUG_REPORT, CARRIER_BUG_REPORT, CHECKIN_FAILURE, @@ -195,8 +204,11 @@ public final class Checkin { values.put(Events.TAG, tag.toString()); if (value != null) values.put(Events.VALUE, value); return resolver.insert(Events.CONTENT_URI, values); + } catch (IllegalArgumentException e) { // thrown when provider is unavailable. + Log.w(TAG, "Can't log event " + tag + ": " + e); + return null; } catch (SQLException e) { - Log.e(TAG, "Can't log event: " + tag, e); // Database errors are not fatal. + Log.e(TAG, "Can't log event " + tag, e); // Database errors are not fatal. return null; } } @@ -218,8 +230,11 @@ public final class Checkin { if (count != 0) values.put(Stats.COUNT, count); if (sum != 0.0) values.put(Stats.SUM, sum); return resolver.insert(Stats.CONTENT_URI, values); + } catch (IllegalArgumentException e) { // thrown when provider is unavailable. + Log.w(TAG, "Can't update stat " + tag + ": " + e); + return null; } catch (SQLException e) { - Log.e(TAG, "Can't update stat: " + tag, e); // Database errors are not fatal. + Log.e(TAG, "Can't update stat " + tag, e); // Database errors are not fatal. return null; } } @@ -285,4 +300,3 @@ public final class Checkin { } } } - diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java index 085c095569daa..3bffaec52989f 100644 --- a/core/java/android/provider/Contacts.java +++ b/core/java/android/provider/Contacts.java @@ -932,27 +932,33 @@ public class Contacts { } /** - * This looks up the provider category defined in - * {@link android.provider.Im.ProviderCategories} from the predefined IM protocol id. + * This looks up the provider name defined in + * {@link android.provider.Im.ProviderNames} from the predefined IM protocol id. * This is used for interacting with the IM application. - * + * * @param protocol the protocol ID - * @return the provider category the IM app uses for the given protocol, or null if no + * @return the provider name the IM app uses for the given protocol, or null if no * provider is defined for the given protocol * @hide */ - public static String lookupProviderCategoryFromId(int protocol) { + public static String lookupProviderNameFromId(int protocol) { switch (protocol) { case PROTOCOL_GOOGLE_TALK: - return Im.ProviderCategories.GTALK; + return Im.ProviderNames.GTALK; case PROTOCOL_AIM: - return Im.ProviderCategories.AIM; + return Im.ProviderNames.AIM; case PROTOCOL_MSN: - return Im.ProviderCategories.MSN; + return Im.ProviderNames.MSN; case PROTOCOL_YAHOO: - return Im.ProviderCategories.YAHOO; + return Im.ProviderNames.YAHOO; case PROTOCOL_ICQ: - return Im.ProviderCategories.ICQ; + return Im.ProviderNames.ICQ; + case PROTOCOL_JABBER: + return Im.ProviderNames.JABBER; + case PROTOCOL_SKYPE: + return Im.ProviderNames.SKYPE; + case PROTOCOL_QQ: + return Im.ProviderNames.QQ; } return null; } diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index a5a30b92f553a..4c58e0d951bd8 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -470,35 +470,41 @@ public final class Downloads implements BaseColumns { */ public static final int STATUS_UNHANDLED_REDIRECT = 493; - /** - * This download couldn't be completed because there were - * too many redirects. - */ - public static final int STATUS_TOO_MANY_REDIRECTS = 494; - /** * This download couldn't be completed because of an * unspecified unhandled HTTP code. */ - public static final int STATUS_UNHANDLED_HTTP_CODE = 495; + public static final int STATUS_UNHANDLED_HTTP_CODE = 494; /** * This download couldn't be completed because of an * error receiving or processing data at the HTTP level. */ - public static final int STATUS_HTTP_DATA_ERROR = 496; + public static final int STATUS_HTTP_DATA_ERROR = 495; /** - * This download is visible and shows in the notifications while - * in progress and after completion. + * This download couldn't be completed because of an + * HttpException while setting up the request. */ - public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 0; + public static final int STATUS_HTTP_EXCEPTION = 496; + + /** + * This download couldn't be completed because there were + * too many redirects. + */ + public static final int STATUS_TOO_MANY_REDIRECTS = 497; /** * This download is visible but only shows in the notifications * while it's in progress. */ - public static final int VISIBILITY_VISIBLE = 1; + public static final int VISIBILITY_VISIBLE = 0; + + /** + * This download is visible and shows in the notifications while + * in progress and after completion. + */ + public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1; /** * This download doesn't show in the UI or in the notifications. diff --git a/core/java/android/provider/Gmail.java b/core/java/android/provider/Gmail.java index 325f19db98a99..253dd4a6d22e1 100644 --- a/core/java/android/provider/Gmail.java +++ b/core/java/android/provider/Gmail.java @@ -370,6 +370,25 @@ public final class Gmail { "maxAttachmentSize"; } + /** + * These flags can be included as Selection Arguments when + * querying the provider. + */ + public static class SelectionArguments { + private SelectionArguments() { + // forbid instantiation + } + + /** + * Specifies that you do NOT wish the returned cursor to + * become the Active Network Cursor. If you do not include + * this flag as a selectionArg, the new cursor will become the + * Active Network Cursor by default. + */ + public static final String DO_NOT_BECOME_ACTIVE_NETWORK_CURSOR = + "SELECTION_ARGUMENT_DO_NOT_BECOME_ACTIVE_NETWORK_CURSOR"; + } + // These are the projections that we need when getting cursors from the // content provider. private static String[] CONVERSATION_PROJECTION = { @@ -435,6 +454,28 @@ public final class Gmail { } } + /** + * Behavior for a new cursor: should it become the Active Network + * Cursor? This could potentially lead to bad behavior if someone + * else is using the Active Network Cursor, since theirs will stop + * being the Active Network Cursor. + */ + public static enum BecomeActiveNetworkCursor { + /** + * The new cursor should become the one and only Active + * Network Cursor. Any other cursor that might already be the + * Active Network Cursor will cease to be so. + */ + YES, + + /** + * The new cursor should not become the Active Network + * Cursor. Any other cursor that might already be the Active + * Network Cursor will continue to be so. + */ + NO + } + /** * Wraps a Cursor in a ConversationCursor * @@ -449,6 +490,20 @@ public final class Gmail { return new ConversationCursor(this, account, cursor); } + /** + * Creates an array of SelectionArguments suitable for passing to the provider's query. + * Currently this only handles one flag, but it could be expanded in the future. + */ + private static String[] getSelectionArguments( + BecomeActiveNetworkCursor becomeActiveNetworkCursor) { + if (BecomeActiveNetworkCursor.NO == becomeActiveNetworkCursor) { + return new String[] {SelectionArguments.DO_NOT_BECOME_ACTIVE_NETWORK_CURSOR}; + } else { + // Default behavior; no args required. + return null; + } + } + /** * Asynchronously gets a cursor over all conversations matching a query. The * query is in Gmail's query syntax. When the operation is complete the handler's @@ -458,14 +513,17 @@ public final class Gmail { * @param handler An AsyncQueryHanlder that will be used to run the query * @param token The token to pass to startQuery, which will be passed back to onQueryComplete * @param query a query in Gmail's query syntax + * @param becomeActiveNetworkCursor whether or not the returned + * cursor should become the Active Network Cursor */ public void runQueryForConversations(String account, AsyncQueryHandler handler, int token, - String query) { + String query, BecomeActiveNetworkCursor becomeActiveNetworkCursor) { if (TextUtils.isEmpty(account)) { throw new IllegalArgumentException("account is empty"); } + String[] selectionArgs = getSelectionArguments(becomeActiveNetworkCursor); handler.startQuery(token, null, Uri.withAppendedPath(CONVERSATIONS_URI, account), - CONVERSATION_PROJECTION, query, null, null); + CONVERSATION_PROJECTION, query, selectionArgs, null); } /** @@ -474,11 +532,15 @@ public final class Gmail { * * @param account run the query on this account * @param query a query in Gmail's query syntax + * @param becomeActiveNetworkCursor whether or not the returned + * cursor should become the Active Network Cursor */ - public ConversationCursor getConversationCursorForQuery(String account, String query) { + public ConversationCursor getConversationCursorForQuery( + String account, String query, BecomeActiveNetworkCursor becomeActiveNetworkCursor) { + String[] selectionArgs = getSelectionArguments(becomeActiveNetworkCursor); Cursor cursor = mContentResolver.query( Uri.withAppendedPath(CONVERSATIONS_URI, account), CONVERSATION_PROJECTION, - query, null, null); + query, selectionArgs, null); return new ConversationCursor(this, account, cursor); } @@ -559,12 +621,10 @@ public final class Gmail { * server message id. * @param label the label to add or remove * @param add true to add the label, false to remove it - * @throws NonexistentLabelException thrown if the label does not exist */ public void addOrRemoveLabelOnConversation( String account, long conversationId, long maxServerMessageId, String label, - boolean add) - throws NonexistentLabelException { + boolean add) { if (TextUtils.isEmpty(account)) { throw new IllegalArgumentException("account is empty"); } @@ -599,7 +659,6 @@ public final class Gmail { * @param messageId the id of the message to whose labels should be changed * @param label the label to add or remove * @param add true to add the label, false to remove it - * @throws NonexistentLabelException thrown if the label does not exist */ public static void addOrRemoveLabelOnMessage(ContentResolver contentResolver, String account, long conversationId, long messageId, String label, boolean add) { @@ -1174,19 +1233,6 @@ public final class Gmail { void onCursorChanged(MailCursor cursor); } - /** - * Thrown when an operation is requested with a label that does not exist. - * - * TODO: this is here because I wanted a checked exception. However, I don't - * think that that is appropriate. In fact, I don't think that we should - * throw an exception at all because the label might have been valid when - * the caller presented it to the user but removed as a result of a sync. - * Maybe we should kill this and eat the errors. - */ - public static class NonexistentLabelException extends Exception { - // TODO: Add label name? - } - /** * A cursor over labels. */ @@ -2021,10 +2067,8 @@ public final class Gmail { * * @param label the label to add or remove * @param add whether to add or remove the label - * @throws NonexistentLabelException thrown if the named label does not - * exist */ - public void addOrRemoveLabel(String label, boolean add) throws NonexistentLabelException { + public void addOrRemoveLabel(String label, boolean add) { addOrRemoveLabelOnMessage(mContentResolver, mAccount, getConversationId(), getMessageId(), label, add); } diff --git a/core/java/android/provider/Im.java b/core/java/android/provider/Im.java index 68b2acdfaf53f..bea857feeaf86 100644 --- a/core/java/android/provider/Im.java +++ b/core/java/android/provider/Im.java @@ -78,21 +78,10 @@ public class Im { String MSN = "MSN"; String ICQ = "ICQ"; String AIM = "AIM"; - } - - /** - * The ProviderCategories definitions are used for the Intent category for the Intent - * - * Intent intent = new Intent(Intent.ACTION_SENDTO, - * Uri.fromParts("im", data, null)). - * addCategory(category); - */ - public interface ProviderCategories { - String GTALK = "com.android.im.category.GTALK"; - String AIM = "com.android.im.category.AIM"; - String MSN = "com.android.im.category.MSN"; - String YAHOO = "com.android.im.category.YAHOO"; - String ICQ = "com.android.im.category.ICQ"; + String XMPP = "XMPP"; + String JABBER = "JABBER"; + String SKYPE = "SKYPE"; + String QQ = "QQ"; } /** @@ -140,54 +129,6 @@ public class Im { return retVal; } - /** - * This returns the provider name given a provider category. - * - * @param providerCategory the provider category defined in {@link ProviderCategories}. - * @return the corresponding provider name defined in {@link ProviderNames}. - */ - public static String getProviderNameForCategory(String providerCategory) { - if (providerCategory != null) { - if (providerCategory.equalsIgnoreCase(ProviderCategories.GTALK)) { - return ProviderNames.GTALK; - } else if (providerCategory.equalsIgnoreCase(ProviderCategories.AIM)) { - return ProviderNames.AIM; - } else if (providerCategory.equalsIgnoreCase(ProviderCategories.MSN)) { - return ProviderNames.MSN; - } else if (providerCategory.equalsIgnoreCase(ProviderCategories.YAHOO)) { - return ProviderNames.YAHOO; - } else if (providerCategory.equalsIgnoreCase(ProviderCategories.ICQ)) { - return ProviderNames.ICQ; - } - } - - return null; - } - - /** - * This returns the provider category given a provider name. - * - * @param providername the provider name defined in {@link ProviderNames}. - * @return the provider category defined in {@link ProviderCategories}. - */ - public static String getProviderCategoryFromName(String providername) { - if (providername != null) { - if (providername.equalsIgnoreCase(Im.ProviderNames.GTALK)) { - return Im.ProviderCategories.GTALK; - } else if (providername.equalsIgnoreCase(Im.ProviderNames.AIM)) { - return Im.ProviderCategories.AIM; - } else if (providername.equalsIgnoreCase(Im.ProviderNames.MSN)) { - return Im.ProviderCategories.MSN; - } else if (providername.equalsIgnoreCase(Im.ProviderNames.YAHOO)) { - return Im.ProviderCategories.YAHOO; - } else if (providername.equalsIgnoreCase(Im.ProviderNames.ICQ)) { - return Im.ProviderCategories.ICQ; - } - } - - return null; - } - private static final String[] PROVIDER_PROJECTION = new String[] { _ID, NAME @@ -796,6 +737,13 @@ public class Im { */ String ETAG = "etag"; + /** + * The OTR etag, computed by the server, stored on the client. There is one OTR etag + * per account roster. + *

    Type: TEXT

    + */ + String OTR_ETAG = "otr_etag"; + /** * The account id for the etag. *

    Type: INTEGER

    @@ -837,12 +785,38 @@ public class Im { return retVal; } + public static final String getOtrEtag(ContentResolver resolver, long accountId) { + String retVal = null; + + Cursor c = resolver.query(CONTENT_URI, + CONTACT_OTR_ETAG_PROJECTION, + ACCOUNT + "=" + accountId, + null /* selection args */, + null /* sort order */); + + try { + if (c.moveToFirst()) { + retVal = c.getString(COLUMN_OTR_ETAG); + } + } finally { + c.close(); + } + + return retVal; + } + private static final String[] CONTACT_ETAG_PROJECTION = new String[] { Im.ContactsEtag.ETAG // 0 }; private static int COLUMN_ETAG = 0; + private static final String[] CONTACT_OTR_ETAG_PROJECTION = new String[] { + Im.ContactsEtag.OTR_ETAG // 0 + }; + + private static int COLUMN_OTR_ETAG = 0; + /** * The content:// style URL for this table */ @@ -1271,9 +1245,9 @@ public class Im { } /** - * Columns shared between the IM and contacts presence tables + * Common presence columns shared between the IM and contacts presence tables */ - interface CommonPresenceColumns { + public interface CommonPresenceColumns { /** * The priority, an integer, used by XMPP presence *

    Type: INTEGER

    diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 87a02e6c2ae25..2897b4e7645d8 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -91,14 +91,14 @@ public final class MediaStore public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation"; /** - * The name of the Intent-extra used to control the orientation of a ViewImage. + * The name of an Intent-extra used to control the UI of a ViewImage. * This is a boolean property that overrides the activity's default fullscreen state. * @hide */ public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen"; /** - * The name of the Intent-extra used to control the orientation of a ViewImage. + * The name of an Intent-extra used to control the UI of a ViewImage. * This is a boolean property that specifies whether or not to show action icons. * @hide */ @@ -117,25 +117,36 @@ public final class MediaStore */ public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA"; - /** * The name of the Intent action used to launch a camera in video mode. */ public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA"; /** - * Standard Intent action that can be sent to have the media application - * capture an image and return it. The image is returned as a Bitmap - * object in the extra field. - * @hide + * Standard Intent action that can be sent to have the camera application + * capture an image and return it. + *

    + * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written. + * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap + * object in the extra field. This is useful for applications that only need a small image. + * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri + * value of EXTRA_OUTPUT. + * @see #EXTRA_OUTPUT + * @see #EXTRA_VIDEO_QUALITY */ public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; /** - * Standard Intent action that can be sent to have the media application - * capture an video and return it. The caller may pass in an extra EXTRA_VIDEO_QUALITY - * control the video quality. - * @hide + * Standard Intent action that can be sent to have the camera application + * capture an video and return it. + *

    + * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality. + *

    + * The caller may pass in an extra EXTRA_OUTPUT to control + * where the video is written. If EXTRA_OUTPUT is not present the video will be + * written to the standard location for videos, and the Uri of that location will be + * returned in the data field of the Uri. + * @see #EXTRA_OUTPUT */ public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE"; @@ -143,14 +154,12 @@ public final class MediaStore * The name of the Intent-extra used to control the quality of a recorded video. This is an * integer property. Currently value 0 means low quality, suitable for MMS messages, and * value 1 means high quality. In the future other quality levels may be added. - * @hide */ public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality"; /** - * The name of the Intent-extra used to indicate a Uri to be used to + * The name of the Intent-extra used to indicate a content resolver Uri to be used to * store the requested image or video. - * @hide */ public final static String EXTRA_OUTPUT = "output"; @@ -471,7 +480,7 @@ public final class MediaStore /** * The default sort order for this table */ - public static final String DEFAULT_SORT_ORDER = "name ASC"; + public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME; } public static class Thumbnails implements BaseColumns @@ -581,6 +590,14 @@ public final class MediaStore */ public static final String DURATION = "duration"; + /** + * The position, in ms, playback was at when playback for this file + * was last stopped. + *

    Type: INTEGER (long)

    + * @hide + */ + public static final String BOOKMARK = "bookmark"; + /** * The id of the artist who created the audio file, if any *

    Type: INTEGER (long)

    @@ -653,6 +670,13 @@ public final class MediaStore */ public static final String IS_MUSIC = "is_music"; + /** + * Non-zero if the audio file is a podcast + *

    Type: INTEGER (boolean)

    + * @hide + */ + public static final String IS_PODCAST = "is_podcast"; + /** * Non-zero id the audio file may be a ringtone *

    Type: INTEGER (boolean)

    @@ -1198,22 +1222,15 @@ public final class MediaStore } public static final class Video { - /** - * deprecated Replaced by DEFAULT_SORT_ORDER2 - * This variable is a mistake that is retained for backwards compatibility. - * (There is no "name" column in the Video table.) - */ - public static final String DEFAULT_SORT_ORDER = "name ASC"; /** - * The default sort order for this table - * @hide + * The default sort order for this table. */ - public static final String DEFAULT_SORT_ORDER2 = MediaColumns.DISPLAY_NAME; + public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME; public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) { - return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER2); + return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); } public interface VideoColumns extends MediaColumns { @@ -1316,7 +1333,6 @@ public final class MediaStore * video should start playing at the next time it is opened. If the value is null or * out of the range 0..DURATION-1 then the video should start playing from the * beginning. - * @hide *

    Type: INTEGER

    */ public static final String BOOKMARK = "bookmark"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index abbfd5bdb8e1b..054da1de5f48e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -234,6 +234,35 @@ public final class Settings { public static final String ACTION_LOCALE_SETTINGS = "android.settings.LOCALE_SETTINGS"; + /** + * Activity Action: Show settings to configure input methods, in particular + * allowing the user to enable input methods. + *

    + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + *

    + * Input: Nothing. + *

    + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_INPUT_METHOD_SETTINGS = + "android.settings.INPUT_METHOD_SETTINGS"; + + /** + * Activity Action: Show settings to manage the user input dictionary. + *

    + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + *

    + * Input: Nothing. + *

    + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_USER_DICTIONARY_SETTINGS = + "android.settings.USER_DICTIONARY_SETTINGS"; + /** * Activity Action: Show settings to allow configuration of application-related settings. *

    @@ -1232,6 +1261,13 @@ public final class Settings { */ public static final String TRANSITION_ANIMATION_SCALE = "transition_animation_scale"; + /** + * Scaling factor for normal window animations. Setting to 0 will disable window + * animations. + * @hide + */ + public static final String FANCY_IME_ANIMATIONS = "fancy_ime_animations"; + /** * Control whether the accelerometer will be used to change screen * orientation. If 0, it will not be used unless explicitly requested @@ -2008,6 +2044,14 @@ public final class Settings { */ public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms"; + + /** + * Whether background data usage is allowed by the user. See + * ConnectivityManager for more info. + * + * @hide pending API council + */ + public static final String BACKGROUND_DATA = "background_data"; } /** @@ -2130,6 +2174,11 @@ public final class Settings { * Event tags from the kernel event log to upload during checkin. */ public static final String CHECKIN_EVENTS = "checkin_events"; + + /** + * Event tags for list of services to upload during checkin. + */ + public static final String CHECKIN_DUMPSYS_LIST = "checkin_dumpsys_list"; /** * The interval (in seconds) between periodic checkin attempts. @@ -2704,8 +2753,27 @@ public final class Settings { * Speech encoding used with voice search on WIFI networks. To be factored out of this class. */ public static final String VOICE_SEARCH_ENCODING_WIFI = "voice_search_encoding_wifi"; - - + + /** + * Prefix for rules that launch automatic instrumentation test cycles. + * The format is the InstrumentationTestRunner (or compatible) package and class, + * followed optionally by space-separated key value pairs to pass to it. + */ + public static final String AUTOTEST_PREFIX = "autotest:"; + + /** + * Interval between synchronous checkins forced by the automatic test runner. + * If you set this to a value smaller than CHECKIN_INTERVAL, then the test runner's + * frequent checkins will prevent asynchronous background checkins from interfering + * with any performance measurements. + */ + public static final String AUTOTEST_CHECKIN_SECONDS = "autotest_checkin_seconds"; + + /** + * Interval between reboots forced by the automatic test runner. + */ + public static final String AUTOTEST_REBOOT_SECONDS = "autotest_reboot_seconds"; + /** * @deprecated * @hide @@ -2912,9 +2980,10 @@ public final class Settings { * * @param context A context. * @param cursor A cursor pointing to the row whose title should be - * returned. The cursor must contain at least the - * {@link #TITLE} and {@link #INTENT} columns. - * @return A title that is localized and can be displayed to the user. + * returned. The cursor must contain at least the {@link #TITLE} + * and {@link #INTENT} columns. + * @return A title that is localized and can be displayed to the user, + * or the empty string if one could not be found. */ public static CharSequence getTitle(Context context, Cursor cursor) { int titleColumn = cursor.getColumnIndex(TITLE); @@ -2943,7 +3012,7 @@ public final class Settings { PackageManager packageManager = context.getPackageManager(); ResolveInfo info = packageManager.resolveActivity(intent, 0); - return info.loadLabel(packageManager); + return info != null ? info.loadLabel(packageManager) : ""; } } diff --git a/core/java/android/provider/Sync.java b/core/java/android/provider/Sync.java index 94bf80777d0f0..628852f4c37cf 100644 --- a/core/java/android/provider/Sync.java +++ b/core/java/android/provider/Sync.java @@ -273,6 +273,7 @@ public final class Sync { public static final int ERROR_CONFLICT = 5; public static final int ERROR_TOO_MANY_DELETIONS = 6; public static final int ERROR_TOO_MANY_RETRIES = 7; + public static final int ERROR_INTERNAL = 8; // The MESG column will contain one of these or one of the Error types. public static final String MESG_SUCCESS = "success"; @@ -292,6 +293,7 @@ public final class Sync { case ERROR_CONFLICT: return "conflict detected"; case ERROR_TOO_MANY_DELETIONS: return "too many deletions"; case ERROR_TOO_MANY_RETRIES: return "too many retries"; + case ERROR_INTERNAL: return "internal error"; default: return "unknown error"; } } @@ -491,11 +493,6 @@ public final class Sync { /** controls whether or not the device listens for sync tickles */ public static final String SETTING_LISTEN_FOR_TICKLES = "listen_for_tickles"; - /** controls whether or not the device connect to Google in background for various - * stuff, including GTalk, checkin, Market and data sync ... - */ - public static final String SETTING_BACKGROUND_DATA = "background_data"; - /** controls whether or not the individual provider is synced when tickles are received */ public static final String SETTING_SYNC_PROVIDER_PREFIX = "sync_provider_"; @@ -572,18 +569,6 @@ public final class Sync { putBoolean(contentResolver, SETTING_LISTEN_FOR_TICKLES, flag); } - /** - * A convenience method to set whether or not the device should connect to Google - * in background. - * - * @param contentResolver the ContentResolver to use to access the settings table - * @param flag true if it should connect. - */ - static public void setBackgroundData(ContentResolver contentResolver, - boolean flag) { - putBoolean(contentResolver, SETTING_BACKGROUND_DATA, flag); - } - public static class QueryMap extends ContentQueryMap { private ContentResolver mContentResolver; @@ -631,24 +616,6 @@ public final class Sync { return getBoolean(SETTING_LISTEN_FOR_TICKLES, true); } - /** - * Set whether or not the device should connect to Google in background - * - * @param flag true if it should - */ - public void setBackgroundData(boolean flag) { - Settings.setBackgroundData(mContentResolver, flag); - } - - /** - * Check if the device should connect to Google in background. - - * @return true if it should - */ - public boolean getBackgroundData() { - return getBoolean(SETTING_BACKGROUND_DATA, true); - } - /** * Convenience function for retrieving a single settings value * as a boolean. diff --git a/core/java/android/provider/UserDictionary.java b/core/java/android/provider/UserDictionary.java index 58e5731f4cc76..5a7ef85315b41 100644 --- a/core/java/android/provider/UserDictionary.java +++ b/core/java/android/provider/UserDictionary.java @@ -25,10 +25,13 @@ import android.net.Uri; import android.text.TextUtils; /** - * - * @hide Pending API council approval + * A provider of user defined words for input methods to use for predictive text input. + * Applications and input methods may add words into the dictionary. Words can have associated + * frequency information and locale information. */ public class UserDictionary { + + /** Authority string for this provider. */ public static final String AUTHORITY = "user_dictionary"; /** @@ -39,7 +42,6 @@ public class UserDictionary { /** * Contains the user defined words. - * @hide Pending API council approval */ public static class Words implements BaseColumns { /** @@ -67,14 +69,14 @@ public class UserDictionary { public static final String WORD = "word"; /** - * The frequency column. A value between 1 and 255. + * The frequency column. A value between 1 and 255. Higher values imply higher frequency. *

    TYPE: INTEGER

    */ public static final String FREQUENCY = "frequency"; /** * The locale that this word belongs to. Null if it pertains to all - * locales. Locale is a 5 letter string such as
    en_US
    . + * locales. Locale is as defined by the string returned by Locale.toString(). *

    TYPE: TEXT

    */ public static final String LOCALE = "locale"; @@ -85,8 +87,10 @@ public class UserDictionary { */ public static final String APP_ID = "appid"; + /** The locale type to specify that the word is common to all locales. */ public static final int LOCALE_TYPE_ALL = 0; + /** The locale type to specify that the word is for the current locale. */ public static final int LOCALE_TYPE_CURRENT = 1; /** @@ -94,7 +98,14 @@ public class UserDictionary { */ public static final String DEFAULT_SORT_ORDER = FREQUENCY + " DESC"; - + /** Adds a word to the dictionary, with the given frequency and the specified + * specified locale type. + * @param context the current application context + * @param word the word to add to the dictionary. This should not be null or + * empty. + * @param localeType the locale type for this word. It should be one of + * {@link #LOCALE_TYPE_ALL} or {@link #LOCALE_TYPE_CURRENT}. + */ public static void addWord(Context context, String word, int frequency, int localeType) { final ContentResolver resolver = context.getContentResolver(); diff --git a/core/java/android/provider/package.html b/core/java/android/provider/package.html index a5535920f9cac..055b037b5d262 100644 --- a/core/java/android/provider/package.html +++ b/core/java/android/provider/package.html @@ -6,6 +6,6 @@ Android. as contact informations, calendar information, and media files. These classes provide simplified methods of adding or retrieving data from these content providers. For information about how to use a content provider, see Reading and Writing Persistent Data. +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers. diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 8486e4b5acd1c..58f9491b98ff8 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -55,6 +55,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; private static final String A2DP_SINK_ADDRESS = "a2dp_sink_address"; + private static final String BLUETOOTH_ENABLED = "bluetooth_enabled"; private final Context mContext; private final IntentFilter mIntentFilter; @@ -136,10 +137,28 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { BluetoothA2dp.STATE_DISCONNECTED)); } } + mAudioManager.setParameter(BLUETOOTH_ENABLED, "true"); } private synchronized void onBluetoothDisable() { - mAudioDevices = null; + if (mAudioDevices != null) { + for (String path : mAudioDevices.keySet()) { + switch (mAudioDevices.get(path).state) { + case BluetoothA2dp.STATE_CONNECTING: + case BluetoothA2dp.STATE_CONNECTED: + case BluetoothA2dp.STATE_PLAYING: + disconnectSinkNative(path); + updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + break; + case BluetoothA2dp.STATE_DISCONNECTING: + updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + break; + } + } + mAudioDevices = null; + } + mAudioManager.setBluetoothA2dpOn(false); + mAudioManager.setParameter(BLUETOOTH_ENABLED, "false"); } public synchronized int connectSink(String address) { @@ -289,6 +308,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } } + updateState(path, BluetoothA2dp.STATE_CONNECTING); mAudioManager.setParameter(A2DP_SINK_ADDRESS, lookupAddress(path)); mAudioManager.setBluetoothA2dpOn(true); updateState(path, BluetoothA2dp.STATE_CONNECTED); @@ -309,6 +329,11 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private synchronized final String lookupAddress(String path) { if (mAudioDevices == null) return null; + SinkState sink = mAudioDevices.get(path); + if (sink == null) { + Log.w(TAG, "lookupAddress() called for unknown device " + path); + updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + } String address = mAudioDevices.get(path).address; if (address == null) Log.e(TAG, "Can't find address for " + path); return address; @@ -349,6 +374,15 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothA2dp.SINK_STATE, state); mContext.sendBroadcast(intent, BLUETOOTH_PERM); + + if ((prevState == BluetoothA2dp.STATE_CONNECTED || + prevState == BluetoothA2dp.STATE_PLAYING) && + (state != BluetoothA2dp.STATE_CONNECTED && + state != BluetoothA2dp.STATE_PLAYING)) { + // disconnected + intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + mContext.sendBroadcast(intent); + } } } diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 3ce34c30a198b..d1497619a92e2 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -91,8 +91,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mIsDiscovering = false; mEventLoop = new BluetoothEventLoop(mContext, this); registerForAirplaneMode(); - - disableEsco(); // TODO: enable eSCO support once its fully supported } private native void initializeNativeDataNative(); @@ -130,10 +128,21 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } mEventLoop.stop(); disableNative(); + + // mark in progress bondings as cancelled + for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) { + mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED, + BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); + } + // update mode + Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mIsEnabled = false; - Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, 0); + persistBluetoothOnSetting(false); mIsDiscovering = false; - Intent intent = new Intent(BluetoothIntent.DISABLED_ACTION); + intent = new Intent(BluetoothIntent.DISABLED_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_PERM); return true; } @@ -202,18 +211,27 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (res) { mIsEnabled = true; - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.BLUETOOTH_ON, 1); + persistBluetoothOnSetting(true); mIsDiscovering = false; Intent intent = new Intent(BluetoothIntent.ENABLED_ACTION); mBondState.loadBondState(); mContext.sendBroadcast(intent, BLUETOOTH_PERM); mHandler.sendMessageDelayed(mHandler.obtainMessage(REGISTER_SDP_RECORDS), 3000); + + // Update mode + mEventLoop.onModeChanged(getModeNative()); } mEnableThread = null; } - }; + } + private void persistBluetoothOnSetting(boolean bluetoothOn) { + long origCallerIdentityToken = Binder.clearCallingIdentity(); + Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, + bluetoothOn ? 1 : 0); + Binder.restoreCallingIdentity(origCallerIdentityToken); + } + private native int enableNative(); private native int disableNative(); @@ -288,10 +306,10 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return state.intValue(); } - public synchronized String[] listBonds() { + private synchronized String[] listInState(int state) { ArrayList result = new ArrayList(mState.size()); for (Map.Entry e : mState.entrySet()) { - if (e.getValue().intValue() == BluetoothDevice.BOND_BONDED) { + if (e.getValue().intValue() == state) { result.add(e.getKey()); } } @@ -353,18 +371,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } private native boolean setNameNative(String name); - public synchronized String getMajorClass() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getMajorClassNative(); - } - private native String getMajorClassNative(); - - public synchronized String getMinorClass() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getMinorClassNative(); - } - private native String getMinorClassNative(); - /** * Returns the user-friendly name of a remote device. This value is * retrned from our local cache, which is updated during device discovery. @@ -386,24 +392,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { /* pacakge */ native String getAdapterPathNative(); - /** - * Initiate a remote-device-discovery procedure. This procedure may be - * canceled by calling {@link #stopDiscovery}. Remote-device discoveries - * are returned as intents - *

    - * Typically, when a remote device is found, your - * android.bluetooth.DiscoveryEventNotifier#notifyRemoteDeviceFound - * method will be invoked, and subsequently, your - * android.bluetooth.RemoteDeviceEventNotifier#notifyRemoteNameUpdated - * will tell you the user-friendly name of the remote device. However, - * it is possible that the name update may fail for various reasons, so you - * should display the device's Bluetooth address as soon as you get a - * notifyRemoteDeviceFound event, and update the name when you get the - * remote name. - * - * @return true if discovery has started, - * false otherwise. - */ public synchronized boolean startDiscovery(boolean resolveNames) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); @@ -411,12 +399,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } private native boolean startDiscoveryNative(boolean resolveNames); - /** - * Cancel a remote-device discovery. - * - * Note: you may safely call this method even when discovery has not been - * started. - */ public synchronized boolean cancelDiscovery() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); @@ -492,171 +474,23 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } private native boolean isConnectedNative(String address); - /** - * Detetermines whether this device is connectable (that is, whether remote - * devices can connect to it.) - *

    - * Note: A Bluetooth adapter has separate connectable and discoverable - * states, and you could have any combination of those. Although - * any combination is possible (such as discoverable but not - * connectable), we restrict the possible combinations to one of - * three possibilities: discoverable and connectable, connectable - * but not discoverable, and neither connectable nor discoverable. - * - * @return true if this adapter is connectable - * false otherwise - * - * @see #isDiscoverable - * @see #getMode - * @see #setMode - */ - public synchronized boolean isConnectable() { + public synchronized int getScanMode() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return isConnectableNative(); - } - private native boolean isConnectableNative(); - - /** - * Detetermines whether this device is discoverable. - * - * Note: a Bluetooth adapter has separate connectable and discoverable - * states, and you could have any combination of those. Although - * any combination is possible (such as discoverable but not - * connectable), we restrict the possible combinations to one of - * three possibilities: discoverable and connectable, connectable - * but not discoverable, and neither connectable nor discoverable. - * - * @return true if this adapter is discoverable - * false otherwise - * - * @see #isConnectable - * @see #getMode - * @see #setMode - */ - public synchronized boolean isDiscoverable() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return isDiscoverableNative(); - } - private native boolean isDiscoverableNative(); - - /** - * Determines which one of three modes this adapter is in: discoverable and - * connectable, not discoverable but connectable, or neither. - * - * @return Mode enumeration containing the current mode. - * - * @see #setMode - */ - public synchronized int getMode() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - String mode = getModeNative(); - if (mode == null) { - return BluetoothDevice.MODE_UNKNOWN; - } - if (mode.equalsIgnoreCase("off")) { - return BluetoothDevice.MODE_OFF; - } - else if (mode.equalsIgnoreCase("connectable")) { - return BluetoothDevice.MODE_CONNECTABLE; - } - else if (mode.equalsIgnoreCase("discoverable")) { - return BluetoothDevice.MODE_DISCOVERABLE; - } - else { - return BluetoothDevice.MODE_UNKNOWN; - } + return bluezStringToScanMode(getModeNative()); } private native String getModeNative(); - /** - * Set the discoverability and connectability mode of this adapter. The - * possibilities are discoverable and connectable (MODE_DISCOVERABLE), - * connectable but not discoverable (MODE_CONNECTABLE), and neither - * (MODE_OFF). - * - * Note: MODE_OFF does not mean that the adapter is physically off. It - * may be neither discoverable nor connectable, but it could still - * initiate outgoing connections, or could participate in a - * connection initiated by a remote device before its mode was set - * to MODE_OFF. - * - * @param mode the new mode - * @see #getMode - */ - public synchronized boolean setMode(int mode) { + public synchronized boolean setScanMode(int mode) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - switch (mode) { - case BluetoothDevice.MODE_OFF: - return setModeNative("off"); - case BluetoothDevice.MODE_CONNECTABLE: - return setModeNative("connectable"); - case BluetoothDevice.MODE_DISCOVERABLE: - return setModeNative("discoverable"); + String bluezMode = scanModeToBluezString(mode); + if (bluezMode != null) { + return setModeNative(bluezMode); } return false; } private native boolean setModeNative(String mode); - /** - * Retrieves the alias of a remote device. The alias is a local feature, - * and allows us to associate a name with a remote device that is different - * from that remote device's user-friendly name. The remote device knows - * nothing about this. The alias can be changed with - * {@link #setRemoteAlias}, and it may be removed with - * {@link #clearRemoteAlias} - * - * @param address Bluetooth address of remote device. - * - * @return The alias of the remote device. - */ - public synchronized String getRemoteAlias(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteAliasNative(address); - } - private native String getRemoteAliasNative(String address); - - /** - * Changes the alias of a remote device. The alias is a local feature, - * from that remote device's user-friendly name. The remote device knows - * nothing about this. The alias can be retrieved with - * {@link #getRemoteAlias}, and it may be removed with - * {@link #clearRemoteAlias}. - * - * @param address Bluetooth address of remote device - * @param alias Alias for the remote device - */ - public synchronized boolean setRemoteAlias(String address, String alias) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (alias == null || !BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return setRemoteAliasNative(address, alias); - } - private native boolean setRemoteAliasNative(String address, String alias); - - /** - * Removes the alias of a remote device. The alias is a local feature, - * from that remote device's user-friendly name. The remote device knows - * nothing about this. The alias can be retrieved with - * {@link #getRemoteAlias}. - * - * @param address Bluetooth address of remote device - */ - public synchronized boolean clearRemoteAlias(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return clearRemoteAliasNative(address); - } - private native boolean clearRemoteAliasNative(String address); - public synchronized boolean disconnectRemoteDeviceAcl(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); @@ -699,7 +533,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED, - BluetoothDevice.UNBOND_REASON_CANCELLED); + BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); cancelBondingProcessNative(address); return true; } @@ -717,7 +551,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { public synchronized String[] listBonds() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBondState.listBonds(); + return mBondState.listInState(BluetoothDevice.BOND_BONDED); } public synchronized int getBondState(String address) { @@ -918,69 +752,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } private native String lastUsedNative(String address); - /** - * Gets the major device class of the specified device. - * Example: "computer" - * - * Note: This is simply a string desciption of the major class of the - * device-class information, which is returned as a 32-bit value - * during device discovery. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device major class - * - * @see #getRemoteClass - */ - public synchronized String getRemoteMajorClass(String address) { - if (!BluetoothDevice.checkBluetoothAddress(address)) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return null; - } - return getRemoteMajorClassNative(address); - } - private native String getRemoteMajorClassNative(String address); - - /** - * Gets the minor device class of the specified device. - * Example: "laptop" - * - * Note: This is simply a string desciption of the minor class of the - * device-class information, which is returned as a 32-bit value - * during device discovery. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device minor class - * - * @see #getRemoteClass - */ - public synchronized String getRemoteMinorClass(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteMinorClassNative(address); - } - private native String getRemoteMinorClassNative(String address); - - /** - * Gets the service classes of the specified device. - * Example: ["networking", "object transfer"] - * - * @return a String array with the descriptions of the service classes. - * - * @see #getRemoteClass - */ - public synchronized String[] getRemoteServiceClasses(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteServiceClassesNative(address); - } - private native String[] getRemoteServiceClassesNative(String address); - /** * Gets the remote major, minor, and service classes encoded as a 32-bit * integer. @@ -1188,16 +959,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { Settings.System.AIRPLANE_MODE_ON, 0) == 1; } - private static final String DISABLE_ESCO_PATH = "/sys/module/sco/parameters/disable_esco"; - private static void disableEsco() { - try { - FileWriter file = new FileWriter(DISABLE_ESCO_PATH); - file.write("Y"); - file.close(); - } catch (FileNotFoundException e) { - } catch (IOException e) {} - } - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mIsEnabled) { @@ -1248,6 +1009,34 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive); } + /* package */ static int bluezStringToScanMode(String mode) { + if (mode == null) { + return BluetoothError.ERROR; + } + mode = mode.toLowerCase(); + if (mode.equals("off")) { + return BluetoothDevice.SCAN_MODE_NONE; + } else if (mode.equals("connectable")) { + return BluetoothDevice.SCAN_MODE_CONNECTABLE; + } else if (mode.equals("discoverable")) { + return BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE; + } else { + return BluetoothError.ERROR; + } + } + + /* package */ static String scanModeToBluezString(int mode) { + switch (mode) { + case BluetoothDevice.SCAN_MODE_NONE: + return "off"; + case BluetoothDevice.SCAN_MODE_CONNECTABLE: + return "connectable"; + case BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE: + return "discoverable"; + } + return null; + } + private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 4f63f9835033e..0f60fae346205 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -16,6 +16,7 @@ package android.server; +import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothError; @@ -114,9 +115,7 @@ class BluetoothEventLoop { public synchronized void stop() { if (mThread != null) { - mInterrupted = true; - try { mThread.join(); mThread = null; @@ -130,104 +129,86 @@ class BluetoothEventLoop { return mThread != null; } - public void onModeChanged(String mode) { - Intent intent = new Intent(BluetoothIntent.MODE_CHANGED_ACTION); - int intMode = BluetoothDevice.MODE_UNKNOWN; - if (mode.equalsIgnoreCase("off")) { - intMode = BluetoothDevice.MODE_OFF; + /*package*/ void onModeChanged(String bluezMode) { + int mode = BluetoothDeviceService.bluezStringToScanMode(bluezMode); + if (mode >= 0) { + Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.SCAN_MODE, mode); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - else if (mode.equalsIgnoreCase("connectable")) { - intMode = BluetoothDevice.MODE_CONNECTABLE; - } - else if (mode.equalsIgnoreCase("discoverable")) { - intMode = BluetoothDevice.MODE_DISCOVERABLE; - } - intent.putExtra(BluetoothIntent.MODE, intMode); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onDiscoveryStarted() { + private void onDiscoveryStarted() { mBluetoothService.setIsDiscovering(true); Intent intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onDiscoveryCompleted() { + private void onDiscoveryCompleted() { mBluetoothService.setIsDiscovering(false); Intent intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onPairingRequest() { + private void onPairingRequest() { Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); } - public void onPairingCancel() { + private void onPairingCancel() { Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); } - public void onRemoteDeviceFound(String address, int deviceClass, short rssi) { + private void onRemoteDeviceFound(String address, int deviceClass, short rssi) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); intent.putExtra(BluetoothIntent.CLASS, deviceClass); intent.putExtra(BluetoothIntent.RSSI, rssi); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteDeviceDisappeared(String address) { + private void onRemoteDeviceDisappeared(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteClassUpdated(String address, int deviceClass) { + private void onRemoteClassUpdated(String address, int deviceClass) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); intent.putExtra(BluetoothIntent.CLASS, deviceClass); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteDeviceConnected(String address) { + private void onRemoteDeviceConnected(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteDeviceDisconnectRequested(String address) { + private void onRemoteDeviceDisconnectRequested(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteDeviceDisconnected(String address) { + private void onRemoteDeviceDisconnected(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteNameUpdated(String address, String name) { + private void onRemoteNameUpdated(String address, String name) { Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); intent.putExtra(BluetoothIntent.NAME, name); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteNameFailed(String address) { + private void onRemoteNameFailed(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_FAILED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteNameChanged(String address, String name) { + private void onRemoteNameChanged(String address, String name) { Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); intent.putExtra(BluetoothIntent.NAME, name); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onRemoteAliasChanged(String address, String alias) { - Intent intent = new Intent(BluetoothIntent.REMOTE_ALIAS_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.ALIAS, alias); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - public void onRemoteAliasCleared(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_ALIAS_CLEARED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } private void onCreateBondingResult(String address, int result) { address = address.toUpperCase(); @@ -239,23 +220,23 @@ class BluetoothEventLoop { } } - public void onBondingCreated(String address) { + private void onBondingCreated(String address) { mBluetoothService.getBondState().setBondState(address.toUpperCase(), BluetoothDevice.BOND_BONDED); } - public void onBondingRemoved(String address) { + private void onBondingRemoved(String address) { mBluetoothService.getBondState().setBondState(address.toUpperCase(), BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED); } - public void onNameChanged(String name) { + private void onNameChanged(String name) { Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION); intent.putExtra(BluetoothIntent.NAME, name); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - public void onPasskeyAgentRequest(String address, int nativeData) { + private void onPasskeyAgentRequest(String address, int nativeData) { address = address.toUpperCase(); mPasskeyAgentRequestData.put(address, new Integer(nativeData)); @@ -284,14 +265,36 @@ class BluetoothEventLoop { mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); } - public void onPasskeyAgentCancel(String address) { + private void onPasskeyAgentCancel(String address) { address = address.toUpperCase(); mPasskeyAgentRequestData.remove(address); Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED, - BluetoothDevice.UNBOND_REASON_CANCELLED); + BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); + } + + private boolean onAuthAgentAuthorize(String address, String service, String uuid) { + boolean authorized = false; + if (service.endsWith("service_audio")) { + BluetoothA2dp a2dp = new BluetoothA2dp(mContext); + authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF; + if (authorized) { + Log.i(TAG, "Allowing incoming A2DP connection from " + address); + } else { + Log.i(TAG, "Rejecting incoming A2DP connection from " + address); + } + } else { + Log.i(TAG, "Rejecting incoming " + service + " connection from " + address); + } + return authorized; + } + + private void onAuthAgentCancel(String address, String service, String uuid) { + // We immediately response to DBUS Authorize() so this should not + // usually happen + log("onAuthAgentCancel(" + address + ", " + service + ", " + uuid + ")"); } private void onGetRemoteServiceChannelResult(String address, int channel) { diff --git a/core/java/android/server/checkin/CheckinProvider.java b/core/java/android/server/checkin/CheckinProvider.java deleted file mode 100644 index 86ece4ac1c2a3..0000000000000 --- a/core/java/android/server/checkin/CheckinProvider.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * 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.server.checkin; - -import android.content.ContentProvider; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.database.sqlite.SQLiteQueryBuilder; -import android.net.Uri; -import android.os.Environment; -import android.provider.BaseColumns; -import android.provider.Checkin; -import android.util.Log; - -import java.io.File; - -/** - * Content provider for the database used to store events and statistics - * while they wait to be uploaded by the checkin service. - */ -public class CheckinProvider extends ContentProvider { - /** Class identifier for logging. */ - private static final String TAG = "CheckinProvider"; - - /** Filename of database (in /data directory). */ - private static final String DATABASE_FILENAME = "checkin.db"; - - /** Version of database schema. */ - private static final int DATABASE_VERSION = 1; - - /** Maximum number of events recorded. */ - private static final int EVENT_LIMIT = 1000; - - /** Maximum size of individual event data. */ - private static final int EVENT_SIZE = 8192; - - /** Maximum number of crashes recorded. */ - private static final int CRASH_LIMIT = 25; - - /** Maximum size of individual crashes recorded. */ - private static final int CRASH_SIZE = 16384; - - /** Permission required for access to the 'properties' database. */ - private static final String PROPERTIES_PERMISSION = - "android.permission.ACCESS_CHECKIN_PROPERTIES"; - - /** Lock for stats read-modify-write update cycle (see {@link #insert}). */ - private final Object mStatsLock = new Object(); - - /** The underlying SQLite database. */ - private SQLiteOpenHelper mOpenHelper; - - private static class OpenHelper extends SQLiteOpenHelper { - public OpenHelper(Context context) { - super(context, DATABASE_FILENAME, null, DATABASE_VERSION); - - // The database used to live in /data/checkin.db. - File oldLocation = Environment.getDataDirectory(); - File old = new File(oldLocation, DATABASE_FILENAME); - File file = context.getDatabasePath(DATABASE_FILENAME); - - // Try to move the file to the new location. - // TODO: Remove this code before shipping. - if (old.exists() && !file.exists() && !old.renameTo(file)) { - Log.e(TAG, "Can't rename " + old + " to " + file); - } - if (old.exists() && !old.delete()) { - // Clean up the old data file in any case. - Log.e(TAG, "Can't remove " + old); - } - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + Checkin.Events.TABLE_NAME + " (" + - BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Checkin.Events.TAG + " TEXT NOT NULL," + - Checkin.Events.VALUE + " TEXT DEFAULT \"\"," + - Checkin.Events.DATE + " INTEGER NOT NULL)"); - - db.execSQL("CREATE INDEX events_index ON " + - Checkin.Events.TABLE_NAME + " (" + - Checkin.Events.TAG + ")"); - - db.execSQL("CREATE TABLE " + Checkin.Stats.TABLE_NAME + " (" + - BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Checkin.Stats.TAG + " TEXT UNIQUE," + - Checkin.Stats.COUNT + " INTEGER DEFAULT 0," + - Checkin.Stats.SUM + " REAL DEFAULT 0.0)"); - - db.execSQL("CREATE TABLE " + Checkin.Crashes.TABLE_NAME + " (" + - BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Checkin.Crashes.DATA + " TEXT NOT NULL," + - Checkin.Crashes.LOGS + " TEXT)"); - - db.execSQL("CREATE TABLE " + Checkin.Properties.TABLE_NAME + " (" + - BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Checkin.Properties.TAG + " TEXT UNIQUE ON CONFLICT REPLACE," - + Checkin.Properties.VALUE + " TEXT DEFAULT \"\")"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int old, int version) { - db.execSQL("DROP TABLE IF EXISTS " + Checkin.Events.TABLE_NAME); - db.execSQL("DROP TABLE IF EXISTS " + Checkin.Stats.TABLE_NAME); - db.execSQL("DROP TABLE IF EXISTS " + Checkin.Crashes.TABLE_NAME); - db.execSQL("DROP TABLE IF EXISTS " + Checkin.Properties.TABLE_NAME); - onCreate(db); - } - } - - @Override public boolean onCreate() { - mOpenHelper = new OpenHelper(getContext()); - return true; - } - - @Override - public Cursor query(Uri uri, String[] select, - String where, String[] args, String sort) { - checkPermissions(uri); - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - qb.setTables(uri.getPathSegments().get(0)); - if (uri.getPathSegments().size() == 2) { - qb.appendWhere("_id=" + ContentUris.parseId(uri)); - } else if (uri.getPathSegments().size() != 1) { - throw new IllegalArgumentException("Invalid query URI: " + uri); - } - - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - Cursor cursor = qb.query(db, select, where, args, null, null, sort); - cursor.setNotificationUri(getContext().getContentResolver(), uri); - return cursor; - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - checkPermissions(uri); - if (uri.getPathSegments().size() != 1) { - throw new IllegalArgumentException("Invalid insert URI: " + uri); - } - - long id; - String table = uri.getPathSegments().get(0); - if (Checkin.Events.TABLE_NAME.equals(table)) { - id = insertEvent(values); - } else if (Checkin.Stats.TABLE_NAME.equals(table)) { - id = insertStats(values); - } else if (Checkin.Crashes.TABLE_NAME.equals(table)) { - id = insertCrash(values); - } else { - id = mOpenHelper.getWritableDatabase().insert(table, null, values); - } - - if (id < 0) { - return null; - } else { - uri = ContentUris.withAppendedId(uri, id); - getContext().getContentResolver().notifyChange(uri, null); - return uri; - } - } - - /** - * Insert an entry into the events table. - * Trims old events from the table to keep the size bounded. - * @param values to insert - * @return the row ID of the new entry - */ - private long insertEvent(ContentValues values) { - String value = values.getAsString(Checkin.Events.VALUE); - if (value != null && value.length() > EVENT_SIZE) { - // Event values are readable text, so they can be truncated. - value = value.substring(0, EVENT_SIZE - 3) + "..."; - values.put(Checkin.Events.VALUE, value); - } - - if (!values.containsKey(Checkin.Events.DATE)) { - values.put(Checkin.Events.DATE, System.currentTimeMillis()); - } - - // TODO: Make this more efficient; don't do it on every insert. - // Also, consider keeping the most recent instance of every tag, - // and possibly update a counter when events are deleted. - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.execSQL("DELETE FROM " + - Checkin.Events.TABLE_NAME + " WHERE " + - Checkin.Events._ID + " IN (SELECT " + - Checkin.Events._ID + " FROM " + - Checkin.Events.TABLE_NAME + " ORDER BY " + - Checkin.Events.DATE + " DESC LIMIT -1 OFFSET " + - (EVENT_LIMIT - 1) + ")"); - return db.insert(Checkin.Events.TABLE_NAME, null, values); - } - - /** - * Add an entry into the stats table. - * For statistics, instead of just inserting a row into the database, - * we add the count and sum values to the existing values (if any) - * for the specified tag. This must be done with a lock held, - * to avoid a race condition during the read-modify-write update. - * @param values to insert - * @return the row ID of the modified entry - */ - private long insertStats(ContentValues values) { - synchronized (mStatsLock) { - String tag = values.getAsString(Checkin.Stats.TAG); - if (tag == null) { - throw new IllegalArgumentException("Tag required:" + values); - } - - // Look for existing values with this tag. - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - Cursor cursor = db.query(false, - Checkin.Stats.TABLE_NAME, - new String[] { - Checkin.Stats._ID, - Checkin.Stats.COUNT, - Checkin.Stats.SUM - }, - Checkin.Stats.TAG + "=?", - new String[] { tag }, - null, null, null, null /* limit */); - - try { - if (cursor == null || !cursor.moveToNext()) { - // This is a new statistic, insert it directly. - return db.insert(Checkin.Stats.TABLE_NAME, null, values); - } else { - // Depend on SELECT column order to avoid getColumnIndex() - long id = cursor.getLong(0); - int count = cursor.getInt(1); - double sum = cursor.getDouble(2); - - Integer countAdd = values.getAsInteger(Checkin.Stats.COUNT); - if (countAdd != null) count += countAdd.intValue(); - - Double sumAdd = values.getAsDouble(Checkin.Stats.SUM); - if (sumAdd != null) sum += sumAdd.doubleValue(); - - if (count <= 0 && sum == 0.0) { - // Updated to nothing: delete the row! - cursor.deleteRow(); - getContext().getContentResolver().notifyChange( - ContentUris.withAppendedId(Checkin.Stats.CONTENT_URI, id), null); - return -1; - } else { - if (countAdd != null) cursor.updateInt(1, count); - if (sumAdd != null) cursor.updateDouble(2, sum); - cursor.commitUpdates(); - return id; - } - } - } finally { - // Always clean up the cursor. - if (cursor != null) cursor.close(); - } - } - } - - /** - * Add an entry into the crashes table. - * @param values to insert - * @return the row ID of the modified entry - */ - private long insertCrash(ContentValues values) { - try { - int crashSize = values.getAsString(Checkin.Crashes.DATA).length(); - if (crashSize > CRASH_SIZE) { - // The crash is too big. Don't report it, but do log a stat. - Checkin.updateStats(getContext().getContentResolver(), - Checkin.Stats.Tag.CRASHES_TRUNCATED, 1, 0.0); - throw new IllegalArgumentException("Too big: " + crashSize); - } - - // Count the number of crashes reported, even if they roll over. - Checkin.updateStats(getContext().getContentResolver(), - Checkin.Stats.Tag.CRASHES_REPORTED, 1, 0.0); - - // Trim the crashes database, if needed. - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.execSQL("DELETE FROM " + - Checkin.Crashes.TABLE_NAME + " WHERE " + - Checkin.Crashes._ID + " IN (SELECT " + - Checkin.Crashes._ID + " FROM " + - Checkin.Crashes.TABLE_NAME + " ORDER BY " + - Checkin.Crashes._ID + " DESC LIMIT -1 OFFSET " + - (CRASH_LIMIT - 1) + ")"); - - return db.insert(Checkin.Crashes.TABLE_NAME, null, values); - } catch (Throwable t) { - // To avoid an infinite crash-reporting loop, swallow the error. - Log.e("CheckinProvider", "Error inserting crash: " + t); - return -1; - } - } - - // TODO: optimize bulkInsert, especially for stats? - - @Override - public int update(Uri uri, ContentValues values, - String where, String[] args) { - checkPermissions(uri); - if (uri.getPathSegments().size() == 2) { - if (where != null && where.length() > 0) { - throw new UnsupportedOperationException( - "WHERE clause not supported for update: " + uri); - } - where = "_id=" + ContentUris.parseId(uri); - args = null; - } else if (uri.getPathSegments().size() != 1) { - throw new IllegalArgumentException("Invalid update URI: " + uri); - } - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - int count = db.update(uri.getPathSegments().get(0), values, where, args); - getContext().getContentResolver().notifyChange(uri, null); - return count; - } - - @Override - public int delete(Uri uri, String where, String[] args) { - checkPermissions(uri); - if (uri.getPathSegments().size() == 2) { - if (where != null && where.length() > 0) { - throw new UnsupportedOperationException( - "WHERE clause not supported for delete: " + uri); - } - where = "_id=" + ContentUris.parseId(uri); - args = null; - } else if (uri.getPathSegments().size() != 1) { - throw new IllegalArgumentException("Invalid delete URI: " + uri); - } - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - int count = db.delete(uri.getPathSegments().get(0), where, args); - getContext().getContentResolver().notifyChange(uri, null); - return count; - } - - @Override - public String getType(Uri uri) { - if (uri.getPathSegments().size() == 1) { - return "vnd.android.cursor.dir/" + uri.getPathSegments().get(0); - } else if (uri.getPathSegments().size() == 2) { - return "vnd.android.cursor.item/" + uri.getPathSegments().get(0); - } else { - throw new IllegalArgumentException("Invalid URI: " + uri); - } - } - - /** - * Make sure the caller has permission to the database. - * @param uri the caller is requesting access to - * @throws SecurityException if the caller is forbidden. - */ - private void checkPermissions(Uri uri) { - if (uri.getPathSegments().size() < 1) { - throw new IllegalArgumentException("Invalid query URI: " + uri); - } - - String table = uri.getPathSegments().get(0); - if (table.equals(Checkin.Properties.TABLE_NAME) && - getContext().checkCallingOrSelfPermission(PROPERTIES_PERMISSION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Cannot access checkin properties"); - } - } -} diff --git a/core/java/android/server/checkin/FallbackCheckinService.java b/core/java/android/server/checkin/FallbackCheckinService.java deleted file mode 100644 index 65921af5038b5..0000000000000 --- a/core/java/android/server/checkin/FallbackCheckinService.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.server.checkin; - -import android.os.ICheckinService; -import android.os.RemoteException; -import android.os.IParentalControlCallback; -import com.google.android.net.ParentalControlState; - -/** - * @hide - */ -public final class FallbackCheckinService extends ICheckinService.Stub { - public FallbackCheckinService() { - } - - public void reportCrashSync(byte[] crashData) throws RemoteException { - } - - public void reportCrashAsync(byte[] crashData) throws RemoteException { - } - - public void masterClear() throws RemoteException { - } - - public void getParentalControlState(IParentalControlCallback p) throws RemoteException { - ParentalControlState state = new ParentalControlState(); - state.isEnabled = false; - p.onResult(state); - } - - public void getParentalControlState(IParentalControlCallback p, String requestingApp) - throws android.os.RemoteException { - } -} diff --git a/core/java/android/server/checkin/package.html b/core/java/android/server/checkin/package.html deleted file mode 100644 index 1c9bf9dad8350..0000000000000 --- a/core/java/android/server/checkin/package.html +++ /dev/null @@ -1,5 +0,0 @@ - - - {@hide} - - diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java index c8f395e8a03c7..c18675ef13e9f 100644 --- a/core/java/android/server/search/SearchableInfo.java +++ b/core/java/android/server/search/SearchableInfo.java @@ -86,6 +86,16 @@ public final class SearchableInfo implements Parcelable { private String mSuggestProviderPackage = null; private Context mCacheActivityContext = null; // use during setup only - don't hold memory! + // Flag values for Searchable_voiceSearchMode + private static int VOICE_SEARCH_SHOW_BUTTON = 1; + private static int VOICE_SEARCH_LAUNCH_WEB_SEARCH = 2; + private static int VOICE_SEARCH_LAUNCH_RECOGNIZER = 4; + private int mVoiceSearchMode = 0; + private int mVoiceLanguageModeId; // voiceLanguageModel + private int mVoicePromptTextId; // voicePromptText + private int mVoiceLanguageId; // voiceLanguage + private int mVoiceMaxResults; // voiceMaxResults + /** * Set the default searchable activity (when none is specified). */ @@ -420,7 +430,7 @@ public final class SearchableInfo implements Parcelable { mSearchInputType = a.getInt(com.android.internal.R.styleable.Searchable_inputType, InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_SEARCH | - InputType.TYPE_TEXT_VARIATION_SEARCH_STRING); + InputType.TYPE_TEXT_VARIATION_NORMAL); setSearchModeFlags(); if (DBG_INHIBIT_SUGGESTIONS == 0) { @@ -435,6 +445,18 @@ public final class SearchableInfo implements Parcelable { mSuggestIntentData = a.getString( com.android.internal.R.styleable.Searchable_searchSuggestIntentData); } + mVoiceSearchMode = + a.getInt(com.android.internal.R.styleable.Searchable_voiceSearchMode, 0); + // TODO this didn't work - came back zero from YouTube + mVoiceLanguageModeId = + a.getResourceId(com.android.internal.R.styleable.Searchable_voiceLanguageModel, 0); + mVoicePromptTextId = + a.getResourceId(com.android.internal.R.styleable.Searchable_voicePromptText, 0); + mVoiceLanguageId = + a.getResourceId(com.android.internal.R.styleable.Searchable_voiceLanguage, 0); + mVoiceMaxResults = + a.getInt(com.android.internal.R.styleable.Searchable_voiceMaxResults, 0); + a.recycle(); // get package info for suggestions provider (if any) @@ -462,11 +484,6 @@ public final class SearchableInfo implements Parcelable { * Convert searchmode to flags. */ private void setSearchModeFlags() { - // decompose searchMode attribute - // TODO How do I reconcile these hardcoded values with the flag bits defined in - // in attrs.xml? e.g. android.R.id.filterMode = 0x010200a4 instead of just "1" - /* mFilterMode = (0 != (mSearchMode & 1)); */ - /* mQuickStart = (0 != (mSearchMode & 2)); */ mBadgeLabel = (0 != (mSearchMode & 4)); mBadgeIcon = (0 != (mSearchMode & 8)) && (mIconId != 0); mQueryRewriteFromData = (0 != (mSearchMode & 0x10)); @@ -653,6 +670,59 @@ public final class SearchableInfo implements Parcelable { return mIconId; } + /** + * @return true if android:voiceSearchMode="showVoiceSearchButton" + */ + public boolean getVoiceSearchEnabled() { + return 0 != (mVoiceSearchMode & VOICE_SEARCH_SHOW_BUTTON); + } + + /** + * @return true if android:voiceSearchMode="launchWebSearch" + */ + public boolean getVoiceSearchLaunchWebSearch() { + return 0 != (mVoiceSearchMode & VOICE_SEARCH_LAUNCH_WEB_SEARCH); + } + + /** + * @return true if android:voiceSearchMode="launchRecognizer" + */ + public boolean getVoiceSearchLaunchRecognizer() { + return 0 != (mVoiceSearchMode & VOICE_SEARCH_LAUNCH_RECOGNIZER); + } + + /** + * @return the resource Id of the language model string, if specified in the searchable + * activity's metadata, or 0 if not specified. + */ + public int getVoiceLanguageModeId() { + return mVoiceLanguageModeId; + } + + /** + * @return the resource Id of the voice prompt text string, if specified in the searchable + * activity's metadata, or 0 if not specified. + */ + public int getVoicePromptTextId() { + return mVoicePromptTextId; + } + + /** + * @return the resource Id of the spoken langauge, if specified in the searchable + * activity's metadata, or 0 if not specified. + */ + public int getVoiceLanguageId() { + return mVoiceLanguageId; + } + + /** + * @return the max results count, if specified in the searchable + * activity's metadata, or 0 if not specified. + */ + public int getVoiceMaxResults() { + return mVoiceMaxResults; + } + /** * Return the resource Id of replacement text for the "Search" button. * @@ -727,6 +797,12 @@ public final class SearchableInfo implements Parcelable { } mSuggestProviderPackage = in.readString(); + + mVoiceSearchMode = in.readInt(); + mVoiceLanguageModeId = in.readInt(); + mVoicePromptTextId = in.readInt(); + mVoiceLanguageId = in.readInt(); + mVoiceMaxResults = in.readInt(); } public int describeContents() { @@ -764,5 +840,11 @@ public final class SearchableInfo implements Parcelable { } dest.writeString(mSuggestProviderPackage); + + dest.writeInt(mVoiceSearchMode); + dest.writeInt(mVoiceLanguageModeId); + dest.writeInt(mVoicePromptTextId); + dest.writeInt(mVoiceLanguageId); + dest.writeInt(mVoiceMaxResults); } } diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index abbf8a77c31aa..987e763160de3 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -22,8 +22,6 @@ import android.content.Intent; /** * Constants for supporting speech recognition through starting an {@link Intent} - * - * @hide {pending API council review} */ public class RecognizerIntent { private RecognizerIntent() { @@ -32,7 +30,8 @@ public class RecognizerIntent { /** * Starts an activity that will prompt the user for speech and sends it through a - * speech recognizer. + * speech recognizer. The results will be returned via activity results, or forwarded + * via a PendingIntent if one is provided. * *

    Required extras: *

      @@ -41,9 +40,11 @@ public class RecognizerIntent { * *

      Optional extras: *

        - *
      • {@link Intent#EXTRA_PROMPT} + *
      • {@link #EXTRA_PROMPT} *
      • {@link #EXTRA_LANGUAGE} *
      • {@link #EXTRA_MAX_RESULTS} + *
      • {@link #EXTRA_RESULTS_PENDINGINTENT} + *
      • {@link #EXTRA_RESULTS_PENDINGINTENT_BUNDLE} *
      * *

      Result extras: @@ -56,6 +57,32 @@ public class RecognizerIntent { */ public static final String ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH"; + /** + * Starts an activity that will prompt the user for speech, sends it through a + * speech recognizer, and invokes and displays a web search result. + * + *

      Required extras: + *

        + *
      • {@link #EXTRA_LANGUAGE_MODEL} + *
      + * + *

      Optional extras: + *

        + *
      • {@link #EXTRA_PROMPT} + *
      • {@link #EXTRA_LANGUAGE} + *
      • {@link #EXTRA_MAX_RESULTS} + *
      + * + *

      Result extras: + *

        + *
      • {@link #EXTRA_RESULTS} + *
      + * + *

      NOTE: There may not be any applications installed to handle this action, so you should + * make sure to catch {@link ActivityNotFoundException}. + */ + public static final String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH"; + /** * Informs the recognizer which speech model to prefer when performing * {@link #ACTION_RECOGNIZE_SPEECH}. The recognizer uses this @@ -65,27 +92,51 @@ public class RecognizerIntent { * @see #LANGUAGE_MODEL_FREE_FORM * @see #LANGUAGE_MODEL_WEB_SEARCH */ - public static final String EXTRA_LANGUAGE_MODEL = "language_model"; + public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL"; - /** Free form speech recognition */ + /** + * Use a language model based on free-form speech recognition. This is a value to use for + * {@link #EXTRA_LANGUAGE_MODEL}. + * @see #EXTRA_LANGUAGE_MODEL + */ public static final String LANGUAGE_MODEL_FREE_FORM = "free_form"; - /** Use a language model based on web search terms */ + /** + * Use a language model based on web search terms. This is a value to use for + * {@link #EXTRA_LANGUAGE_MODEL}. + * @see #EXTRA_LANGUAGE_MODEL + */ public static final String LANGUAGE_MODEL_WEB_SEARCH = "web_search"; /** Optional text prompt to show to the user when asking them to speak. */ - public static final String EXTRA_PROMPT = "prompt"; + public static final String EXTRA_PROMPT = "android.speech.extra.PROMPT"; /** * Optional language override to inform the recognizer that it should expect speech in * a language different than the one set in the {@link java.util.Locale#getDefault()}. */ - public static final String EXTRA_LANGUAGE = "lang"; + public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE"; /** * Optional limit on the maximum number of results to return. If omitted the recognizer * will choose how many results to return. Must be an integer. */ - public static final String EXTRA_MAX_RESULTS = "max_results"; + public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS"; + + /** + * When the intent is {@link #ACTION_RECOGNIZE_SPEECH}, the speech input activity will + * return results to you via the activity results mechanism. Alternatively, if you use this + * extra to supply a PendingIntent, the results will be added to its bundle and the + * PendingIntent will be sent to its target. + */ + public static final String EXTRA_RESULTS_PENDINGINTENT = + "android.speech.extra.RESULTS_PENDINGINTENT"; + /** + * If you use {@link #EXTRA_RESULTS_PENDINGINTENT} to supply a forwarding intent, you can + * also use this extra to supply additional extras for the final intent. The search results + * will be added to this bundle, and the combined bundle will be sent to the target. + */ + public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = + "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE"; /** Result code returned when no matches are found for the given speech */ public static final int RESULT_NO_MATCH = Activity.RESULT_FIRST_USER; @@ -102,5 +153,5 @@ public class RecognizerIntent { * An ArrayList of the potential results when performing * {@link #ACTION_RECOGNIZE_SPEECH}. Only present when {@link Activity#RESULT_OK} is returned. */ - public static final String EXTRA_RESULTS = "results"; + public static final String EXTRA_RESULTS = "android.speech.extra.RESULTS"; } diff --git a/core/java/android/text/Annotation.java b/core/java/android/text/Annotation.java index a3812a80f960a..dbc290b76c035 100644 --- a/core/java/android/text/Annotation.java +++ b/core/java/android/text/Annotation.java @@ -16,20 +16,40 @@ package android.text; +import android.os.Parcel; + /** * Annotations are simple key-value pairs that are preserved across * TextView save/restore cycles and can be used to keep application-specific * data that needs to be maintained for regions of text. */ -public class Annotation { - private String mKey; - private String mValue; +public class Annotation implements ParcelableSpan { + private final String mKey; + private final String mValue; public Annotation(String key, String value) { mKey = key; mValue = value; } + public Annotation(Parcel src) { + mKey = src.readString(); + mValue = src.readString(); + } + + public int getSpanTypeId() { + return TextUtils.ANNOTATION; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mKey); + dest.writeString(mValue); + } + public String getKey() { return mKey; } diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index 90f5e4c683a17..849571485dc7a 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -700,7 +700,41 @@ class HtmlToSpannedConverter implements ContentHandler { } public void characters(char ch[], int start, int length) throws SAXException { - mSpannableStringBuilder.append(CharBuffer.wrap(ch, start, length)); + StringBuilder sb = new StringBuilder(); + + /* + * Ignore whitespace that immediately follows other whitespace; + * newlines count as spaces. + */ + + for (int i = 0; i < length; i++) { + char c = ch[i + start]; + + if (c == ' ' || c == '\n') { + char pred; + int len = sb.length(); + + if (len == 0) { + len = mSpannableStringBuilder.length(); + + if (len == 0) { + pred = '\n'; + } else { + pred = mSpannableStringBuilder.charAt(len - 1); + } + } else { + pred = sb.charAt(len - 1); + } + + if (pred != ' ' && pred != '\n') { + sb.append(' '); + } + } else { + sb.append(c); + } + } + + mSpannableStringBuilder.append(sb); } public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java index bd868340dcaff..a073cf49081c9 100644 --- a/core/java/android/text/InputType.java +++ b/core/java/android/text/InputType.java @@ -116,14 +116,22 @@ public interface InputType { /** * Flag for {@link #TYPE_CLASS_TEXT}: multiple lines of text can be - * entered into the field. + * entered into the field. If this flag is not set, the text field + * will be constrained to a single line. */ public static final int TYPE_TEXT_FLAG_MULTI_LINE = 0x00020000; + /** + * Flag for {@link #TYPE_CLASS_TEXT}: the regular text view associated + * with this should not be multi-line, but when a fullscreen input method + * is providing text it should use multiple lines if it can. + */ + public static final int TYPE_TEXT_FLAG_IME_MULTI_LINE = 0x00040000; + /** * Flag for {@link #TYPE_CLASS_TEXT}: flags any text being used as a search string */ - public static final int TYPE_TEXT_FLAG_SEARCH = 0x00040000; + public static final int TYPE_TEXT_FLAG_SEARCH = 0x00080000; // ---------------------------------------------------------------------- @@ -149,30 +157,31 @@ public interface InputType { public static final int TYPE_TEXT_VARIATION_EMAIL_SUBJECT = 0x00000030; /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering the content of - * an e-mail. + * Variation of {@link #TYPE_CLASS_TEXT}: entering a short, possibly informal + * message such as an instant message or a text message. */ - public static final int TYPE_TEXT_VARIATION_EMAIL_CONTENT = 0x00000040; + public static final int TYPE_TEXT_VARIATION_SHORT_MESSAGE = 0x00000040; + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering the content of a long, possibly + * formal message such as the body of an e-mail. + */ + public static final int TYPE_TEXT_VARIATION_LONG_MESSAGE = 0x00000050; + /** * Variation of {@link #TYPE_CLASS_TEXT}: entering the name of a person. */ - public static final int TYPE_TEXT_VARIATION_PERSON_NAME = 0x00000050; + public static final int TYPE_TEXT_VARIATION_PERSON_NAME = 0x00000060; /** * Variation of {@link #TYPE_CLASS_TEXT}: entering a postal mailing address. */ - public static final int TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x00000060; + public static final int TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x00000070; /** * Variation of {@link #TYPE_CLASS_TEXT}: entering a password. */ - public static final int TYPE_TEXT_VARIATION_PASSWORD = 0x00000070; - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering a simple text search (e.g. web search) - */ - public static final int TYPE_TEXT_VARIATION_SEARCH_STRING = 0x00000080; + public static final int TYPE_TEXT_VARIATION_PASSWORD = 0x00000080; /** * Variation of {@link #TYPE_CLASS_TEXT}: entering text inside of a web form. diff --git a/location/java/com/android/internal/location/protocol/GAddress.java b/core/java/android/text/NoCopySpan.java similarity index 52% rename from location/java/com/android/internal/location/protocol/GAddress.java rename to core/java/android/text/NoCopySpan.java index 86a3912e36068..0855c0b250c1b 100644 --- a/location/java/com/android/internal/location/protocol/GAddress.java +++ b/core/java/android/text/NoCopySpan.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2009 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. @@ -14,10 +14,18 @@ * limitations under the License. */ -package com.android.internal.location.protocol; +package android.text; -public interface GAddress { - static final int FORMATTED_ADDRESS_LINE = 1; - static final int COMPONENT = 2; +/** + * This interface should be added to a span object that should not be copied + * into a new Spenned when performing a slice or copy operation on the original + * Spanned it was placed in. + */ +public interface NoCopySpan { + /** + * Convenience equivalent for when you would just want a new Object() for + * a span but want it to be no-copy. Use this instead. + */ + public class Concrete implements NoCopySpan { + } } - diff --git a/location/java/com/android/internal/location/protocol/GAppProfile.java b/core/java/android/text/ParcelableSpan.java similarity index 53% rename from location/java/com/android/internal/location/protocol/GAppProfile.java rename to core/java/android/text/ParcelableSpan.java index e3332eb4377f1..224511a7172c2 100644 --- a/location/java/com/android/internal/location/protocol/GAppProfile.java +++ b/core/java/android/text/ParcelableSpan.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2009 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. @@ -14,13 +14,18 @@ * limitations under the License. */ -package com.android.internal.location.protocol; +package android.text; -public interface GAppProfile { - static final int APP_NAME = 1; - static final int APP_KEY = 2; - static final int REQUEST_TYPE = 3; - static final int SEARCH_TYPE = 4; - static final int SEARCH_TERM = 5; +import android.os.Parcelable; + +/** + * A special kind of Parcelable for objects that will serve as text spans. + * This can only be used by code in the framework; it is not intended for + * applications to implement their own Parcelable spans. + */ +public interface ParcelableSpan extends Parcelable { + /** + * Return a special type identifier for this span class. + */ + public abstract int getSpanTypeId(); } - diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java index 44469eced07e9..bb98bce67cee8 100644 --- a/core/java/android/text/Selection.java +++ b/core/java/android/text/Selection.java @@ -72,7 +72,7 @@ public class Selection { if (ostart != start || oend != stop) { text.setSpan(SELECTION_START, start, start, - Spanned.SPAN_POINT_POINT); + Spanned.SPAN_POINT_POINT|Spanned.SPAN_INTERMEDIATE); text.setSpan(SELECTION_END, stop, stop, Spanned.SPAN_POINT_POINT); } @@ -417,8 +417,8 @@ public class Selection { } } - private static final class START { }; - private static final class END { }; + private static final class START implements NoCopySpan { }; + private static final class END implements NoCopySpan { }; /* * Public constants diff --git a/core/java/android/text/SpanWatcher.java b/core/java/android/text/SpanWatcher.java index f99882a45226f..01e82c815ac82 100644 --- a/core/java/android/text/SpanWatcher.java +++ b/core/java/android/text/SpanWatcher.java @@ -21,7 +21,7 @@ package android.text; * will be called to notify it that other markup objects have been * added, changed, or removed. */ -public interface SpanWatcher { +public interface SpanWatcher extends NoCopySpan { /** * This method is called to notify you that the specified object * has been attached to the specified range of the text. diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 223ce2fc29927..caaafa1473976 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -70,6 +70,10 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable, Object[] spans = sp.getSpans(start, end, Object.class); for (int i = 0; i < spans.length; i++) { + if (spans[i] instanceof NoCopySpan) { + continue; + } + int st = sp.getSpanStart(spans[i]) - start; int en = sp.getSpanEnd(spans[i]) - start; int fl = sp.getSpanFlags(spans[i]); diff --git a/core/java/android/text/Spanned.java b/core/java/android/text/Spanned.java index bd0a16b7e26fb..154497dc37301 100644 --- a/core/java/android/text/Spanned.java +++ b/core/java/android/text/Spanned.java @@ -105,6 +105,14 @@ extends CharSequence */ public static final int SPAN_COMPOSING = 0x100; + /** + * This flag will be set for intermediate span changes, meaning there + * is guaranteed to be another change following it. Typically it is + * used for {@link Selection} which automatically uses this with the first + * offset it sets when updating the selection. + */ + public static final int SPAN_INTERMEDIATE = 0x200; + /** * The bits numbered SPAN_USER_SHIFT and above are available * for callers to use to store scalar data associated with their diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index ceb9f4fab43be..0fef40beb89f9 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -576,7 +576,28 @@ extends Layout if (fmbottom > fitbottom) fitbottom = fmbottom; - if (c == ' ' || c == '\t') { + /* + * From the Unicode Line Breaking Algorithm: + * (at least approximately) + * + * .,:; are class IS: breakpoints + * except when adjacent to digits + * / is class SY: a breakpoint + * except when followed by a digit. + * - is class HY: a breakpoint + * except when followed by a digit. + * + * Ideographs are class ID: breakpoints when adjacent. + */ + + if (c == ' ' || c == '\t' || + ((c == '.' || c == ',' || c == ':' || c == ';') && + (j - 1 < here || !Character.isDigit(chs[j - 1 - start])) && + (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) || + ((c == '/' || c == '-') && + (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) || + (c >= FIRST_CJK && isIdeographic(c) && + j + 1 < next && isIdeographic(chs[j + 1 - start]))) { okwidth = w; ok = j + 1; @@ -592,6 +613,11 @@ extends Layout } else if (breakOnlyAtSpaces) { if (ok != here) { // Log.e("text", "output ok " + here + " to " +ok); + + while (ok < next && chs[ok - start] == ' ') { + ok++; + } + v = out(source, here, ok, okascent, okdescent, oktop, okbottom, @@ -623,6 +649,11 @@ extends Layout } else { if (ok != here) { // Log.e("text", "output ok " + here + " to " +ok); + + while (ok < next && chs[ok - start] == ' ') { + ok++; + } + v = out(source, here, ok, okascent, okdescent, oktop, okbottom, @@ -739,6 +770,51 @@ extends Layout } } + private static final char FIRST_CJK = '\u2E80'; + /** + * Returns true if the specified character is one of those specified + * as being Ideographic (class ID) by the Unicode Line Breaking Algorithm + * (http://www.unicode.org/unicode/reports/tr14/), and is therefore OK + * to break between a pair of. + */ + private static final boolean isIdeographic(char c) { + if (c >= '\u2E80' && c <= '\u2FFF') { + return true; // CJK, KANGXI RADICALS, DESCRIPTION SYMBOLS + } + if (c == '\u3000') { + return true; // IDEOGRAPHIC SPACE + } + if (c >= '\u3040' && c <= '\u309F') { + return true; // Hiragana (except small characters) + } + if (c >= '\u30A0' && c <= '\u30FF') { + return true; // Katakana (except small characters) + } + if (c >= '\u3400' && c <= '\u4DB5') { + return true; // CJK UNIFIED IDEOGRAPHS EXTENSION A + } + if (c >= '\u4E00' && c <= '\u9FBB') { + return true; // CJK UNIFIED IDEOGRAPHS + } + if (c >= '\uF900' && c <= '\uFAD9') { + return true; // CJK COMPATIBILITY IDEOGRAPHS + } + if (c >= '\uA000' && c <= '\uA48F') { + return true; // YI SYLLABLES + } + if (c >= '\uA490' && c <= '\uA4CF') { + return true; // YI RADICALS + } + if (c >= '\uFE62' && c <= '\uFE66') { + return true; // SMALL PLUS SIGN to SMALL EQUALS SIGN + } + if (c >= '\uFF10' && c <= '\uFF19') { + return true; // WIDE DIGITS + } + + return false; + } + /* private static void dump(byte[] data, int count, String label) { if (false) { diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 405d9341181c8..5b4c3802b3ceb 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -236,6 +236,13 @@ public class TextUtils { return match; } + /** + * Create a new String object containing the given range of characters + * from the source string. This is different than simply calling + * {@link CharSequence#subSequence(int, int) CharSequence.subSequence} + * in that it does not preserve any style runs in the source sequence, + * allowing a more efficient implementation. + */ public static String substring(CharSequence source, int start, int end) { if (source instanceof String) return ((String) source).substring(start, end); @@ -447,13 +454,26 @@ public class TextUtils { /** * Returns true if a and b are equal, including if they are both null. - * + *

      Note: In platform versions 1.1 and earlier, this method only worked well if + * both the arguments were instances of String.

      * @param a first CharSequence to check * @param b second CharSequence to check * @return true if a and b are equal */ public static boolean equals(CharSequence a, CharSequence b) { - return a == b || (a != null && a.equals(b)); + if (a == b) return true; + int length; + if (a != null && b != null && (length = a.length()) == b.length()) { + if (a instanceof String && b instanceof String) { + return a.equals(b); + } else { + for (int i = 0; i < length; i++) { + if (a.charAt(i) != b.charAt(i)) return false; + } + return true; + } + } + return false; } // XXX currently this only reverses chars, not spans @@ -510,24 +530,42 @@ public class TextUtils { private int mEnd; } - private static final int ALIGNMENT_SPAN = 1; - private static final int FOREGROUND_COLOR_SPAN = 2; - private static final int RELATIVE_SIZE_SPAN = 3; - private static final int SCALE_X_SPAN = 4; - private static final int STRIKETHROUGH_SPAN = 5; - private static final int UNDERLINE_SPAN = 6; - private static final int STYLE_SPAN = 7; - private static final int BULLET_SPAN = 8; - private static final int QUOTE_SPAN = 9; - private static final int LEADING_MARGIN_SPAN = 10; - private static final int URL_SPAN = 11; - private static final int BACKGROUND_COLOR_SPAN = 12; - private static final int TYPEFACE_SPAN = 13; - private static final int SUPERSCRIPT_SPAN = 14; - private static final int SUBSCRIPT_SPAN = 15; - private static final int ABSOLUTE_SIZE_SPAN = 16; - private static final int TEXT_APPEARANCE_SPAN = 17; - private static final int ANNOTATION = 18; + /** @hide */ + public static final int ALIGNMENT_SPAN = 1; + /** @hide */ + public static final int FOREGROUND_COLOR_SPAN = 2; + /** @hide */ + public static final int RELATIVE_SIZE_SPAN = 3; + /** @hide */ + public static final int SCALE_X_SPAN = 4; + /** @hide */ + public static final int STRIKETHROUGH_SPAN = 5; + /** @hide */ + public static final int UNDERLINE_SPAN = 6; + /** @hide */ + public static final int STYLE_SPAN = 7; + /** @hide */ + public static final int BULLET_SPAN = 8; + /** @hide */ + public static final int QUOTE_SPAN = 9; + /** @hide */ + public static final int LEADING_MARGIN_SPAN = 10; + /** @hide */ + public static final int URL_SPAN = 11; + /** @hide */ + public static final int BACKGROUND_COLOR_SPAN = 12; + /** @hide */ + public static final int TYPEFACE_SPAN = 13; + /** @hide */ + public static final int SUPERSCRIPT_SPAN = 14; + /** @hide */ + public static final int SUBSCRIPT_SPAN = 15; + /** @hide */ + public static final int ABSOLUTE_SIZE_SPAN = 16; + /** @hide */ + public static final int TEXT_APPEARANCE_SPAN = 17; + /** @hide */ + public static final int ANNOTATION = 18; /** * Flatten a CharSequence and whatever styles can be copied across processes @@ -555,136 +593,10 @@ public class TextUtils { prop = ((CharacterStyle) prop).getUnderlying(); } - if (prop instanceof AlignmentSpan) { - p.writeInt(ALIGNMENT_SPAN); - p.writeString(((AlignmentSpan) prop).getAlignment().name()); - writeWhere(p, sp, o); - } - - if (prop instanceof ForegroundColorSpan) { - p.writeInt(FOREGROUND_COLOR_SPAN); - p.writeInt(((ForegroundColorSpan) prop).getForegroundColor()); - writeWhere(p, sp, o); - } - - if (prop instanceof RelativeSizeSpan) { - p.writeInt(RELATIVE_SIZE_SPAN); - p.writeFloat(((RelativeSizeSpan) prop).getSizeChange()); - writeWhere(p, sp, o); - } - - if (prop instanceof ScaleXSpan) { - p.writeInt(SCALE_X_SPAN); - p.writeFloat(((ScaleXSpan) prop).getScaleX()); - writeWhere(p, sp, o); - } - - if (prop instanceof StrikethroughSpan) { - p.writeInt(STRIKETHROUGH_SPAN); - writeWhere(p, sp, o); - } - - if (prop instanceof UnderlineSpan) { - p.writeInt(UNDERLINE_SPAN); - writeWhere(p, sp, o); - } - - if (prop instanceof StyleSpan) { - p.writeInt(STYLE_SPAN); - p.writeInt(((StyleSpan) prop).getStyle()); - writeWhere(p, sp, o); - } - - if (prop instanceof LeadingMarginSpan) { - if (prop instanceof BulletSpan) { - p.writeInt(BULLET_SPAN); - writeWhere(p, sp, o); - } else if (prop instanceof QuoteSpan) { - p.writeInt(QUOTE_SPAN); - p.writeInt(((QuoteSpan) prop).getColor()); - writeWhere(p, sp, o); - } else { - p.writeInt(LEADING_MARGIN_SPAN); - p.writeInt(((LeadingMarginSpan) prop). - getLeadingMargin(true)); - p.writeInt(((LeadingMarginSpan) prop). - getLeadingMargin(false)); - writeWhere(p, sp, o); - } - } - - if (prop instanceof URLSpan) { - p.writeInt(URL_SPAN); - p.writeString(((URLSpan) prop).getURL()); - writeWhere(p, sp, o); - } - - if (prop instanceof BackgroundColorSpan) { - p.writeInt(BACKGROUND_COLOR_SPAN); - p.writeInt(((BackgroundColorSpan) prop).getBackgroundColor()); - writeWhere(p, sp, o); - } - - if (prop instanceof TypefaceSpan) { - p.writeInt(TYPEFACE_SPAN); - p.writeString(((TypefaceSpan) prop).getFamily()); - writeWhere(p, sp, o); - } - - if (prop instanceof SuperscriptSpan) { - p.writeInt(SUPERSCRIPT_SPAN); - writeWhere(p, sp, o); - } - - if (prop instanceof SubscriptSpan) { - p.writeInt(SUBSCRIPT_SPAN); - writeWhere(p, sp, o); - } - - if (prop instanceof AbsoluteSizeSpan) { - p.writeInt(ABSOLUTE_SIZE_SPAN); - p.writeInt(((AbsoluteSizeSpan) prop).getSize()); - writeWhere(p, sp, o); - } - - if (prop instanceof TextAppearanceSpan) { - TextAppearanceSpan tas = (TextAppearanceSpan) prop; - p.writeInt(TEXT_APPEARANCE_SPAN); - - String tf = tas.getFamily(); - if (tf != null) { - p.writeInt(1); - p.writeString(tf); - } else { - p.writeInt(0); - } - - p.writeInt(tas.getTextStyle()); - p.writeInt(tas.getTextSize()); - - ColorStateList csl = tas.getTextColor(); - if (csl == null) { - p.writeInt(0); - } else { - p.writeInt(1); - csl.writeToParcel(p, parcelableFlags); - } - - csl = tas.getLinkTextColor(); - if (csl == null) { - p.writeInt(0); - } else { - p.writeInt(1); - csl.writeToParcel(p, parcelableFlags); - } - - writeWhere(p, sp, o); - } - - if (prop instanceof Annotation) { - p.writeInt(ANNOTATION); - p.writeString(((Annotation) prop).getKey()); - p.writeString(((Annotation) prop).getValue()); + if (prop instanceof ParcelableSpan) { + ParcelableSpan ps = (ParcelableSpan)prop; + p.writeInt(ps.getSpanTypeId()); + ps.writeToParcel(p, parcelableFlags); writeWhere(p, sp, o); } } @@ -707,8 +619,7 @@ public class TextUtils { } public static final Parcelable.Creator CHAR_SEQUENCE_CREATOR - = new Parcelable.Creator() - { + = new Parcelable.Creator() { /** * Read and return a new CharSequence, possibly with styles, * from the parcel. @@ -729,89 +640,75 @@ public class TextUtils { switch (kind) { case ALIGNMENT_SPAN: - readSpan(p, sp, new AlignmentSpan.Standard( - Layout.Alignment.valueOf(p.readString()))); + readSpan(p, sp, new AlignmentSpan.Standard(p)); break; case FOREGROUND_COLOR_SPAN: - readSpan(p, sp, new ForegroundColorSpan(p.readInt())); + readSpan(p, sp, new ForegroundColorSpan(p)); break; case RELATIVE_SIZE_SPAN: - readSpan(p, sp, new RelativeSizeSpan(p.readFloat())); + readSpan(p, sp, new RelativeSizeSpan(p)); break; case SCALE_X_SPAN: - readSpan(p, sp, new ScaleXSpan(p.readFloat())); + readSpan(p, sp, new ScaleXSpan(p)); break; case STRIKETHROUGH_SPAN: - readSpan(p, sp, new StrikethroughSpan()); + readSpan(p, sp, new StrikethroughSpan(p)); break; case UNDERLINE_SPAN: - readSpan(p, sp, new UnderlineSpan()); + readSpan(p, sp, new UnderlineSpan(p)); break; case STYLE_SPAN: - readSpan(p, sp, new StyleSpan(p.readInt())); + readSpan(p, sp, new StyleSpan(p)); break; case BULLET_SPAN: - readSpan(p, sp, new BulletSpan()); + readSpan(p, sp, new BulletSpan(p)); break; case QUOTE_SPAN: - readSpan(p, sp, new QuoteSpan(p.readInt())); + readSpan(p, sp, new QuoteSpan(p)); break; case LEADING_MARGIN_SPAN: - readSpan(p, sp, new LeadingMarginSpan.Standard(p.readInt(), - p.readInt())); + readSpan(p, sp, new LeadingMarginSpan.Standard(p)); break; case URL_SPAN: - readSpan(p, sp, new URLSpan(p.readString())); + readSpan(p, sp, new URLSpan(p)); break; case BACKGROUND_COLOR_SPAN: - readSpan(p, sp, new BackgroundColorSpan(p.readInt())); + readSpan(p, sp, new BackgroundColorSpan(p)); break; case TYPEFACE_SPAN: - readSpan(p, sp, new TypefaceSpan(p.readString())); + readSpan(p, sp, new TypefaceSpan(p)); break; case SUPERSCRIPT_SPAN: - readSpan(p, sp, new SuperscriptSpan()); + readSpan(p, sp, new SuperscriptSpan(p)); break; case SUBSCRIPT_SPAN: - readSpan(p, sp, new SubscriptSpan()); + readSpan(p, sp, new SubscriptSpan(p)); break; case ABSOLUTE_SIZE_SPAN: - readSpan(p, sp, new AbsoluteSizeSpan(p.readInt())); + readSpan(p, sp, new AbsoluteSizeSpan(p)); break; case TEXT_APPEARANCE_SPAN: - readSpan(p, sp, new TextAppearanceSpan( - p.readInt() != 0 - ? p.readString() - : null, - p.readInt(), // style - p.readInt(), // size - p.readInt() != 0 - ? ColorStateList.CREATOR.createFromParcel(p) - : null, - p.readInt() != 0 - ? ColorStateList.CREATOR.createFromParcel(p) - : null)); + readSpan(p, sp, new TextAppearanceSpan(p)); break; case ANNOTATION: - readSpan(p, sp, - new Annotation(p.readString(), p.readString())); + readSpan(p, sp, new Annotation(p)); break; default: diff --git a/core/java/android/text/TextWatcher.java b/core/java/android/text/TextWatcher.java index 7456b28d6ede5..bad09f2a0f0ee 100644 --- a/core/java/android/text/TextWatcher.java +++ b/core/java/android/text/TextWatcher.java @@ -20,7 +20,7 @@ package android.text; * When an object of a type is attached to an Editable, its methods will * be called when the text is changed. */ -public interface TextWatcher { +public interface TextWatcher extends NoCopySpan { /** * This method is called to notify you that, within s, * the count characters beginning at start diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 73adedf4229ae..0dc96c369b780 100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -27,6 +27,7 @@ import com.android.internal.R; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Locale; import java.util.TimeZone; import java.text.SimpleDateFormat; @@ -188,6 +189,12 @@ public class DateFormat { */ public static final char YEAR = 'y'; + + private static final Object sLocaleLock = new Object(); + private static Locale sIs24HourLocale; + private static boolean sIs24Hour; + + /** * Returns true if user preference is set to 24-hour format. * @param context the context to use for the content resolver @@ -198,20 +205,34 @@ public class DateFormat { Settings.System.TIME_12_24); if (value == null) { + Locale locale = context.getResources().getConfiguration().locale; + + synchronized (sLocaleLock) { + if (sIs24HourLocale != null && sIs24HourLocale.equals(locale)) { + return sIs24Hour; + } + } + java.text.DateFormat natural = java.text.DateFormat.getTimeInstance( - java.text.DateFormat.LONG, - context.getResources().getConfiguration().locale); + java.text.DateFormat.LONG, locale); if (natural instanceof SimpleDateFormat) { SimpleDateFormat sdf = (SimpleDateFormat) natural; String pattern = sdf.toPattern(); if (pattern.indexOf('H') >= 0) { - return true; + value = "24"; } else { - return false; + value = "12"; } + } else { + value = "12"; + } + + synchronized (sLocaleLock) { + sIs24HourLocale = locale; + sIs24Hour = !value.equals("12"); } } diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index 7457439a6a8c6..a559b9d6f43d3 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -131,6 +131,16 @@ implements MovementMethod } public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { + if (executeDown(widget, buffer, keyCode)) { + MetaKeyKeyListener.adjustMetaAfterKeypress(buffer); + MetaKeyKeyListener.resetLockedMeta(buffer); + return true; + } + + return false; + } + + private boolean executeDown(TextView widget, Spannable buffer, int keyCode) { boolean handled = false; switch (keyCode) { @@ -170,6 +180,26 @@ implements MovementMethod return false; } + public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) { + int code = event.getKeyCode(); + if (code != KeyEvent.KEYCODE_UNKNOWN + && event.getAction() == KeyEvent.ACTION_MULTIPLE) { + int repeat = event.getRepeatCount(); + boolean first = true; + boolean handled = false; + while ((--repeat) > 0) { + if (first && executeDown(view, text, code)) { + handled = true; + MetaKeyKeyListener.adjustMetaAfterKeypress(text); + MetaKeyKeyListener.resetLockedMeta(text); + } + first = false; + } + return handled; + } + return false; + } + public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) { return false; diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java index a875368834490..6df6a3ae7ab3b 100644 --- a/core/java/android/text/method/BaseKeyListener.java +++ b/core/java/android/text/method/BaseKeyListener.java @@ -18,7 +18,6 @@ package android.text.method; import android.view.KeyEvent; import android.view.View; -import android.text.InputType; import android.text.*; import android.text.method.TextKeyListener.Capitalize; import android.widget.TextView; @@ -26,7 +25,7 @@ import android.widget.TextView; public abstract class BaseKeyListener extends MetaKeyKeyListener implements KeyListener { - /* package */ static final Object OLD_SEL_START = new Object(); + /* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete(); /** * Performs the action that happens when you press the DEL key in @@ -127,5 +126,35 @@ implements KeyListener { return super.onKeyDown(view, content, keyCode, event); } + + /** + * Base implementation handles ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting + * the event's text into the content. + */ + public boolean onKeyOther(View view, Editable content, KeyEvent event) { + if (event.getAction() != KeyEvent.ACTION_MULTIPLE + || event.getKeyCode() != KeyEvent.KEYCODE_UNKNOWN) { + // Not something we are interested in. + return false; + } + + int selStart, selEnd; + + { + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + selStart = Math.min(a, b); + selEnd = Math.max(a, b); + } + + CharSequence text = event.getCharacters(); + if (text == null) { + return false; + } + + content.replace(selStart, selEnd, text); + return true; + } } diff --git a/core/java/android/text/method/KeyListener.java b/core/java/android/text/method/KeyListener.java index 4ae6191ef69e8..85948523a0095 100644 --- a/core/java/android/text/method/KeyListener.java +++ b/core/java/android/text/method/KeyListener.java @@ -65,6 +65,13 @@ public interface KeyListener { public boolean onKeyUp(View view, Editable text, int keyCode, KeyEvent event); + /** + * If the key listener wants to other kinds of key events, return true, + * otherwise return false and the caller (i.e. the widget host) + * will handle the key. + */ + public boolean onKeyOther(View view, Editable text, KeyEvent event); + /** * Remove the given shift states from the edited text. */ diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java index 92ac531ce8c74..22e9cc6d1bd15 100644 --- a/core/java/android/text/method/LinkMovementMethod.java +++ b/core/java/android/text/method/LinkMovementMethod.java @@ -252,5 +252,5 @@ extends ScrollingMovementMethod } private static LinkMovementMethod sInstance; - private static Object FROM_BELOW = new Object(); + private static Object FROM_BELOW = new NoCopySpan.Concrete(); } diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java index d5a473b0d6d92..d89fbec557ae6 100644 --- a/core/java/android/text/method/MetaKeyKeyListener.java +++ b/core/java/android/text/method/MetaKeyKeyListener.java @@ -71,10 +71,10 @@ public abstract class MetaKeyKeyListener { | META_SYM_LOCKED | META_SYM_USED | META_SYM_PRESSED | META_SYM_RELEASED; - private static final Object CAP = new Object(); - private static final Object ALT = new Object(); - private static final Object SYM = new Object(); - private static final Object SELECTING = new Object(); + private static final Object CAP = new NoCopySpan.Concrete(); + private static final Object ALT = new NoCopySpan.Concrete(); + private static final Object SYM = new NoCopySpan.Concrete(); + private static final Object SELECTING = new NoCopySpan.Concrete(); /** * Resets all meta state to inactive. @@ -283,6 +283,10 @@ public abstract class MetaKeyKeyListener { } public void clearMetaKeyState(View view, Editable content, int states) { + clearMetaKeyState(content, states); + } + + public static void clearMetaKeyState(Editable content, int states) { if ((states&META_SHIFT_ON) != 0) resetLock(content, CAP); if ((states&META_ALT_ON) != 0) resetLock(content, ALT); if ((states&META_SYM_ON) != 0) resetLock(content, SYM); diff --git a/core/java/android/text/method/MovementMethod.java b/core/java/android/text/method/MovementMethod.java index 9e37e59a2defc..29f67a1ac4449 100644 --- a/core/java/android/text/method/MovementMethod.java +++ b/core/java/android/text/method/MovementMethod.java @@ -26,6 +26,14 @@ public interface MovementMethod public void initialize(TextView widget, Spannable text); public boolean onKeyDown(TextView widget, Spannable text, int keyCode, KeyEvent event); public boolean onKeyUp(TextView widget, Spannable text, int keyCode, KeyEvent event); + + /** + * If the key listener wants to other kinds of key events, return true, + * otherwise return false and the caller (i.e. the widget host) + * will handle the key. + */ + public boolean onKeyOther(TextView view, Spannable text, KeyEvent event); + public void onTakeFocus(TextView widget, Spannable text, int direction); public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event); diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java index 348b658ebe7e7..e500faec4439c 100644 --- a/core/java/android/text/method/NumberKeyListener.java +++ b/core/java/android/text/method/NumberKeyListener.java @@ -24,7 +24,6 @@ import android.text.Selection; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.Spanned; -import android.util.SparseIntArray; /** * For numeric text entry diff --git a/core/java/android/text/method/PasswordTransformationMethod.java b/core/java/android/text/method/PasswordTransformationMethod.java index edaa836d6954a..85adabd72abb0 100644 --- a/core/java/android/text/method/PasswordTransformationMethod.java +++ b/core/java/android/text/method/PasswordTransformationMethod.java @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.view.View; import android.text.Editable; import android.text.GetChars; +import android.text.NoCopySpan; import android.text.TextUtils; import android.text.TextWatcher; import android.text.Selection; @@ -249,7 +250,8 @@ implements TransformationMethod, TextWatcher * Used to stash a reference back to the View in the Editable so we * can use it to check the settings. */ - private static class ViewReference extends WeakReference { + private static class ViewReference extends WeakReference + implements NoCopySpan { public ViewReference(View v) { super(v); } diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java index 863b2e2deacdd..0b3951755e94c 100644 --- a/core/java/android/text/method/QwertyKeyListener.java +++ b/core/java/android/text/method/QwertyKeyListener.java @@ -16,18 +16,12 @@ package android.text.method; -import android.os.Message; -import android.os.Handler; import android.text.*; import android.text.method.TextKeyListener.Capitalize; import android.util.SparseArray; -import android.util.SparseIntArray; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; -import android.widget.TextView; - -import java.util.HashMap; /** * This is the standard key listener for alphabetic input on qwerty @@ -442,7 +436,7 @@ public class QwertyKeyListener extends BaseKeyListener { return Character.toUpperCase(src.charAt(0)) + src.substring(1); } - /* package */ static class Replaced + /* package */ static class Replaced implements NoCopySpan { public Replaced(char[] text) { mText = text; diff --git a/core/java/android/text/method/ScrollingMovementMethod.java b/core/java/android/text/method/ScrollingMovementMethod.java index db470beed0a39..563ceed398241 100644 --- a/core/java/android/text/method/ScrollingMovementMethod.java +++ b/core/java/android/text/method/ScrollingMovementMethod.java @@ -144,6 +144,10 @@ implements MovementMethod } public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) { + return executeDown(widget, buffer, keyCode); + } + + private boolean executeDown(TextView widget, Spannable buffer, int keyCode) { boolean handled = false; switch (keyCode) { @@ -171,6 +175,26 @@ implements MovementMethod return false; } + public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) { + int code = event.getKeyCode(); + if (code != KeyEvent.KEYCODE_UNKNOWN + && event.getAction() == KeyEvent.ACTION_MULTIPLE) { + int repeat = event.getRepeatCount(); + boolean first = true; + boolean handled = false; + while ((--repeat) > 0) { + if (first && executeDown(view, text, code)) { + handled = true; + MetaKeyKeyListener.adjustMetaAfterKeypress(text); + MetaKeyKeyListener.resetLockedMeta(text); + } + first = false; + } + return handled; + } + return false; + } + public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) { return false; diff --git a/core/java/android/text/method/SingleLineTransformationMethod.java b/core/java/android/text/method/SingleLineTransformationMethod.java index a4fcf15566816..6a05fe4b64fd8 100644 --- a/core/java/android/text/method/SingleLineTransformationMethod.java +++ b/core/java/android/text/method/SingleLineTransformationMethod.java @@ -27,22 +27,24 @@ import android.view.View; /** * This transformation method causes any newline characters (\n) to be - * displayed as spaces instead of causing line breaks. + * displayed as spaces instead of causing line breaks, and causes + * carriage return characters (\r) to have no appearance. */ public class SingleLineTransformationMethod extends ReplacementTransformationMethod { - private static char[] ORIGINAL = new char[] { '\n' }; - private static char[] REPLACEMENT = new char[] { ' ' }; + private static char[] ORIGINAL = new char[] { '\n', '\r' }; + private static char[] REPLACEMENT = new char[] { ' ', '\uFEFF' }; /** - * The character to be replaced is \n. + * The characters to be replaced are \n and \r. */ protected char[] getOriginal() { return ORIGINAL; } /** - * The character \n is replaced with is space. + * The character \n is replaced with is space; + * the character \r is replaced with is FEFF (zero width space). */ protected char[] getReplacement() { return REPLACEMENT; diff --git a/core/java/android/text/method/TextKeyListener.java b/core/java/android/text/method/TextKeyListener.java index b1c380ab70e33..5be2a4866c4f8 100644 --- a/core/java/android/text/method/TextKeyListener.java +++ b/core/java/android/text/method/TextKeyListener.java @@ -38,10 +38,10 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher { private static TextKeyListener[] sInstance = new TextKeyListener[Capitalize.values().length * 2]; - /* package */ static final Object ACTIVE = new Object(); - /* package */ static final Object CAPPED = new Object(); - /* package */ static final Object INHIBIT_REPLACEMENT = new Object(); - /* package */ static final Object LAST_TYPED = new Object(); + /* package */ static final Object ACTIVE = new NoCopySpan.Concrete(); + /* package */ static final Object CAPPED = new NoCopySpan.Concrete(); + /* package */ static final Object INHIBIT_REPLACEMENT = new NoCopySpan.Concrete(); + /* package */ static final Object LAST_TYPED = new NoCopySpan.Concrete(); private Capitalize mAutoCap; private boolean mAutoText; @@ -140,6 +140,13 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher { return im.onKeyUp(view, content, keyCode, event); } + @Override + public boolean onKeyOther(View view, Editable content, KeyEvent event) { + KeyListener im = getKeyListener(event); + + return im.onKeyOther(view, content, event); + } + /** * Clear all the input state (autotext, autocap, multitap, undo) * from the specified Editable, going beyond Editable.clear(), which @@ -205,6 +212,10 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher { return false; } + public boolean onKeyOther(View view, Editable content, KeyEvent event) { + return false; + } + public void clearMetaKeyState(View view, Editable content, int states) { } diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java index 8b097c5b3fa25..65036ad46d01d 100644 --- a/core/java/android/text/method/Touch.java +++ b/core/java/android/text/method/Touch.java @@ -17,6 +17,7 @@ package android.text.method; import android.text.Layout; +import android.text.NoCopySpan; import android.text.Layout.Alignment; import android.text.Spannable; import android.view.MotionEvent; @@ -103,7 +104,7 @@ public class Touch { if (ds.length > 0) { if (ds[0].mFarEnough == false) { - int slop = ViewConfiguration.getTouchSlop(); + int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop(); if (Math.abs(event.getX() - ds[0].mX) >= slop || Math.abs(event.getY() - ds[0].mY) >= slop) { @@ -141,7 +142,7 @@ public class Touch { return false; } - private static class DragState { + private static class DragState implements NoCopySpan { public float mX; public float mY; public boolean mFarEnough; diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java index 8f6ed5ae30e35..484f8ce410a29 100644 --- a/core/java/android/text/style/AbsoluteSizeSpan.java +++ b/core/java/android/text/style/AbsoluteSizeSpan.java @@ -16,17 +16,35 @@ package android.text.style; -import android.graphics.Paint; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class AbsoluteSizeSpan extends MetricAffectingSpan { +public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan { - private int mSize; + private final int mSize; public AbsoluteSizeSpan(int size) { mSize = size; } + public AbsoluteSizeSpan(Parcel src) { + mSize = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.ABSOLUTE_SIZE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSize); + } + public int getSize() { return mSize; } diff --git a/core/java/android/text/style/AlignmentSpan.java b/core/java/android/text/style/AlignmentSpan.java index d51edccca1ada..b8a37daada331 100644 --- a/core/java/android/text/style/AlignmentSpan.java +++ b/core/java/android/text/style/AlignmentSpan.java @@ -16,24 +16,40 @@ package android.text.style; +import android.os.Parcel; import android.text.Layout; +import android.text.ParcelableSpan; +import android.text.TextUtils; -public interface AlignmentSpan -extends ParagraphStyle -{ +public interface AlignmentSpan extends ParagraphStyle { public Layout.Alignment getAlignment(); public static class Standard - implements AlignmentSpan - { + implements AlignmentSpan, ParcelableSpan { public Standard(Layout.Alignment align) { mAlignment = align; } + public Standard(Parcel src) { + mAlignment = Layout.Alignment.valueOf(src.readString()); + } + + public int getSpanTypeId() { + return TextUtils.ALIGNMENT_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mAlignment.name()); + } + public Layout.Alignment getAlignment() { return mAlignment; } - private Layout.Alignment mAlignment; + private final Layout.Alignment mAlignment; } } diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java index 27eda69d97348..580a3697439e2 100644 --- a/core/java/android/text/style/BackgroundColorSpan.java +++ b/core/java/android/text/style/BackgroundColorSpan.java @@ -16,16 +16,36 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class BackgroundColorSpan extends CharacterStyle implements UpdateAppearance { +public class BackgroundColorSpan extends CharacterStyle + implements UpdateAppearance, ParcelableSpan { - private int mColor; + private final int mColor; public BackgroundColorSpan(int color) { mColor = color; } + public BackgroundColorSpan(Parcel src) { + mColor = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.BACKGROUND_COLOR_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mColor); + } + public int getBackgroundColor() { return mColor; } diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java index 70c4d33a74fe1..655bd81cd493d 100644 --- a/core/java/android/text/style/BulletSpan.java +++ b/core/java/android/text/style/BulletSpan.java @@ -18,17 +18,30 @@ package android.text.style; import android.graphics.Canvas; import android.graphics.Paint; +import android.os.Parcel; import android.text.Layout; +import android.text.ParcelableSpan; import android.text.Spanned; +import android.text.TextUtils; -public class BulletSpan implements LeadingMarginSpan { +public class BulletSpan implements LeadingMarginSpan, ParcelableSpan { + private final int mGapWidth; + private final boolean mWantColor; + private final int mColor; + + private static final int BULLET_RADIUS = 3; + public static final int STANDARD_GAP_WIDTH = 2; public BulletSpan() { mGapWidth = STANDARD_GAP_WIDTH; + mWantColor = false; + mColor = 0; } public BulletSpan(int gapWidth) { mGapWidth = gapWidth; + mWantColor = false; + mColor = 0; } public BulletSpan(int gapWidth, int color) { @@ -37,6 +50,26 @@ public class BulletSpan implements LeadingMarginSpan { mColor = color; } + public BulletSpan(Parcel src) { + mGapWidth = src.readInt(); + mWantColor = src.readInt() != 0; + mColor = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.BULLET_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mGapWidth); + dest.writeInt(mWantColor ? 1 : 0); + dest.writeInt(mColor); + } + public int getLeadingMargin(boolean first) { return 2 * BULLET_RADIUS + mGapWidth; } @@ -66,11 +99,4 @@ public class BulletSpan implements LeadingMarginSpan { p.setStyle(style); } } - - private int mGapWidth; - private boolean mWantColor; - private int mColor; - - private static final int BULLET_RADIUS = 3; - public static final int STANDARD_GAP_WIDTH = 2; } diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java index 99b33818b8e43..476124d2af0b2 100644 --- a/core/java/android/text/style/ForegroundColorSpan.java +++ b/core/java/android/text/style/ForegroundColorSpan.java @@ -16,16 +16,36 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class ForegroundColorSpan extends CharacterStyle implements UpdateAppearance { +public class ForegroundColorSpan extends CharacterStyle + implements UpdateAppearance, ParcelableSpan { - private int mColor; + private final int mColor; public ForegroundColorSpan(int color) { mColor = color; } + public ForegroundColorSpan(Parcel src) { + mColor = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.FOREGROUND_COLOR_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mColor); + } + public int getForegroundColor() { return mColor; } diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java index 85a27dc55eb51..8e212e34b2329 100644 --- a/core/java/android/text/style/LeadingMarginSpan.java +++ b/core/java/android/text/style/LeadingMarginSpan.java @@ -18,7 +18,10 @@ package android.text.style; import android.graphics.Paint; import android.graphics.Canvas; +import android.os.Parcel; import android.text.Layout; +import android.text.ParcelableSpan; +import android.text.TextUtils; public interface LeadingMarginSpan extends ParagraphStyle @@ -30,9 +33,9 @@ extends ParagraphStyle CharSequence text, int start, int end, boolean first, Layout layout); - public static class Standard - implements LeadingMarginSpan - { + public static class Standard implements LeadingMarginSpan, ParcelableSpan { + private final int mFirst, mRest; + public Standard(int first, int rest) { mFirst = first; mRest = rest; @@ -42,6 +45,24 @@ extends ParagraphStyle this(every, every); } + public Standard(Parcel src) { + mFirst = src.readInt(); + mRest = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.LEADING_MARGIN_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mFirst); + dest.writeInt(mRest); + } + public int getLeadingMargin(boolean first) { return first ? mFirst : mRest; } @@ -53,7 +74,5 @@ extends ParagraphStyle boolean first, Layout layout) { ; } - - private int mFirst, mRest; } } diff --git a/core/java/android/text/style/QuoteSpan.java b/core/java/android/text/style/QuoteSpan.java index 3f4a32f98bb79..29dd2732d0f0e 100644 --- a/core/java/android/text/style/QuoteSpan.java +++ b/core/java/android/text/style/QuoteSpan.java @@ -18,26 +18,43 @@ package android.text.style; import android.graphics.Paint; import android.graphics.Canvas; -import android.graphics.RectF; +import android.os.Parcel; import android.text.Layout; +import android.text.ParcelableSpan; +import android.text.TextUtils; -public class QuoteSpan -implements LeadingMarginSpan -{ +public class QuoteSpan implements LeadingMarginSpan, ParcelableSpan { private static final int STRIPE_WIDTH = 2; private static final int GAP_WIDTH = 2; - private int mColor = 0xff0000ff; + private final int mColor; public QuoteSpan() { super(); + mColor = 0xff0000ff; } public QuoteSpan(int color) { - this(); + super(); mColor = color; } + public QuoteSpan(Parcel src) { + mColor = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.QUOTE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mColor); + } + public int getColor() { return mColor; } diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java index a8ad0769229b6..9717362e865ad 100644 --- a/core/java/android/text/style/RelativeSizeSpan.java +++ b/core/java/android/text/style/RelativeSizeSpan.java @@ -16,17 +16,35 @@ package android.text.style; -import android.graphics.Paint; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class RelativeSizeSpan extends MetricAffectingSpan { +public class RelativeSizeSpan extends MetricAffectingSpan implements ParcelableSpan { - private float mProportion; + private final float mProportion; public RelativeSizeSpan(float proportion) { mProportion = proportion; } + public RelativeSizeSpan(Parcel src) { + mProportion = src.readFloat(); + } + + public int getSpanTypeId() { + return TextUtils.RELATIVE_SIZE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(mProportion); + } + public float getSizeChange() { return mProportion; } diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java index ac9e35d04fe4b..655064b46ecea 100644 --- a/core/java/android/text/style/ScaleXSpan.java +++ b/core/java/android/text/style/ScaleXSpan.java @@ -16,17 +16,35 @@ package android.text.style; -import android.graphics.Paint; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class ScaleXSpan extends MetricAffectingSpan { +public class ScaleXSpan extends MetricAffectingSpan implements ParcelableSpan { - private float mProportion; + private final float mProportion; public ScaleXSpan(float proportion) { mProportion = proportion; } + public ScaleXSpan(Parcel src) { + mProportion = src.readFloat(); + } + + public int getSpanTypeId() { + return TextUtils.SCALE_X_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(mProportion); + } + public float getScaleX() { return mProportion; } diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java index dd430e56ffff9..b51363ac4895b 100644 --- a/core/java/android/text/style/StrikethroughSpan.java +++ b/core/java/android/text/style/StrikethroughSpan.java @@ -16,9 +16,29 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class StrikethroughSpan extends CharacterStyle implements UpdateAppearance { +public class StrikethroughSpan extends CharacterStyle + implements UpdateAppearance, ParcelableSpan { + public StrikethroughSpan() { + } + + public StrikethroughSpan(Parcel src) { + } + + public int getSpanTypeId() { + return TextUtils.STRIKETHROUGH_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + } @Override public void updateDrawState(TextPaint ds) { diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java index cc8b06c1a17a2..8e6147c8f91b6 100644 --- a/core/java/android/text/style/StyleSpan.java +++ b/core/java/android/text/style/StyleSpan.java @@ -18,7 +18,10 @@ package android.text.style; import android.graphics.Paint; import android.graphics.Typeface; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; /** * @@ -28,9 +31,9 @@ import android.text.TextPaint; * you get bold italic. You can't turn off a style from the base style. * */ -public class StyleSpan extends MetricAffectingSpan { +public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan { - private int mStyle; + private final int mStyle; /** * @@ -42,6 +45,22 @@ public class StyleSpan extends MetricAffectingSpan { mStyle = style; } + public StyleSpan(Parcel src) { + mStyle = src.readInt(); + } + + public int getSpanTypeId() { + return TextUtils.STYLE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mStyle); + } + /** * Returns the style constant defined in {@link android.graphics.Typeface}. */ diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java index 78d6ba98ca0fa..de1d8b20f0df6 100644 --- a/core/java/android/text/style/SubscriptSpan.java +++ b/core/java/android/text/style/SubscriptSpan.java @@ -16,9 +16,29 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; + +public class SubscriptSpan extends MetricAffectingSpan implements ParcelableSpan { + public SubscriptSpan() { + } + + public SubscriptSpan(Parcel src) { + } + + public int getSpanTypeId() { + return TextUtils.SUBSCRIPT_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + } -public class SubscriptSpan extends MetricAffectingSpan { @Override public void updateDrawState(TextPaint tp) { tp.baselineShift -= (int) (tp.ascent() / 2); diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java index 79be4de8e661c..285fe84e9ec74 100644 --- a/core/java/android/text/style/SuperscriptSpan.java +++ b/core/java/android/text/style/SuperscriptSpan.java @@ -16,9 +16,29 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; + +public class SuperscriptSpan extends MetricAffectingSpan implements ParcelableSpan { + public SuperscriptSpan() { + } + + public SuperscriptSpan(Parcel src) { + } + + public int getSpanTypeId() { + return TextUtils.SUPERSCRIPT_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + } -public class SuperscriptSpan extends MetricAffectingSpan { @Override public void updateDrawState(TextPaint tp) { tp.baselineShift += (int) (tp.ascent() / 2); diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java index c4ec97611d51c..de929e3b4a016 100644 --- a/core/java/android/text/style/TextAppearanceSpan.java +++ b/core/java/android/text/style/TextAppearanceSpan.java @@ -19,20 +19,22 @@ package android.text.style; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; -import android.graphics.Paint; import android.graphics.Typeface; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; /** * Sets the text color, size, style, and typeface to match a TextAppearance * resource. */ -public class TextAppearanceSpan extends MetricAffectingSpan { - private String mTypeface; - private int mStyle; - private int mTextSize; - private ColorStateList mTextColor; - private ColorStateList mTextColorLink; +public class TextAppearanceSpan extends MetricAffectingSpan implements ParcelableSpan { + private final String mTypeface; + private final int mStyle; + private final int mTextSize; + private final ColorStateList mTextColor; + private final ColorStateList mTextColorLink; /** * Uses the specified TextAppearance resource to determine the @@ -53,11 +55,13 @@ public class TextAppearanceSpan extends MetricAffectingSpan { */ public TextAppearanceSpan(Context context, int appearance, int colorList) { + ColorStateList textColor; + TypedArray a = context.obtainStyledAttributes(appearance, com.android.internal.R.styleable.TextAppearance); - mTextColor = a.getColorStateList(com.android.internal.R.styleable. + textColor = a.getColorStateList(com.android.internal.R.styleable. TextAppearance_textColor); mTextColorLink = a.getColorStateList(com.android.internal.R.styleable. TextAppearance_textColorLink); @@ -79,6 +83,10 @@ public class TextAppearanceSpan extends MetricAffectingSpan { case 3: mTypeface = "monospace"; break; + + default: + mTypeface = null; + break; } a.recycle(); @@ -87,9 +95,11 @@ public class TextAppearanceSpan extends MetricAffectingSpan { a = context.obtainStyledAttributes(com.android.internal.R.style.Theme, com.android.internal.R.styleable.Theme); - mTextColor = a.getColorStateList(colorList); + textColor = a.getColorStateList(colorList); a.recycle(); } + + mTextColor = textColor; } /** @@ -105,6 +115,48 @@ public class TextAppearanceSpan extends MetricAffectingSpan { mTextColorLink = linkColor; } + public TextAppearanceSpan(Parcel src) { + mTypeface = src.readString(); + mStyle = src.readInt(); + mTextSize = src.readInt(); + if (src.readInt() != 0) { + mTextColor = ColorStateList.CREATOR.createFromParcel(src); + } else { + mTextColor = null; + } + if (src.readInt() != 0) { + mTextColorLink = ColorStateList.CREATOR.createFromParcel(src); + } else { + mTextColorLink = null; + } + } + + public int getSpanTypeId() { + return TextUtils.TEXT_APPEARANCE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mTypeface); + dest.writeInt(mStyle); + dest.writeInt(mTextSize); + if (mTextColor != null) { + dest.writeInt(1); + mTextColor.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mTextColorLink != null) { + dest.writeInt(1); + mTextColorLink.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + } + /** * Returns the typeface family specified by this span, or null * if it does not specify one. diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java index 7519ac2b34496..f19406007111e 100644 --- a/core/java/android/text/style/TypefaceSpan.java +++ b/core/java/android/text/style/TypefaceSpan.java @@ -18,13 +18,16 @@ package android.text.style; import android.graphics.Paint; import android.graphics.Typeface; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; /** * Changes the typeface family of the text to which the span is attached. */ -public class TypefaceSpan extends MetricAffectingSpan { - private String mFamily; +public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan { + private final String mFamily; /** * @param family The font family for this typeface. Examples include @@ -34,6 +37,22 @@ public class TypefaceSpan extends MetricAffectingSpan { mFamily = family; } + public TypefaceSpan(Parcel src) { + mFamily = src.readString(); + } + + public int getSpanTypeId() { + return TextUtils.TYPEFACE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mFamily); + } + /** * Returns the font family name. */ diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java index 79809b551c9ba..f458611b453b2 100644 --- a/core/java/android/text/style/URLSpan.java +++ b/core/java/android/text/style/URLSpan.java @@ -18,17 +18,35 @@ package android.text.style; import android.content.Intent; import android.net.Uri; -import android.text.TextPaint; +import android.os.Parcel; +import android.text.ParcelableSpan; +import android.text.TextUtils; import android.view.View; -public class URLSpan extends ClickableSpan { +public class URLSpan extends ClickableSpan implements ParcelableSpan { - private String mURL; + private final String mURL; public URLSpan(String url) { mURL = url; } + public URLSpan(Parcel src) { + mURL = src.readString(); + } + + public int getSpanTypeId() { + return TextUtils.URL_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mURL); + } + public String getURL() { return mURL; } diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java index ca6f10c53c474..b0cb0e8f2c448 100644 --- a/core/java/android/text/style/UnderlineSpan.java +++ b/core/java/android/text/style/UnderlineSpan.java @@ -16,9 +16,29 @@ package android.text.style; +import android.os.Parcel; +import android.text.ParcelableSpan; import android.text.TextPaint; +import android.text.TextUtils; -public class UnderlineSpan extends CharacterStyle implements UpdateAppearance { +public class UnderlineSpan extends CharacterStyle + implements UpdateAppearance, ParcelableSpan { + public UnderlineSpan() { + } + + public UnderlineSpan(Parcel src) { + } + + public int getSpanTypeId() { + return TextUtils.UNDERLINE_SPAN; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + } @Override public void updateDrawState(TextPaint ds) { diff --git a/core/java/android/text/util/Rfc822Validator.java b/core/java/android/text/util/Rfc822Validator.java index 9f03bb01e3ba0..6a6bf698360f5 100644 --- a/core/java/android/text/util/Rfc822Validator.java +++ b/core/java/android/text/util/Rfc822Validator.java @@ -16,6 +16,7 @@ package android.text.util; +import android.text.TextUtils; import android.widget.AutoCompleteTextView; import java.util.regex.Pattern; @@ -67,7 +68,7 @@ public class Rfc822Validator implements AutoCompleteTextView.Validator { /** * @return a string in which all the characters that are illegal for the username - * part of the email address have been removed. + * or the domain name part of the email address have been removed. */ private String removeIllegalCharacters(String s) { StringBuilder result = new StringBuilder(); @@ -101,6 +102,9 @@ public class Rfc822Validator implements AutoCompleteTextView.Validator { * {@inheritDoc} */ public CharSequence fixText(CharSequence cs) { + // Return an empty string if the email address only contains spaces, \n or \t + if (TextUtils.getTrimmedLength(cs) == 0) return ""; + Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs); StringBuilder sb = new StringBuilder(); @@ -111,10 +115,10 @@ public class Rfc822Validator implements AutoCompleteTextView.Validator { // If there is no @, just append the domain of the account tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain); } else { - // Otherwise, remove everything right of the '@' and append the domain - // ("a@b" becomes "a@gmail.com"). + // Otherwise, remove the illegal characters on both sides of the '@' String fix = removeIllegalCharacters(text.substring(0, index)); - tokens[i].setAddress(fix + "@" + mDomain); + String domain = removeIllegalCharacters(text.substring(index + 1)); + tokens[i].setAddress(fix + "@" + (domain.length() != 0 ? domain : mDomain)); } sb.append(tokens[i].toString()); diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index 8fc3602e8ba96..9de4cbe80665f 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -16,12 +16,24 @@ package android.util; +import android.os.*; + /** * A structure describing general information about a display, such as its * size, density, and font scaling. */ public class DisplayMetrics { + /** + * The reference density used throughout the system. + * + * @hide Pending API council approval + */ + public static final int DEFAULT_DENSITY = 160; + + private static final int sLcdDensity = SystemProperties.getInt("ro.sf.lcd_density", + DEFAULT_DENSITY); + /** * The absolute width of the display in pixels. */ @@ -43,7 +55,9 @@ public class DisplayMetrics { * example, a 240x320 screen will have a density of 1 even if its width is * 1.8", 1.3", etc. However, if the screen resolution is increased to * 320x480 but the screen size remained 1.5"x2" then the density would be - * increased (probably to 1.5). + * increased (probably to 1.5). + * + * @see #DEFAULT_DENSITY */ public float density; /** @@ -60,7 +74,7 @@ public class DisplayMetrics { * The exact physical pixels per inch of the screen in the Y dimension. */ public float ydpi; - + public DisplayMetrics() { } @@ -76,10 +90,9 @@ public class DisplayMetrics { public void setToDefaults() { widthPixels = 0; heightPixels = 0; - density = 1; - scaledDensity = 1; - xdpi = 160; - ydpi = 160; + density = sLcdDensity / (float) DEFAULT_DENSITY; + scaledDensity = density; + xdpi = sLcdDensity; + ydpi = sLcdDensity; } } - diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index a4ee35ad2e129..d4ba9e21ac13f 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -16,9 +16,6 @@ package android.util; -import android.util.Config; -import android.util.Log; - /** * Container for a dynamically typed data value. Primarily used with * {@link android.content.res.Resources} for holding resource values. @@ -141,6 +138,16 @@ public class TypedValue { /* ------------------------------------------------------------ */ + /** + * If {@link #density} is equal to this value, then the density should be + * treated as the system's default density value: {@link DisplayMetrics#DEFAULT_DENSITY}. + * + * @hide Pending API council approval + */ + public static final int DENSITY_DEFAULT = 0; + + /* ------------------------------------------------------------ */ + /** The type held by this value, as defined by the constants here. * This tells you how to interpret the other fields in the object. */ public int type; @@ -161,7 +168,14 @@ public class TypedValue { /** If Value came from a resource, these are the configurations for which * its contents can change. */ public int changingConfigurations = -1; - + + /** + * If the Value came from a resource, this holds the corresponding pixel density. + * + * @hide Pending API council approval + * */ + public int density; + /* ------------------------------------------------------------ */ /** Return the data for this value as a float. Only use for values @@ -454,6 +468,7 @@ public class TypedValue { data = other.data; assetCookie = other.assetCookie; resourceId = other.resourceId; + density = other.density; } public String toString() diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index 4048763d042ce..15fb83903e502 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -397,7 +397,7 @@ public class FocusFinder { int numTouchables = touchables.size(); - int edgeSlop = ViewConfiguration.getEdgeSlop(); + int edgeSlop = ViewConfiguration.get(root.mContext).getScaledEdgeSlop(); Rect closestBounds = new Rect(); Rect touchableBounds = mOtherRect; diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index fc9af05944fa1..a4726894c0cd1 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -18,6 +18,7 @@ package android.view; import android.os.Handler; import android.os.Message; +import android.content.Context; /** * Detects various gestures and events using the supplied {@link MotionEvent}s. @@ -34,7 +35,6 @@ import android.os.Message; *
    */ public class GestureDetector { - /** * The listener that is used to notify when gestures occur. * If you want to listen for all the different gestures then implement @@ -112,6 +112,14 @@ public class GestureDetector { boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY); } + /** + * @hide pending API council + */ + public interface OnDoubleTapListener { + boolean onSingleTapConfirmed(MotionEvent e); + boolean onDoubleTapEvent(MotionEvent e); + } + /** * A convenience class to extend when you only want to listen for a * subset of all the gestures. This implements all methods in the @@ -144,22 +152,40 @@ public class GestureDetector { } } - private static final int TOUCH_SLOP_SQUARE = ViewConfiguration.getTouchSlop() - * ViewConfiguration.getTouchSlop(); + // TODO: ViewConfiguration + private int mBiggerTouchSlopSquare = 20 * 20; + + private int mTouchSlopSquare; + private int mDoubleTapSlopSquare; + private int mMinimumFlingVelocity; + private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); + private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout(); + // TODO make new double-tap timeout, and define its events (i.e. either time + // between down-down or time between up-down) + private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getJumpTapTimeout(); + // constants for Message.what used by GestureHandler below private static final int SHOW_PRESS = 1; private static final int LONG_PRESS = 2; + private static final int TAP = 3; private final Handler mHandler; private final OnGestureListener mListener; + private OnDoubleTapListener mDoubleTapListener; private boolean mInLongPress; private boolean mAlwaysInTapRegion; + private boolean mAlwaysInBiggerTapRegion; private MotionEvent mCurrentDownEvent; - private MotionEvent mCurrentUpEvent; - + + /** + * True when the user is still touching for the second tap (down, move, and + * up events). Can only be true if there is a double tap listener attached. + */ + private boolean mIsDoubleTapping; + private float mLastMotionY; private float mLastMotionX; @@ -189,6 +215,12 @@ public class GestureDetector { case LONG_PRESS: dispatchLongPress(); break; + + case TAP: + if (mDoubleTapListener != null) { + mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent); + } + break; default: throw new RuntimeException("Unknown message " + msg); //never @@ -203,16 +235,17 @@ public class GestureDetector { * * @param listener the listener invoked for all the callbacks, this must * not be null. - * @param handler the handler to use, this must - * not be null. + * @param handler the handler to use * * @throws NullPointerException if either {@code listener} or * {@code handler} is null. + * + * @deprecated Use {@link #GestureDetector(android.content.Context, + * android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead. */ + @Deprecated public GestureDetector(OnGestureListener listener, Handler handler) { - mHandler = new GestureHandler(handler); - mListener = listener; - init(); + this(null, listener, handler); } /** @@ -222,19 +255,84 @@ public class GestureDetector { * * @param listener the listener invoked for all the callbacks, this must * not be null. + * * @throws NullPointerException if {@code listener} is null. + * + * @deprecated Use {@link #GestureDetector(android.content.Context, + * android.view.GestureDetector.OnGestureListener)} instead. */ + @Deprecated public GestureDetector(OnGestureListener listener) { - mHandler = new GestureHandler(); - mListener = listener; - init(); + this(null, listener, null); } - private void init() { + /** + * Creates a GestureDetector with the supplied listener. + * You may only use this constructor from a UI thread (this is the usual situation). + * @see android.os.Handler#Handler() + * + * @param context the application's context + * @param listener the listener invoked for all the callbacks, this must + * not be null. + * + * @throws NullPointerException if {@code listener} is null. + */ + public GestureDetector(Context context, OnGestureListener listener) { + this(context, listener, null); + } + + /** + * Creates a GestureDetector with the supplied listener. + * You may only use this constructor from a UI thread (this is the usual situation). + * @see android.os.Handler#Handler() + * + * @param context the application's context + * @param listener the listener invoked for all the callbacks, this must + * not be null. + * @param handler the handler to use + * + * @throws NullPointerException if {@code listener} is null. + */ + public GestureDetector(Context context, OnGestureListener listener, Handler handler) { + if (handler != null) { + mHandler = new GestureHandler(handler); + } else { + mHandler = new GestureHandler(); + } + mListener = listener; + init(context); + } + + private void init(Context context) { if (mListener == null) { throw new NullPointerException("OnGestureListener must not be null"); } mIsLongpressEnabled = true; + + // Fallback to support pre-donuts releases + int touchSlop, doubleTapSlop; + if (context == null) { + //noinspection deprecation + touchSlop = ViewConfiguration.getTouchSlop(); + doubleTapSlop = ViewConfiguration.getDoubleTapSlop(); + //noinspection deprecation + mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); + } else { + final ViewConfiguration configuration = ViewConfiguration.get(context); + touchSlop = configuration.getScaledTouchSlop(); + doubleTapSlop = configuration.getScaledDoubleTapSlop(); + mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); + } + mTouchSlopSquare = touchSlop * touchSlop; + mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop; + } + + /** + * @hide pending API council + * @param onDoubleTapListener + */ + public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) { + mDoubleTapListener = onDoubleTapListener; } /** @@ -266,9 +364,6 @@ public class GestureDetector { * else false. */ public boolean onTouchEvent(MotionEvent ev) { - final long tapTime = ViewConfiguration.getTapTimeout(); - final long longpressTime = ViewConfiguration.getLongPressTimeout(); - final int touchSlop = ViewConfiguration.getTouchSlop(); final int action = ev.getAction(); final float y = ev.getY(); final float x = ev.getX(); @@ -282,19 +377,32 @@ public class GestureDetector { switch (action) { case MotionEvent.ACTION_DOWN: + if (mDoubleTapListener != null) { + mHandler.removeMessages(TAP); + if (mCurrentDownEvent != null && isConsideredDoubleTap(mCurrentDownEvent, ev)) { + // This is a second tap + mIsDoubleTapping = true; + handled = mDoubleTapListener.onDoubleTapEvent(ev); + } else { + // This is a first tap + mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); + } + } + mLastMotionX = x; mLastMotionY = y; mCurrentDownEvent = MotionEvent.obtain(ev); mAlwaysInTapRegion = true; + mAlwaysInBiggerTapRegion = true; mInLongPress = false; - + if (mIsLongpressEnabled) { mHandler.removeMessages(LONG_PRESS); mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() - + tapTime + longpressTime); + + TAP_TIMEOUT + LONGPRESS_TIMEOUT); } - mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + tapTime); - handled = mListener.onDown(ev); + mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); + handled |= mListener.onDown(ev); break; case MotionEvent.ACTION_MOVE: @@ -303,11 +411,13 @@ public class GestureDetector { } final float scrollX = mLastMotionX - x; final float scrollY = mLastMotionY - y; - if (mAlwaysInTapRegion) { + if (mIsDoubleTapping) { + handled = mDoubleTapListener.onDoubleTapEvent(ev); + } else if (mAlwaysInTapRegion) { final int deltaX = (int) (x - mCurrentDownEvent.getX()); final int deltaY = (int) (y - mCurrentDownEvent.getY()); int distance = (deltaX * deltaX) + (deltaY * deltaY); - if (distance > TOUCH_SLOP_SQUARE) { + if (distance > mTouchSlopSquare) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastMotionX = x; mLastMotionY = y; @@ -315,6 +425,9 @@ public class GestureDetector { mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); } + if (distance > mBiggerTouchSlopSquare) { + mAlwaysInBiggerTapRegion = false; + } } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastMotionX = x; @@ -323,8 +436,13 @@ public class GestureDetector { break; case MotionEvent.ACTION_UP: - mCurrentUpEvent = MotionEvent.obtain(ev); - if (mInLongPress) { + MotionEvent currentUpEvent = MotionEvent.obtain(ev); + if (mIsDoubleTapping) { + handled = mDoubleTapListener.onDoubleTapEvent(ev); + mIsDoubleTapping = false; + break; + } else if (mInLongPress) { + mHandler.removeMessages(TAP); mInLongPress = false; break; } @@ -338,9 +456,9 @@ public class GestureDetector { final float velocityY = velocityTracker.getYVelocity(); final float velocityX = velocityTracker.getXVelocity(); - if ((Math.abs(velocityY) > ViewConfiguration.getMinimumFlingVelocity()) - || (Math.abs(velocityX) > ViewConfiguration.getMinimumFlingVelocity())){ - handled = mListener.onFling(mCurrentDownEvent, mCurrentUpEvent, velocityX, velocityY); + if ((Math.abs(velocityY) > mMinimumFlingVelocity) + || (Math.abs(velocityX) > mMinimumFlingVelocity)){ + handled = mListener.onFling(mCurrentDownEvent, currentUpEvent, velocityX, velocityY); } } mVelocityTracker.recycle(); @@ -351,6 +469,7 @@ public class GestureDetector { case MotionEvent.ACTION_CANCEL: mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); + mHandler.removeMessages(TAP); mVelocityTracker.recycle(); mVelocityTracker = null; if (mInLongPress) { @@ -361,6 +480,20 @@ public class GestureDetector { return handled; } + private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent secondDown) { + if (!mAlwaysInBiggerTapRegion) { + return false; + } + + if (secondDown.getEventTime() - firstDown.getEventTime() > DOUBLE_TAP_TIMEOUT) { + return false; + } + + int deltaX = (int) firstDown.getX() - (int) secondDown.getX(); + int deltaY = (int) firstDown.getY() - (int) secondDown.getY(); + return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare); + } + private void dispatchLongPress() { mInLongPress = true; mListener.onLongPress(mCurrentDownEvent); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 40251dbbd2445..a856b24c63506 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -104,6 +104,9 @@ interface IWindowManager int getKeycodeState(int sw); int getKeycodeStateForDevice(int devid, int sw); + // Report whether the hardware supports the given keys; returns true if successful + boolean hasKeys(in int[] keycodes, inout boolean[] keyExists); + // For testing void setInTouchMode(boolean showFocus); diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java index 0347d5098155e..25958aa463143 100644 --- a/core/java/android/view/KeyCharacterMap.java +++ b/core/java/android/view/KeyCharacterMap.java @@ -18,6 +18,8 @@ package android.view; import android.text.method.MetaKeyKeyListener; import android.util.SparseIntArray; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.util.SparseArray; @@ -350,6 +352,28 @@ public class KeyCharacterMap return getKeyboardType_native(mPointer); } + /** + * Queries the framework about whether any physical keys exist on the + * device that are capable of producing the given key codes. + */ + public static boolean deviceHasKey(int keyCode) { + int[] codeArray = new int[1]; + codeArray[0] = keyCode; + boolean[] ret = deviceHasKeys(codeArray); + return ret[0]; + } + + public static boolean[] deviceHasKeys(int[] keyCodes) { + boolean[] ret = new boolean[keyCodes.length]; + IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); + try { + wm.hasKeys(keyCodes, ret); + } catch (RemoteException e) { + // no fallback; just return the empty array + } + return ret; + } + private int mPointer; private int mKeyboardDevice; diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 1575aad11059c..d5434b6a1a8bb 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -92,7 +92,7 @@ public class KeyEvent implements Parcelable { public static final int KEYCODE_SYM = 63; public static final int KEYCODE_EXPLORER = 64; public static final int KEYCODE_ENVELOPE = 65; - public static final int KEYCODE_ENTER = 66; + public static final int KEYCODE_ENTER = 66; public static final int KEYCODE_DEL = 67; public static final int KEYCODE_GRAVE = 68; public static final int KEYCODE_MINUS = 69; @@ -144,8 +144,12 @@ public class KeyEvent implements Parcelable { public static final int ACTION_UP = 1; /** * {@link #getAction} value: multiple duplicate key events have - * occurred in a row. The {#link {@link #getRepeatCount()} method returns - * the number of duplicates. + * occurred in a row, or a complex string is being delivered. If the + * key code is not {#link {@link #KEYCODE_UNKNOWN} then the + * {#link {@link #getRepeatCount()} method returns the number of times + * the given key code should be executed. + * Otherwise, if the key code {@link #KEYCODE_UNKNOWN}, then + * this is a sequence of characters as returned by {@link #getCharacters}. */ public static final int ACTION_MULTIPLE = 2; @@ -248,6 +252,7 @@ public class KeyEvent implements Parcelable { private int mFlags; private long mDownTime; private long mEventTime; + private String mCharacters; public interface Callback { /** @@ -405,6 +410,28 @@ public class KeyEvent implements Parcelable { mFlags = flags; } + /** + * Create a new key event for a string of characters. The key code, + * action, and repeat could will automatically be set to + * {@link #KEYCODE_UNKNOWN}, {@link #ACTION_MULTIPLE}, and 0 for you. + * + * @param time The time (in {@link android.os.SystemClock#uptimeMillis}) + * at which this event occured. + * @param characters The string of characters. + * @param device The device ID that generated the key event. + * @param flags The flags for this key event + */ + public KeyEvent(long time, String characters, int device, int flags) { + mDownTime = time; + mEventTime = time; + mCharacters = characters; + mAction = ACTION_MULTIPLE; + mKeyCode = KEYCODE_UNKNOWN; + mRepeatCount = 0; + mDeviceId = device; + mFlags = flags; + } + /** * Copy an existing key event, modifying its time and repeat count. * @@ -423,6 +450,7 @@ public class KeyEvent implements Parcelable { mDeviceId = origEvent.mDeviceId; mScancode = origEvent.mScancode; mFlags = origEvent.mFlags; + mCharacters = origEvent.mCharacters; } /** @@ -441,6 +469,8 @@ public class KeyEvent implements Parcelable { mDeviceId = origEvent.mDeviceId; mScancode = origEvent.mScancode; mFlags = origEvent.mFlags; + // Don't copy mCharacters, since one way or the other we'll lose it + // when changing the action. } /** @@ -580,7 +610,7 @@ public class KeyEvent implements Parcelable { /** * Retrieve the key code of the key event. This is the physical key that - * was pressed -- not the Unicode character. + * was pressed, not the Unicode character. * * @return The key code of the event. */ @@ -588,6 +618,18 @@ public class KeyEvent implements Parcelable { return mKeyCode; } + /** + * For the special case of a {@link #ACTION_MULTIPLE} event with key + * code of {@link #KEYCODE_UNKNOWN}, this is a raw string of characters + * associated with the event. In all other cases it is null. + * + * @return Returns a String of 1 or more characters associated with + * the event. + */ + public final String getCharacters() { + return mCharacters; + } + /** * Retrieve the hardware key id of this key event. These values are not * reliable and vary from device to device. @@ -772,16 +814,18 @@ public class KeyEvent implements Parcelable { if (receiver.onKeyMultiple(code, count, this)) { return true; } - mAction = ACTION_DOWN; - mRepeatCount = 0; - boolean handled = receiver.onKeyDown(code, this); - if (handled) { - mAction = ACTION_UP; - receiver.onKeyUp(code, this); + if (code != KeyEvent.KEYCODE_UNKNOWN) { + mAction = ACTION_DOWN; + mRepeatCount = 0; + boolean handled = receiver.onKeyDown(code, this); + if (handled) { + mAction = ACTION_UP; + receiver.onKeyUp(code, this); + } + mAction = ACTION_MULTIPLE; + mRepeatCount = count; + return handled; } - mAction = ACTION_MULTIPLE; - mRepeatCount = count; - return handled; } return false; } diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java index 057df92ecf93c..27b49dbb377c3 100644 --- a/core/java/android/view/TouchDelegate.java +++ b/core/java/android/view/TouchDelegate.java @@ -77,7 +77,9 @@ public class TouchDelegate { * actual extent. */ public static final int TO_RIGHT = 8; - + + private int mSlop; + /** * Constructor * @@ -87,10 +89,10 @@ public class TouchDelegate { */ public TouchDelegate(Rect bounds, View delegateView) { mBounds = bounds; - - int slop = ViewConfiguration.getTouchSlop(); + + mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop(); mSlopBounds = new Rect(bounds); - mSlopBounds.inset(-slop, -slop); + mSlopBounds.inset(-mSlop, -mSlop); mDelegateView = delegateView; } @@ -141,7 +143,7 @@ public class TouchDelegate { } else { // Offset event coordinates to be outside the target view (in case it does // something like tracking pressed state) - int slop = ViewConfiguration.getTouchSlop(); + int slop = mSlop; event.setLocation(-(slop * 2), -(slop * 2)); } handled = delegateView.dispatchTouchEvent(event); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 85f482c61cf9c..a51b5646c1576 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -60,12 +60,31 @@ import java.util.Arrays; /** *

    - * The View class represents the basic UI building block. A view + * This class represents the basic building block for user interface components. A View * occupies a rectangular area on the screen and is responsible for drawing and - * event handling. View is the base class for widgets, - * used to create interactive graphical user interfaces. + * event handling. View is the base class for widgets, which are + * used to create interactive UI components (buttons, text fields, etc.). The + * {@link android.view.ViewGroup} subclass is the base class for layouts, which + * are invisible containers that hold other Views (or other ViewGroups) and define + * their layout properties. *

    * + *
    + *

    For an introduction to using this class to develop your + * application's user interface, read the Developer Guide documentation on + * User Interface. Special topics + * include: + *
    Declaring Layout + *
    Creating Menus + *
    Common Layout Objects + *
    Binding to Data with AdapterView + *
    Handling UI Events + *
    Applying Styles and Themes + *
    Building Custom Components + *
    How Android Draws Views. + *

    + *
    + * * *

    Using Views

    *

    @@ -1308,6 +1327,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback { static final int HAS_BOUNDS = 0x00000010; /** {@hide} */ static final int DRAWN = 0x00000020; + /** + * When this flag is set, this view is running an animation on behalf of its + * children and should therefore not cancel invalidate requests, even if they + * lie outside of this view's bounds. + * + * {@hide} + */ + static final int DRAW_ANIMATION = 0x00000040; /** {@hide} */ static final int SKIP_DRAW = 0x00000080; /** {@hide} */ @@ -1353,8 +1380,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ static final int SCROLL_CONTAINER_ADDED = 0x00100000; - // Note: flag 0x00000040 is available - /** * The parent this view is attached to. * {@hide} @@ -1559,6 +1584,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { private int mNextFocusDownId = View.NO_ID; private CheckForLongPress mPendingCheckForLongPress; + private UnsetPressedState mUnsetPressedState; /** * Whether the long press's action has been invoked. The tap's action is invoked on the @@ -1898,7 +1924,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { initScrollCache(); mScrollCache.fadingEdgeLength = a.getDimensionPixelSize( - R.styleable.View_fadingEdgeLength, ViewConfiguration.getFadingEdgeLength()); + R.styleable.View_fadingEdgeLength, + ViewConfiguration.get(mContext).getScaledFadingEdgeLength()); } /** @@ -2013,36 +2040,38 @@ public class View implements Drawable.Callback, KeyEvent.Callback { mScrollCache.scrollBar = new ScrollBarDrawable(); } - mScrollCache.scrollBarSize = a.getDimensionPixelSize( + final ScrollabilityCache scrollabilityCache = mScrollCache; + + scrollabilityCache.scrollBarSize = a.getDimensionPixelSize( com.android.internal.R.styleable.View_scrollbarSize, - ViewConfiguration.getScrollBarSize()); + ViewConfiguration.get(mContext).getScaledScrollBarSize()); Drawable track = a.getDrawable(R.styleable.View_scrollbarTrackHorizontal); - mScrollCache.scrollBar.setHorizontalTrackDrawable(track); + scrollabilityCache.scrollBar.setHorizontalTrackDrawable(track); Drawable thumb = a.getDrawable(R.styleable.View_scrollbarThumbHorizontal); if (thumb != null) { - mScrollCache.scrollBar.setHorizontalThumbDrawable(thumb); + scrollabilityCache.scrollBar.setHorizontalThumbDrawable(thumb); } boolean alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawHorizontalTrack, false); if (alwaysDraw) { - mScrollCache.scrollBar.setAlwaysDrawHorizontalTrack(true); + scrollabilityCache.scrollBar.setAlwaysDrawHorizontalTrack(true); } track = a.getDrawable(R.styleable.View_scrollbarTrackVertical); - mScrollCache.scrollBar.setVerticalTrackDrawable(track); + scrollabilityCache.scrollBar.setVerticalTrackDrawable(track); thumb = a.getDrawable(R.styleable.View_scrollbarThumbVertical); if (thumb != null) { - mScrollCache.scrollBar.setVerticalThumbDrawable(thumb); + scrollabilityCache.scrollBar.setVerticalThumbDrawable(thumb); } alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawVerticalTrack, false); if (alwaysDraw) { - mScrollCache.scrollBar.setAlwaysDrawVerticalTrack(true); + scrollabilityCache.scrollBar.setAlwaysDrawVerticalTrack(true); } // Re-apply user/background padding so that scrollbar(s) get added @@ -2056,7 +2085,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ private void initScrollCache() { if (mScrollCache == null) { - mScrollCache = new ScrollabilityCache(); + mScrollCache = new ScrollabilityCache(ViewConfiguration.get(mContext)); } } @@ -2635,6 +2664,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ public void setVisibility(int visibility) { setFlags(visibility, VISIBILITY_MASK); + if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false); } /** @@ -3410,6 +3440,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } void performCollectViewAttributes(int visibility) { + //noinspection PointlessBitwiseExpression if (((visibility | mViewFlags) & (VISIBILITY_MASK | KEEP_SCREEN_ON)) == (VISIBLE | KEEP_SCREEN_ON)) { mAttachInfo.mKeepScreenOn = true; @@ -3708,10 +3739,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } } - final UnsetPressedState unsetPressedState = new UnsetPressedState(); - if (!post(unsetPressedState)) { + if (mUnsetPressedState == null) { + mUnsetPressedState = new UnsetPressedState(); + } + + if (!post(mUnsetPressedState)) { // If the post failed, unpress right now - unsetPressedState.run(); + mUnsetPressedState.run(); } } break; @@ -3734,7 +3768,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final int y = (int) event.getY(); // Be lenient about moving outside of buttons - int slop = ViewConfiguration.getTouchSlop(); + int slop = ViewConfiguration.get(mContext).getScaledTouchSlop(); if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) { // Outside button @@ -4413,14 +4447,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @see #invalidate() */ public void postInvalidate() { - // We try only with the AttachInfo because there's no point in invalidating - // if we are not attached to our window - if (mAttachInfo != null) { - Message msg = Message.obtain(); - msg.what = AttachInfo.INVALIDATE_MSG; - msg.obj = this; - mAttachInfo.mHandler.sendMessage(msg); - } + postInvalidateDelayed(0); } /** @@ -4436,16 +4463,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @see #invalidate(Rect) */ public void postInvalidate(int left, int top, int right, int bottom) { - // We try only with the AttachInfo because there's no point in invalidating - // if we are not attached to our window - if (mAttachInfo != null) { - Message msg = Message.obtain(); - msg.what = AttachInfo.INVALIDATE_RECT_MSG; - msg.obj = this; - msg.arg1 = (left << 16) | (top & 0xFFFF); - msg.arg2 = (right << 16) | (bottom & 0xFFFF); - mAttachInfo.mHandler.sendMessage(msg); - } + postInvalidateDelayed(0, left, top, right, bottom); } /** @@ -4477,16 +4495,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @param right The right coordinate of the rectangle to invalidate. * @param bottom The bottom coordinate of the rectangle to invalidate. */ - public void postInvalidateDelayed(long delayMilliseconds, int left, int top - , int right, int bottom) { + public void postInvalidateDelayed(long delayMilliseconds, int left, int top, + int right, int bottom) { + // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window if (mAttachInfo != null) { - Message msg = Message.obtain(); + final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire(); + info.target = this; + info.left = left; + info.top = top; + info.right = right; + info.bottom = bottom; + + final Message msg = Message.obtain(); msg.what = AttachInfo.INVALIDATE_RECT_MSG; - msg.obj = this; - msg.arg1 = (left << 16) | (top & 0xFFFF); - msg.arg2 = (right << 16) | (bottom & 0xFFFF); + msg.obj = info; mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds); } } @@ -4865,7 +4889,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final boolean drawHorizontalScrollBar = (viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL; final boolean drawVerticalScrollBar = - (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL; + (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL + && !isVerticalScrollBarHidden(); if (drawVerticalScrollBar || drawHorizontalScrollBar) { final int width = mRight - mLeft; @@ -4887,6 +4912,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } } } + + /** + * Override this if the vertical scrollbar needs to be hidden in a subclass, like when + * FastScroller is visible. + * @return whether to temporarily hide the vertical scrollbar + * @hide + */ + protected boolean isVerticalScrollBarHidden() { + return false; + } /** *

    Draw the horizontal scrollbar if @@ -5022,6 +5057,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } + destroyDrawingCache(); } /** @@ -5408,7 +5444,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (width <= 0 || height <= 0 || (width * height * (opaque ? 2 : 4) >= // Projected bitmap size in bytes - ViewConfiguration.getMaximumDrawingCacheSize())) { + ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { if (mDrawingCache != null) { mDrawingCache.recycle(); } @@ -5485,9 +5521,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final int restoreCount = canvas.save(); canvas.translate(-mScrollX, -mScrollY); + mPrivateFlags |= DRAWN; + // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { - mPrivateFlags |= DRAWN; if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } @@ -5616,6 +5653,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } + mPrivateFlags |= DRAWN; + /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: @@ -5656,7 +5695,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0; if (!verticalEdges && !horizontalEdges) { // Step 3, draw the content - mPrivateFlags |= DRAWN; onDraw(canvas); // Step 4, draw the children @@ -5760,7 +5798,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } // Step 3, draw the content - mPrivateFlags |= DRAWN; onDraw(canvas); // Step 4, draw the children @@ -7671,6 +7708,67 @@ public class View implements Drawable.Callback, KeyEvent.Callback { void playSoundEffect(int effectId); } + /** + * InvalidateInfo is used to post invalidate(int, int, int, int) messages + * to a Handler. This class contains the target (View) to invalidate and + * the coordinates of the dirty rectangle. + * + * For performance purposes, this class also implements a pool of up to + * POOL_LIMIT objects that get reused. This reduces memory allocations + * whenever possible. + * + * The pool is implemented as a linked list of InvalidateInfo object with + * the root pointing to the next available InvalidateInfo. If the root + * is null (i.e. when all instances from the pool have been acquired), + * then a new InvalidateInfo is created and returned to the caller. + * + * An InvalidateInfo is sent back to the pool by calling its release() + * method. If the pool is full the object is simply discarded. + * + * This implementation follows the object pool pattern used in the + * MotionEvent class. + */ + static class InvalidateInfo { + private static final int POOL_LIMIT = 10; + private static final Object sLock = new Object(); + + private static int sAcquiredCount = 0; + private static InvalidateInfo sRoot; + + private InvalidateInfo next; + + View target; + + int left; + int top; + int right; + int bottom; + + static InvalidateInfo acquire() { + synchronized (sLock) { + if (sRoot == null) { + return new InvalidateInfo(); + } + + InvalidateInfo info = sRoot; + sRoot = info.next; + sAcquiredCount--; + + return info; + } + } + + void release() { + synchronized (sLock) { + if (sAcquiredCount < POOL_LIMIT) { + sAcquiredCount++; + next = sRoot; + sRoot = this; + } + } + } + } + final IWindowSession mSession; final IWindow mWindow; @@ -7839,18 +7937,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * instances of View.

    */ private static class ScrollabilityCache { - public int fadingEdgeLength = ViewConfiguration.getFadingEdgeLength(); + public int fadingEdgeLength; - public int scrollBarSize = ViewConfiguration.getScrollBarSize(); + public int scrollBarSize; public ScrollBarDrawable scrollBar; public final Paint paint; public final Matrix matrix; public Shader shader; - private int mLastColor = 0; + private int mLastColor; + + public ScrollabilityCache(ViewConfiguration configuration) { + fadingEdgeLength = configuration.getScaledFadingEdgeLength(); + scrollBarSize = configuration.getScaledScrollBarSize(); - public ScrollabilityCache() { paint = new Paint(); matrix = new Matrix(); // use use a height of 1, and then wack the matrix each time we diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index b7110ce101ab7..7153ea1985c8b 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -16,17 +16,19 @@ package android.view; +import android.content.Context; +import android.util.DisplayMetrics; +import android.util.SparseArray; + /** * Contains methods to standard constants used in the UI for timeouts, sizes, and distances. - * */ public class ViewConfiguration { - /** * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in * pixels */ - private static final int SCROLL_BAR_SIZE = 6; + private static final int SCROLL_BAR_SIZE = 10; /** * Defines the length of the fading edges in pixels @@ -82,6 +84,11 @@ public class ViewConfiguration { */ private static final int TOUCH_SLOP = 12; + /** + * Distance between the first touch and second touch to still be considered a double tap + */ + private static final int DOUBLE_TAP_SLOP = 100; + /** * Distance a touch needs to be outside of a window's bounds for it to * count as outside for purposes of dismissing the window. @@ -97,28 +104,124 @@ public class ViewConfiguration { * The maximum size of View's drawing cache, expressed in bytes. This size * should be at least equal to the size of the screen in ARGB888 format. */ - private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // One HVGA screen, ARGB8888 + @Deprecated + private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888 /** * The coefficient of friction applied to flings/scrolls. */ private static float SCROLL_FRICTION = 0.015f; + private final int mEdgeSlop; + private final int mFadingEdgeLength; + private final int mMinimumFlingVelocity; + private final int mScrollbarSize; + private final int mTouchSlop; + private final int mDoubleTapSlop; + private final int mWindowTouchSlop; + private final int mMaximumDrawingCacheSize; + + private static final SparseArray sConfigurations = + new SparseArray(2); + + /** + * @deprecated Use {@link android.view.ViewConfiguration#get(android.content.Context)} instead. + */ + @Deprecated + public ViewConfiguration() { + mEdgeSlop = EDGE_SLOP; + mFadingEdgeLength = FADING_EDGE_LENGTH; + mMinimumFlingVelocity = MINIMUM_FLING_VELOCITY; + mScrollbarSize = SCROLL_BAR_SIZE; + mTouchSlop = TOUCH_SLOP; + mDoubleTapSlop = DOUBLE_TAP_SLOP; + mWindowTouchSlop = WINDOW_TOUCH_SLOP; + //noinspection deprecation + mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE; + } + + /** + * Creates a new configuration for the specified context. The configuration depends on + * various parameters of the context, like the dimension of the display or the density + * of the display. + * + * @param context The application context used to initialize this view configuration. + * + * @see #get(android.content.Context) + * @see android.util.DisplayMetrics + */ + private ViewConfiguration(Context context) { + final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + final float density = metrics.density; + + mEdgeSlop = (int) (density * EDGE_SLOP + 0.5f); + mFadingEdgeLength = (int) (density * FADING_EDGE_LENGTH + 0.5f); + mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f); + mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f); + mTouchSlop = (int) (density * TOUCH_SLOP + 0.5f); + mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f); + mWindowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f); + + // Size of the screen in bytes, in ARGB_8888 format + mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels; + } + + /** + * Returns a configuration for the specified context. The configuration depends on + * various parameters of the context, like the dimension of the display or the + * density of the display. + * + * @param context The application context used to initialize the view configuration. + */ + public static ViewConfiguration get(Context context) { + final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + final int density = (int) (100.0f * metrics.density); + + ViewConfiguration configuration = sConfigurations.get(density); + if (configuration == null) { + configuration = new ViewConfiguration(context); + sConfigurations.put(density, configuration); + } + + return configuration; + } + /** * @return The width of the horizontal scrollbar and the height of the vertical * scrollbar in pixels + * + * @deprecated Use {@link #getScaledScrollBarSize()} instead. */ + @Deprecated public static int getScrollBarSize() { return SCROLL_BAR_SIZE; } /** - * @return Defines the length of the fading edges in pixels + * @return The width of the horizontal scrollbar and the height of the vertical + * scrollbar in pixels */ + public int getScaledScrollBarSize() { + return mScrollbarSize; + } + + /** + * @return Defines the length of the fading edges in pixels + * + * @deprecated Use {@link #getScaledFadingEdgeLength()} instead. + */ + @Deprecated public static int getFadingEdgeLength() { return FADING_EDGE_LENGTH; } - + + /** + * @return Defines the length of the fading edges in pixels + */ + public int getScaledFadingEdgeLength() { + return mFadingEdgeLength; + } + /** * @return Defines the duration in milliseconds of the pressed state in child * components. @@ -156,32 +259,109 @@ public class ViewConfiguration { /** * @return Inset in pixels to look for touchable content when the user touches the edge of the * screen + * + * @deprecated Use {@link #getScaledEdgeSlop()} instead. */ + @Deprecated public static int getEdgeSlop() { return EDGE_SLOP; } - + + /** + * @return Inset in pixels to look for touchable content when the user touches the edge of the + * screen + */ + public int getScaledEdgeSlop() { + return mEdgeSlop; + } + /** * @return Distance a touch can wander before we think the user is scrolling in pixels + * + * @deprecated Use {@link #getScaledTouchSlop()} instead. */ + @Deprecated public static int getTouchSlop() { return TOUCH_SLOP; } + + /** + * @return Distance a touch can wander before we think the user is scrolling in pixels + */ + public int getScaledTouchSlop() { + return mTouchSlop; + } + + /** + * @return Distance between the first touch and second touch to still be + * considered a double tap + * @deprecated Use {@link #getScaledDoubleTapSlop()} instead. + * @hide The only client of this should be GestureDetector, which needs this + * for clients that still use its deprecated constructor. + */ + @Deprecated + public static int getDoubleTapSlop() { + return DOUBLE_TAP_SLOP; + } + /** + * @return Distance between the first touch and second touch to still be + * considered a double tap + * @hide pending API council + */ + public int getScaledDoubleTapSlop() { + return mDoubleTapSlop; + } + + /** + * @return Distance a touch must be outside the bounds of a window for it + * to be counted as outside the window for purposes of dismissing that + * window. + * + * @deprecated Use {@link #getScaledWindowTouchSlop()} instead. + */ + @Deprecated + public static int getWindowTouchSlop() { + return WINDOW_TOUCH_SLOP; + } + /** * @return Distance a touch must be outside the bounds of a window for it * to be counted as outside the window for purposes of dismissing that * window. */ - public static int getWindowTouchSlop() { - return WINDOW_TOUCH_SLOP; + public int getScaledWindowTouchSlop() { + return mWindowTouchSlop; } /** - * Minimum velocity to initiate a fling, as measured in pixels per second + * @return Minimum velocity to initiate a fling, as measured in pixels per second. + * + * @deprecated Use {@link #getScaledMinimumFlingVelocity()} instead. */ - public static int getMinimumFlingVelocity() { - return MINIMUM_FLING_VELOCITY; + @Deprecated + public static int getMinimumFlingVelocity() { + return MINIMUM_FLING_VELOCITY; + } + + /** + * @return Minimum velocity to initiate a fling, as measured in pixels per second. + */ + public int getScaledMinimumFlingVelocity() { + return mMinimumFlingVelocity; + } + + /** + * The maximum drawing cache size expressed in bytes. + * + * @return the maximum size of View's drawing cache expressed in bytes + * + * @deprecated Use {@link #getScaledMaximumDrawingCacheSize()} instead. + */ + @Deprecated + public static int getMaximumDrawingCacheSize() { + //noinspection deprecation + return MAXIMUM_DRAWING_CACHE_SIZE; } /** @@ -189,8 +369,8 @@ public class ViewConfiguration { * * @return the maximum size of View's drawing cache expressed in bytes */ - public static int getMaximumDrawingCacheSize() { - return MAXIMUM_DRAWING_CACHE_SIZE; + public int getScaledMaximumDrawingCacheSize() { + return mMaximumDrawingCacheSize; } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index e26a19e54e5e4..c75866245215f 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -25,6 +25,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.RectF; import android.os.Parcelable; import android.util.AttributeSet; import android.util.EventLog; @@ -74,6 +75,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // The current transformation to apply on the child being drawn private Transformation mChildTransformation; + private RectF mInvalidateRegion; // Target of Motion events private View mMotionTarget; @@ -1199,6 +1201,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } + // We will draw our child's animation, let's reset the flag + mPrivateFlags &= ~DRAW_ANIMATION; mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; boolean more = false; @@ -1328,8 +1332,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager boolean concatMatrix = false; if (a != null) { - if (!a.isInitialized()) { + if (mInvalidateRegion == null) { + mInvalidateRegion = new RectF(); + } + final RectF region = mInvalidateRegion; + + final boolean initialized = a.isInitialized(); + if (!initialized) { a.initialize(cr - cl, cb - ct, getWidth(), getHeight()); + a.initializeInvalidateRegion(cl, ct, cr, cb); child.onAnimationStart(); } @@ -1347,10 +1358,22 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager FLAG_OPTIMIZE_INVALIDATE) { mGroupFlags |= FLAG_INVALIDATE_REQUIRED; } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) { + // The child need to draw an animation, potentially offscreen, so + // make sure we do not cancel invalidate requests + mPrivateFlags |= DRAW_ANIMATION; invalidate(cl, ct, cr, cb); } } else { - mGroupFlags |= FLAG_INVALIDATE_REQUIRED; + a.getInvalidateRegion(cl, ct, cr, cb, region, transformToApply); + + // The child need to draw an animation, potentially offscreen, so + // make sure we do not cancel invalidate requests + mPrivateFlags |= DRAW_ANIMATION; + // Enlarge the invalidate region to account for rounding errors + // in Animation#getInvalidateRegion(); Using 0.5f is unfortunately + // not enough for some types of animations (e.g. scale down.) + invalidate((int) (region.left - 1.0f), (int) (region.top - 1.0f), + (int) (region.right + 1.0f), (int) (region.bottom + 1.0f)); } } } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) == @@ -1367,7 +1390,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW)) { + if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) && + (child.mPrivateFlags & DRAW_ANIMATION) == 0) { return more; } @@ -1435,10 +1459,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + // Clear the flag as early as possible to allow draw() implementations + // to call invalidate() successfully when doing animations + child.mPrivateFlags |= DRAWN; + if (hasNoCache) { // Fast path for layouts with no backgrounds if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { - child.mPrivateFlags |= DRAWN; if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } @@ -1455,7 +1482,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager cachePaint.setAlpha(255); mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE; } - child.mPrivateFlags |= DRAWN; if (ViewRoot.PROFILE_DRAWING) { EventLog.writeEvent(60003, hashCode()); } @@ -1922,8 +1948,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager LayoutAnimationController.AnimationParameters animationParams = params.layoutAnimationParameters; if (animationParams == null) { - animationParams = - new LayoutAnimationController.AnimationParameters(); + animationParams = new LayoutAnimationController.AnimationParameters(); params.layoutAnimationParameters = animationParams; } @@ -2278,8 +2303,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int[] location = attachInfo.mInvalidateChildLocation; location[CHILD_LEFT_INDEX] = child.mLeft; location[CHILD_TOP_INDEX] = child.mTop; + + // If the child is drawing an animation, we want to copy this flag onto + // ourselves and the parent to make sure the invalidate request goes + // through + final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; do { + if (drawAnimation && parent instanceof View) { + ((View) parent).mPrivateFlags |= DRAW_ANIMATION; + } parent = parent.invalidateChildInParent(location, dirty); } while (parent != null); } @@ -2307,7 +2340,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int left = mLeft; final int top = mTop; - if (dirty.intersect(0, 0, mRight - left, mBottom - top)) { + if (dirty.intersect(0, 0, mRight - left, mBottom - top) || + (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) { mPrivateFlags &= ~DRAWING_CACHE_VALID; location[CHILD_LEFT_INDEX] = left; diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 9e0289a4bedbb..4e46397bae38b 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -177,13 +177,14 @@ public final class ViewRoot extends Handler implements ViewParent, boolean mUseGL; boolean mGlWanted; + final ViewConfiguration mViewConfiguration; + /** * see {@link #playSoundEffect(int)} */ AudioManager mAudioManager; - public ViewRoot(Context context) { super(); @@ -224,6 +225,7 @@ public final class ViewRoot extends Handler implements ViewParent, mSurface = new Surface(); mAdded = false; mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this); + mViewConfiguration = ViewConfiguration.get(context); } @Override @@ -1101,6 +1103,7 @@ public final class ViewRoot extends Handler implements ViewParent, mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); canvas.translate(0, -yoff); + mView.mPrivateFlags |= View.DRAWN; mView.draw(canvas); canvas.translate(0, yoff); @@ -1139,6 +1142,8 @@ public final class ViewRoot extends Handler implements ViewParent, Canvas canvas; try { canvas = surface.lockCanvas(dirty); + // TODO: Do this in native + canvas.setDensityScale(mView.getResources().getDisplayMetrics().density); } catch (Surface.OutOfResourcesException e) { Log.e("ViewRoot", "OutOfResourcesException locking surface", e); // TODO: we should ask the window manager to do something! @@ -1175,6 +1180,7 @@ public final class ViewRoot extends Handler implements ViewParent, dirty.setEmpty(); mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); canvas.translate(0, -yoff); + mView.mPrivateFlags |= View.DRAWN; mView.draw(canvas); canvas.translate(0, yoff); @@ -1197,6 +1203,23 @@ public final class ViewRoot extends Handler implements ViewParent, if (LOCAL_LOGV) { Log.v("ViewRoot", "Surface " + surface + " unlockCanvasAndPost"); } + + } else if (mWidth == 0 || mHeight == 0) { + // This is a special case where a window dimension is 0 -- we + // normally wouldn't draw anything because we have an empty + // dirty rect, but the surface flinger may be waiting for us to + // draw the window before it stops freezing the screen, so we + // need to diddle it like this to keep it from getting stuck. + Canvas canvas; + try { + canvas = surface.lockCanvas(dirty); + } catch (Surface.OutOfResourcesException e) { + Log.e("ViewRoot", "OutOfResourcesException locking surface", e); + // TODO: we should ask the window manager to do something! + // for now we just do nothing + return; + } + surface.unlockCanvasAndPost(canvas); } if (scrolling) { @@ -1414,6 +1437,7 @@ public final class ViewRoot extends Handler implements ViewParent, public final static int FINISHED_EVENT = 1010; public final static int DISPATCH_KEY_FROM_IME = 1011; public final static int FINISH_INPUT_CONNECTION = 1012; + public final static int CHECK_FOCUS = 1013; @Override public void handleMessage(Message msg) { @@ -1422,11 +1446,9 @@ public final class ViewRoot extends Handler implements ViewParent, ((View) msg.obj).invalidate(); break; case View.AttachInfo.INVALIDATE_RECT_MSG: - int left = msg.arg1 >>> 16; - int top = msg.arg1 & 0xFFFF; - int right = msg.arg2 >>> 16; - int bottom = msg.arg2 & 0xFFFF; - ((View) msg.obj).invalidate(left, top, right, bottom); + final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; + info.target.invalidate(info.left, info.top, info.right, info.bottom); + info.release(); break; case DO_TRAVERSAL: if (mProfile) { @@ -1478,7 +1500,7 @@ public final class ViewRoot extends Handler implements ViewParent, event.offsetLocation(0, mCurScrollY); handled = mView.dispatchTouchEvent(event); if (!handled && isDown) { - int edgeSlop = ViewConfiguration.getEdgeSlop(); + int edgeSlop = mViewConfiguration.getScaledEdgeSlop(); final int edgeFlags = event.getEdgeFlags(); int direction = View.FOCUS_UP; @@ -1615,7 +1637,7 @@ public final class ViewRoot extends Handler implements ViewParent, dispatchDetachedFromWindow(); break; case DISPATCH_KEY_FROM_IME: - if (LOCAL_LOGV) Log.v( + if (true) Log.v( "ViewRoot", "Dispatching key " + msg.obj + " from IME to " + mView); deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false); @@ -1626,6 +1648,12 @@ public final class ViewRoot extends Handler implements ViewParent, imm.reportFinishInputConnection((InputConnection)msg.obj); } } break; + case CHECK_FOCUS: { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.checkFocus((View)msg.obj); + } + } break; } } @@ -2042,8 +2070,10 @@ public final class ViewRoot extends Handler implements ViewParent, } private void deliverKeyEvent(KeyEvent event, boolean sendDone) { - boolean handled = false; - handled = mView.dispatchKeyEventPreIme(event); + // If mView is null, we just consume the key event because it doesn't + // make sense to do anything else with it. + boolean handled = mView != null + ? mView.dispatchKeyEventPreIme(event) : true; if (handled) { if (sendDone) { if (LOCAL_LOGV) Log.v( @@ -2061,7 +2091,7 @@ public final class ViewRoot extends Handler implements ViewParent, if (WindowManager.LayoutParams.mayUseInputMethod( mWindowAttributes.flags)) { InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && mView != null && imm.isActive()) { + if (imm != null && mView != null) { int seq = enqueuePendingEvent(event, sendDone); if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq=" + seq + " event=" + event); @@ -2748,7 +2778,6 @@ public final class ViewRoot extends Handler implements ViewParent, synchronized (mActions) { final ArrayList actions = mActions; - final int count = actions.size(); while (actions.remove(handlerAction)) { // Keep going @@ -2776,7 +2805,20 @@ public final class ViewRoot extends Handler implements ViewParent, @Override public boolean equals(Object o) { - return action.equals(o); + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HandlerAction that = (HandlerAction) o; + + return !(action != null ? !action.equals(that.action) : that.action != null); + + } + + @Override + public int hashCode() { + int result = action != null ? action.hashCode() : 0; + result = 31 * result + (int) (delay ^ (delay >>> 32)); + return result; } } } diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index f4d0fdeda8057..a5739835321bb 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -16,18 +16,17 @@ package android.view; -import android.media.ToneGenerator; +import android.bluetooth.HeadsetBase; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; import android.media.AudioManager; import android.media.AudioService; import android.media.AudioSystem; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; +import android.media.ToneGenerator; import android.os.Handler; import android.os.Message; import android.os.Vibrator; -import android.text.TextUtils; import android.util.Config; import android.util.Log; import android.widget.ImageView; @@ -39,7 +38,7 @@ import android.widget.Toast; * Handle the volume up and down keys. * * This code really should be moved elsewhere. - * + * * @hide */ public class VolumePanel extends Handler @@ -54,7 +53,7 @@ public class VolumePanel extends Handler * PhoneWindow will implement this part. */ public static final int PLAY_SOUND_DELAY = 300; - + /** * The delay before vibrating. This small period exists so if the user is * moving to silent mode, it will not emit a short vibrate (it normally @@ -64,28 +63,30 @@ public class VolumePanel extends Handler public static final int VIBRATE_DELAY = 300; private static final int VIBRATE_DURATION = 300; - private static final int BEEP_DURATION = 150; + private static final int BEEP_DURATION = 150; private static final int MAX_VOLUME = 100; private static final int FREE_DELAY = 10000; - + private static final int MSG_VOLUME_CHANGED = 0; private static final int MSG_FREE_RESOURCES = 1; private static final int MSG_PLAY_SOUND = 2; private static final int MSG_STOP_SOUNDS = 3; private static final int MSG_VIBRATE = 4; - + private static final int RINGTONE_VOLUME_TEXT = com.android.internal.R.string.volume_ringtone; private static final int MUSIC_VOLUME_TEXT = com.android.internal.R.string.volume_music; private static final int INCALL_VOLUME_TEXT = com.android.internal.R.string.volume_call; private static final int ALARM_VOLUME_TEXT = com.android.internal.R.string.volume_alarm; private static final int UNKNOWN_VOLUME_TEXT = com.android.internal.R.string.volume_unknown; private static final int NOTIFICATION_VOLUME_TEXT = - com.android.internal.R.string.volume_notification; - + com.android.internal.R.string.volume_notification; + private static final int BLUETOOTH_INCALL_VOLUME_TEXT = + com.android.internal.R.string.volume_bluetooth_call; + protected Context mContext; private AudioManager mAudioManager; protected AudioService mAudioService; - + private final Toast mToast; private final View mView; private final TextView mMessage; @@ -117,13 +118,13 @@ public class VolumePanel extends Handler mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()]; mVibrator = new Vibrator(); } - + public void postVolumeChanged(int streamType, int flags) { if (hasMessages(MSG_VOLUME_CHANGED)) return; removeMessages(MSG_FREE_RESOURCES); obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget(); } - + /** * Override this if you have other work to do when the volume changes (for * example, vibrating, playing a sound, etc.). Make sure to call through to @@ -132,31 +133,31 @@ public class VolumePanel extends Handler protected void onVolumeChanged(int streamType, int flags) { if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")"); - + if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { onShowVolumeChanged(streamType, flags); } - + if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0) { removeMessages(MSG_PLAY_SOUND); sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY); } - + if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) { removeMessages(MSG_PLAY_SOUND); removeMessages(MSG_VIBRATE); onStopSounds(); } - + removeMessages(MSG_FREE_RESOURCES); - sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY); + sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY); } protected void onShowVolumeChanged(int streamType, int flags) { int index = mAudioService.getStreamVolume(streamType); int message = UNKNOWN_VOLUME_TEXT; int additionalMessage = 0; - + if (LOGD) { Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType + ", flags: " + flags + "), index: " + index); @@ -166,13 +167,13 @@ public class VolumePanel extends Handler int max = mAudioService.getStreamMaxVolume(streamType); switch (streamType) { - + case AudioManager.STREAM_RING: { message = RINGTONE_VOLUME_TEXT; setRingerIcon(index); break; } - + case AudioManager.STREAM_MUSIC: { message = MUSIC_VOLUME_TEXT; if (mAudioManager.isBluetoothA2dpOn()) { @@ -184,7 +185,7 @@ public class VolumePanel extends Handler } break; } - + case AudioManager.STREAM_VOICE_CALL: { /* * For in-call voice call volume, there is no inaudible volume. @@ -194,13 +195,7 @@ public class VolumePanel extends Handler index++; max++; message = INCALL_VOLUME_TEXT; - if (mAudioManager.isBluetoothScoOn()) { - additionalMessage = - com.android.internal.R.string.volume_call_hint_playing_through_bluetooth; - setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call); - } else { - setSmallIcon(index); - } + setSmallIcon(index); break; } @@ -209,12 +204,25 @@ public class VolumePanel extends Handler setSmallIcon(index); break; } - + case AudioManager.STREAM_NOTIFICATION: { message = NOTIFICATION_VOLUME_TEXT; setSmallIcon(index); break; } + + case AudioManager.STREAM_BLUETOOTH_SCO: { + /* + * For in-call voice call volume, there is no inaudible volume. + * Rescale the UI control so the progress bar doesn't go all + * the way to zero and don't show the mute icon. + */ + index++; + max++; + message = BLUETOOTH_INCALL_VOLUME_TEXT; + setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call); + break; + } } String messageString = Resources.getSystem().getString(message); @@ -228,25 +236,25 @@ public class VolumePanel extends Handler mAdditionalMessage.setVisibility(View.VISIBLE); mAdditionalMessage.setText(Resources.getSystem().getString(additionalMessage)); } - + if (max != mLevel.getMax()) { mLevel.setMax(max); } mLevel.setProgress(index); - + mToast.setView(mView); mToast.setDuration(Toast.LENGTH_SHORT); mToast.setGravity(Gravity.TOP, 0, 0); mToast.show(); - + // Do a little vibrate if applicable (only when going into vibrate mode) - if ((flags & AudioManager.FLAG_VIBRATE) != 0 && + if ((flags & AudioManager.FLAG_VIBRATE) != 0 && mAudioService.isStreamAffectedByRingerMode(streamType) && mAudioService.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE && mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) { sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY); } - + } protected void onPlaySound(int streamType, int flags) { @@ -256,7 +264,7 @@ public class VolumePanel extends Handler // Force stop right now onStopSounds(); } - + synchronized (this) { ToneGenerator toneGen = getOrCreateToneGenerator(streamType); toneGen.startTone(ToneGenerator.TONE_PROP_BEEP); @@ -266,7 +274,7 @@ public class VolumePanel extends Handler } protected void onStopSounds() { - + synchronized (this) { int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int i = numStreamTypes - 1; i >= 0; i--) { @@ -277,17 +285,17 @@ public class VolumePanel extends Handler } } } - + protected void onVibrate() { - + // Make sure we ended up in vibrate ringer mode if (mAudioService.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) { return; } - + mVibrator.vibrate(VIBRATE_DURATION); } - + /** * Lock on this VolumePanel instance as long as you use the returned ToneGenerator. */ @@ -303,13 +311,13 @@ public class VolumePanel extends Handler /** * Makes the small icon visible, and hides the large icon. - * + * * @param index The volume index, where 0 means muted. */ private void setSmallIcon(int index) { mLargeStreamIcon.setVisibility(View.GONE); mSmallStreamIcon.setVisibility(View.VISIBLE); - + mSmallStreamIcon.setImageResource(index == 0 ? com.android.internal.R.drawable.ic_volume_off_small : com.android.internal.R.drawable.ic_volume_small); @@ -317,7 +325,7 @@ public class VolumePanel extends Handler /** * Makes the large image view visible with the given icon. - * + * * @param resId The icon to display. */ private void setLargeIcon(int resId) { @@ -329,7 +337,7 @@ public class VolumePanel extends Handler /** * Makes the ringer icon visible with an icon that is chosen * based on the current ringer mode. - * + * * @param index */ private void setRingerIcon(int index) { @@ -350,13 +358,13 @@ public class VolumePanel extends Handler } mLargeStreamIcon.setImageResource(icon); } - + protected void onFreeResources() { // We'll keep the views, just ditch the cached drawable and hence // bitmaps mSmallStreamIcon.setImageDrawable(null); mLargeStreamIcon.setImageDrawable(null); - + synchronized (this) { for (int i = mToneGenerators.length - 1; i >= 0; i--) { if (mToneGenerators[i] != null) { @@ -366,26 +374,26 @@ public class VolumePanel extends Handler } } } - + @Override public void handleMessage(Message msg) { switch (msg.what) { - + case MSG_VOLUME_CHANGED: { onVolumeChanged(msg.arg1, msg.arg2); break; } - + case MSG_FREE_RESOURCES: { onFreeResources(); break; } - + case MSG_STOP_SOUNDS: { onStopSounds(); break; } - + case MSG_PLAY_SOUND: { onPlaySound(msg.arg1, msg.arg2); break; @@ -395,8 +403,8 @@ public class VolumePanel extends Handler onVibrate(); break; } - + } } - + } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index a68436b0cc15c..428de67ee80ec 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -524,6 +524,21 @@ public abstract class Window { } } + /** + * Specify custom animations to use for the window, as per + * {@link WindowManager.LayoutParams#windowAnimations + * WindowManager.LayoutParams.windowAnimations}. Providing anything besides + * 0 here will override the animations the window would + * normally retrieve from its theme. + */ + public void setWindowAnimations(int resId) { + final WindowManager.LayoutParams attrs = getAttributes(); + attrs.windowAnimations = resId; + if (mCallback != null) { + mCallback.onWindowAttributesChanged(attrs); + } + } + /** * Specify an explicit soft input mode to use for the window, as per * {@link WindowManager.LayoutParams#softInputMode diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 7e47ad1748a30..d08a6fa675586 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -16,6 +16,7 @@ package android.view; +import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; import android.os.IBinder; import android.os.Parcel; @@ -126,8 +127,6 @@ public interface WindowManager extends ViewManager { * @see #TYPE_APPLICATION_MEDIA * @see #TYPE_APPLICATION_SUB_PANEL * @see #TYPE_APPLICATION_ATTACHED_DIALOG - * @see #TYPE_INPUT_METHOD - * @see #TYPE_INPUT_METHOD_DIALOG * @see #TYPE_STATUS_BAR * @see #TYPE_SEARCH_BAR * @see #TYPE_PHONE @@ -645,6 +644,17 @@ public interface WindowManager extends ViewManager { */ public String packageName = null; + /** + * Specific orientation value for a window. + * May be any of the same values allowed + * for {@link android.content.pm.ActivityInfo#screenOrientation}. + * If not set, a default value of + * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} + * will be used. + */ + public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + + public LayoutParams() { super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); type = TYPE_APPLICATION; @@ -722,6 +732,7 @@ public interface WindowManager extends ViewManager { out.writeStrongBinder(token); out.writeString(packageName); TextUtils.writeToParcel(mTitle, out, parcelableFlags); + out.writeInt(screenOrientation); } public static final Parcelable.Creator CREATOR @@ -755,6 +766,7 @@ public interface WindowManager extends ViewManager { token = in.readStrongBinder(); packageName = in.readString(); mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + screenOrientation = in.readInt(); } public static final int LAYOUT_CHANGED = 1<<0; @@ -767,6 +779,7 @@ public interface WindowManager extends ViewManager { public static final int ALPHA_CHANGED = 1<<7; public static final int MEMORY_TYPE_CHANGED = 1<<8; public static final int SOFT_INPUT_MODE_CHANGED = 1<<9; + public static final int SCREEN_ORIENTATION_CHANGED = 1<<10; public final int copyFrom(LayoutParams o) { int changes = 0; @@ -862,6 +875,10 @@ public interface WindowManager extends ViewManager { changes |= DIM_AMOUNT_CHANGED; } + if (screenOrientation != o.screenOrientation) { + screenOrientation = o.screenOrientation; + changes |= SCREEN_ORIENTATION_CHANGED; + } return changes; } @@ -907,6 +924,10 @@ public interface WindowManager extends ViewManager { sb.append(" wanim=0x"); sb.append(Integer.toHexString(windowAnimations)); } + if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + sb.append("or="); + sb.append(screenOrientation); + } sb.append('}'); return sb.toString(); } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 6af4915f8185a..542b35fc6b8da 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -289,16 +289,18 @@ public interface WindowManagerPolicy { * Can be called by the policy to force a window to be hidden, * regardless of whether the client or window manager would like * it shown. Must be called with the window manager lock held. + * Returns true if {@link #showLw} was last called for the window. */ - public void hideLw(boolean doAnimation); + public boolean hideLw(boolean doAnimation); /** * Can be called to undo the effect of {@link #hideLw}, allowing a * window to be shown as long as the window manager and client would * also like it to be shown. Must be called with the window manager * lock held. + * Returns true if {@link #hideLw} was last called for the window. */ - public void showLw(boolean doAnimation); + public boolean showLw(boolean doAnimation); } /** No transition happening. */ @@ -735,10 +737,17 @@ public interface WindowManagerPolicy { * ActivityInfo.SCREEN_ORIENTATION_PORTRAIT}), return a surface * rotation. */ - public int rotationForOrientation(int orientation); + public int rotationForOrientation(int orientation, int lastRotation, + boolean displayEnabled); /** - * Called when the system is mostly done booting + * Called when the system is mostly done booting to dentermine whether + * the system should go into safe mode. + */ + public boolean detectSafeMode(); + + /** + * Called when the system is mostly done booting. */ public void systemReady(); diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index 92643989e6128..c96b3e5e91948 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -20,13 +20,14 @@ import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.TypedValue; +import android.graphics.RectF; /** * Abstraction for an Animation that can be applied to Views, Surfaces, or * other objects. See the {@link android.view.animation animation package * description file}. */ -public abstract class Animation { +public abstract class Animation implements Cloneable { /** * Repeat the animation indefinitely. */ @@ -174,8 +175,12 @@ public abstract class Animation { */ private int mZAdjustment; - // Indicates what was the last value returned by getTransformation() private boolean mMore = true; + private boolean mOneMoreTime = true; + + RectF mPreviousRegion = new RectF(); + Transformation mTransformation = new Transformation(); + Transformation mPreviousTransformation = new Transformation(); /** * Creates a new animation with a duration of 0ms, the default interpolator, with @@ -217,16 +222,28 @@ public abstract class Animation { a.recycle(); } + @Override + protected Animation clone() throws CloneNotSupportedException { + final Animation animation = (Animation) super.clone(); + animation.mPreviousRegion = new RectF(); + animation.mTransformation = new Transformation(); + animation.mPreviousTransformation = new Transformation(); + return animation; + } + /** * Reset the initialization state of this animation. * * @see #initialize(int, int, int, int) */ public void reset() { + mPreviousRegion.setEmpty(); + mPreviousTransformation.clear(); mInitialized = false; mCycleFlip = false; mRepeated = 0; mMore = true; + mOneMoreTime = true; } /** @@ -255,10 +272,8 @@ public abstract class Animation { * @param parentHeight Height of the animated object's parent */ public void initialize(int width, int height, int parentWidth, int parentHeight) { + reset(); mInitialized = true; - mCycleFlip = false; - mRepeated = 0; - mMore = true; } /** @@ -707,6 +722,11 @@ public abstract class Animation { } } + if (!mMore && mOneMoreTime) { + mOneMoreTime = false; + return true; + } + return mMore; } @@ -765,7 +785,54 @@ public abstract class Animation { return value; } } - + + /** + * @param left + * @param top + * @param right + * @param bottom + * @param invalidate + * @param transformation + * + * @hide + */ + public void getInvalidateRegion(int left, int top, int right, int bottom, + RectF invalidate, Transformation transformation) { + + final RectF previousRegion = mPreviousRegion; + + invalidate.set(left, top, right, bottom); + transformation.getMatrix().mapRect(invalidate); + invalidate.union(previousRegion); + + previousRegion.set(left, top, right, bottom); + transformation.getMatrix().mapRect(previousRegion); + + final Transformation tempTransformation = mTransformation; + final Transformation previousTransformation = mPreviousTransformation; + + tempTransformation.set(transformation); + transformation.set(previousTransformation); + previousTransformation.set(tempTransformation); + } + + /** + * @param left + * @param top + * @param right + * @param bottom + * + * @hide + */ + public void initializeInvalidateRegion(int left, int top, int right, int bottom) { + final RectF region = mPreviousRegion; + region.set(left, top, right, bottom); + if (mFillBefore) { + final Transformation previousTransformation = mPreviousTransformation; + applyTransformation(0.0f, previousTransformation); + } + } + /** * Utility class to parse a string description of a size. */ diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java index 688da70edcc1a..7b56f001f1ae1 100644 --- a/core/java/android/view/animation/AnimationSet.java +++ b/core/java/android/view/animation/AnimationSet.java @@ -19,6 +19,7 @@ package android.view.animation; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; +import android.graphics.RectF; import java.util.ArrayList; import java.util.List; @@ -39,6 +40,7 @@ public class AnimationSet extends Animation { private static final int PROPERTY_SHARE_INTERPOLATOR_MASK = 0x10; private static final int PROPERTY_DURATION_MASK = 0x20; private static final int PROPERTY_MORPH_MATRIX_MASK = 0x40; + private static final int PROPERTY_CHANGE_BOUNDS_MASK = 0x80; private int mFlags = 0; @@ -82,6 +84,22 @@ public class AnimationSet extends Animation { init(); } + @Override + protected AnimationSet clone() throws CloneNotSupportedException { + final AnimationSet animation = (AnimationSet) super.clone(); + animation.mTempTransformation = new Transformation(); + animation.mAnimations = new ArrayList(); + + final int count = mAnimations.size(); + final ArrayList animations = mAnimations; + + for (int i = 0; i < count; i++) { + animation.mAnimations.add(animations.get(i).clone()); + } + + return animation; + } + private void setFlag(int mask, boolean value) { if (value) { mFlags |= mask; @@ -145,6 +163,11 @@ public class AnimationSet extends Animation { mFlags |= PROPERTY_MORPH_MATRIX_MASK; } + boolean changeBounds = (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == 0; + if (changeBounds && a.willChangeTransformationMatrix()) { + mFlags |= PROPERTY_CHANGE_BOUNDS_MASK; + } + if (mAnimations.size() == 1) { mDuration = a.getStartOffset() + a.getDuration(); mLastEnd = mStartOffset + mDuration; @@ -239,7 +262,54 @@ public class AnimationSet extends Animation { } return duration; } - + + /** + * @hide + */ + public void getInvalidateRegion(int left, int top, int right, int bottom, + RectF invalidate, Transformation transformation) { + + final RectF previousRegion = mPreviousRegion; + + invalidate.set(left, top, right, bottom); + transformation.getMatrix().mapRect(invalidate); + invalidate.union(previousRegion); + + previousRegion.set(left, top, right, bottom); + transformation.getMatrix().mapRect(previousRegion); + + final Transformation tempTransformation = mTransformation; + final Transformation previousTransformation = mPreviousTransformation; + + tempTransformation.set(transformation); + transformation.set(previousTransformation); + previousTransformation.set(tempTransformation); + } + + /** + * @hide + */ + public void initializeInvalidateRegion(int left, int top, int right, int bottom) { + final RectF region = mPreviousRegion; + region.set(left, top, right, bottom); + + if (mFillBefore) { + final int count = mAnimations.size(); + final ArrayList animations = mAnimations; + final Transformation temp = mTempTransformation; + + final Transformation previousTransformation = mPreviousTransformation; + + for (int i = count - 1; i >= 0; --i) { + final Animation a = animations.get(i); + + temp.clear(); + a.applyTransformation(0.0f, temp); + previousTransformation.compose(temp); + } + } + } + /** * The transformation of an animation set is the concatenation of all of its * component animations. @@ -313,7 +383,7 @@ public class AnimationSet extends Animation { == PROPERTY_SHARE_INTERPOLATOR_MASK; boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK) == PROPERTY_START_OFFSET_MASK; - + if (shareInterpolator) { ensureInterpolator(); } @@ -327,7 +397,13 @@ public class AnimationSet extends Animation { final int repeatMode = mRepeatMode; final Interpolator interpolator = mInterpolator; final long startOffset = mStartOffset; - + + + long[] storedOffsets = mStoredOffsets; + if (storedOffsets == null || storedOffsets.length != count) { + storedOffsets = mStoredOffsets = new long[count]; + } + for (int i = 0; i < count; i++) { Animation a = children.get(i); if (durationSet) { @@ -346,42 +422,36 @@ public class AnimationSet extends Animation { a.setInterpolator(interpolator); } if (startOffsetSet) { - a.setStartOffset(startOffset); + long offset = a.getStartOffset(); + a.setStartOffset(offset + startOffset); + storedOffsets[i] = offset; } a.initialize(width, height, parentWidth, parentHeight); } } - /** - * @hide - * @param startOffset the startOffset to add to the children's startOffset - */ - void saveChildrenStartOffset(long startOffset) { - final ArrayList children = mAnimations; - final int count = children.size(); - long[] storedOffsets = mStoredOffsets = new long[count]; - - for (int i = 0; i < count; i++) { - Animation animation = children.get(i); - long offset = animation.getStartOffset(); - animation.setStartOffset(offset + startOffset); - storedOffsets[i] = offset; - } + @Override + public void reset() { + super.reset(); + restoreChildrenStartOffset(); } /** * @hide */ void restoreChildrenStartOffset() { + final long[] offsets = mStoredOffsets; + if (offsets == null) return; + final ArrayList children = mAnimations; final int count = children.size(); - final long[] offsets = mStoredOffsets; + for (int i = 0; i < count; i++) { children.get(i).setStartOffset(offsets[i]); } } - + /** * @return All the child animations in this AnimationSet. Note that * this may include other AnimationSets, which are not expanded. @@ -394,4 +464,9 @@ public class AnimationSet extends Animation { public boolean willChangeTransformationMatrix() { return (mFlags & PROPERTY_MORPH_MATRIX_MASK) == PROPERTY_MORPH_MATRIX_MASK; } + + @Override + public boolean willChangeBounds() { + return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK; + } } diff --git a/core/java/android/view/animation/LayoutAnimationController.java b/core/java/android/view/animation/LayoutAnimationController.java index 9cfa8d7fc041c..882e738eaa119 100644 --- a/core/java/android/view/animation/LayoutAnimationController.java +++ b/core/java/android/view/animation/LayoutAnimationController.java @@ -318,9 +318,16 @@ public class LayoutAnimationController { * @see #getDelayForView(android.view.View) */ public final Animation getAnimationForView(View view) { - final long delay = getDelayForView(view); + final long delay = getDelayForView(view) + mAnimation.getStartOffset(); mMaxDelay = Math.max(mMaxDelay, delay); - return new DelayedAnimation(delay, mAnimation); + + try { + final Animation animation = mAnimation.clone(); + animation.setStartOffset(delay); + return animation; + } catch (CloneNotSupportedException e) { + return null; + } } /** @@ -425,149 +432,4 @@ public class LayoutAnimationController { */ public int index; } - - /** - * Encapsulates an animation and delays its start offset by a specified - * amount. This allows to reuse the same base animation for various views - * and get the effect of running multiple instances of the animation at - * different times. - */ - private static class DelayedAnimation extends Animation { - private final long mDelay; - private final Animation mAnimation; - - /** - * Creates a new delayed animation that will delay the controller's - * animation by the specified delay in milliseconds. - * - * @param delay the delay in milliseconds by which to offset the - * @param animation the animation to delay - */ - private DelayedAnimation(long delay, Animation animation) { - mDelay = delay; - mAnimation = animation; - } - - @Override - public boolean isInitialized() { - return mAnimation.isInitialized(); - } - - @Override - public void initialize(int width, int height, int parentWidth, int parentHeight) { - mAnimation.initialize(width, height, parentWidth, parentHeight); - } - - @Override - public void reset() { - mAnimation.reset(); - } - - @Override - public boolean getTransformation(long currentTime, Transformation outTransformation) { - final long oldOffset = mAnimation.getStartOffset(); - final boolean isSet = mAnimation instanceof AnimationSet; - if (isSet) { - AnimationSet set = ((AnimationSet) mAnimation); - set.saveChildrenStartOffset(mDelay); - } - mAnimation.setStartOffset(oldOffset + mDelay); - - boolean result = mAnimation.getTransformation(currentTime, - outTransformation); - - if (isSet) { - AnimationSet set = ((AnimationSet) mAnimation); - set.restoreChildrenStartOffset(); - } - mAnimation.setStartOffset(oldOffset); - - return result; - } - - @Override - public void setStartTime(long startTimeMillis) { - mAnimation.setStartTime(startTimeMillis); - } - - @Override - public long getStartTime() { - return mAnimation.getStartTime(); - } - - @Override - public void setInterpolator(Interpolator i) { - mAnimation.setInterpolator(i); - } - - @Override - public void setStartOffset(long startOffset) { - mAnimation.setStartOffset(startOffset); - } - - @Override - public void setDuration(long durationMillis) { - mAnimation.setDuration(durationMillis); - } - - @Override - public void scaleCurrentDuration(float scale) { - mAnimation.scaleCurrentDuration(scale); - } - - @Override - public void setRepeatMode(int repeatMode) { - mAnimation.setRepeatMode(repeatMode); - } - - @Override - public void setFillBefore(boolean fillBefore) { - mAnimation.setFillBefore(fillBefore); - } - - @Override - public void setFillAfter(boolean fillAfter) { - mAnimation.setFillAfter(fillAfter); - } - - @Override - public Interpolator getInterpolator() { - return mAnimation.getInterpolator(); - } - - @Override - public long getDuration() { - return mAnimation.getDuration(); - } - - @Override - public long getStartOffset() { - return mAnimation.getStartOffset() + mDelay; - } - - @Override - public int getRepeatMode() { - return mAnimation.getRepeatMode(); - } - - @Override - public boolean getFillBefore() { - return mAnimation.getFillBefore(); - } - - @Override - public boolean getFillAfter() { - return mAnimation.getFillAfter(); - } - - @Override - public boolean willChangeTransformationMatrix() { - return mAnimation.willChangeTransformationMatrix(); - } - - @Override - public boolean willChangeBounds() { - return mAnimation.willChangeBounds(); - } - } } diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index a6ce2931f0a23..56c6c924fbf32 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -17,34 +17,364 @@ package android.view.inputmethod; import android.content.Context; +import android.content.res.TypedArray; +import android.os.Bundle; import android.os.Handler; -import android.os.Message; +import android.os.SystemClock; +import android.text.Editable; +import android.text.NoCopySpan; +import android.text.Selection; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.method.MetaKeyKeyListener; +import android.util.Log; +import android.util.LogPrinter; +import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; import android.view.ViewRoot; +class ComposingText implements NoCopySpan { +} + /** * Base class for implementors of the InputConnection interface, taking care - * of implementing common system-oriented parts of the functionality. + * of most of the common behavior for providing a connection to an Editable. + * Implementors of this class will want to be sure to implement + * {@link #getEditable} to provide access to their own editable object. */ -public abstract class BaseInputConnection implements InputConnection { +public class BaseInputConnection implements InputConnection { + private static final boolean DEBUG = false; + private static final String TAG = "BaseInputConnection"; + static final Object COMPOSING = new ComposingText(); + final InputMethodManager mIMM; final Handler mH; final View mTargetView; + final boolean mDummyMode; - BaseInputConnection(InputMethodManager mgr) { + private Object[] mDefaultComposingSpans; + + Editable mEditable; + KeyCharacterMap mKeyCharacterMap; + + BaseInputConnection(InputMethodManager mgr, boolean dummyMode) { mIMM = mgr; mTargetView = null; mH = null; + mDummyMode = dummyMode; } - public BaseInputConnection(View targetView) { + public BaseInputConnection(View targetView, boolean dummyMode) { mIMM = (InputMethodManager)targetView.getContext().getSystemService( Context.INPUT_METHOD_SERVICE); mH = targetView.getHandler(); mTargetView = targetView; + mDummyMode = dummyMode; } + public static final void removeComposingSpans(Spannable text) { + text.removeSpan(COMPOSING); + Object[] sps = text.getSpans(0, text.length(), Object.class); + if (sps != null) { + for (int i=sps.length-1; i>=0; i--) { + Object o = sps[i]; + if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) { + text.removeSpan(o); + } + } + } + } + + public static void setComposingSpans(Spannable text) { + final Object[] sps = text.getSpans(0, text.length(), Object.class); + if (sps != null) { + for (int i=sps.length-1; i>=0; i--) { + final Object o = sps[i]; + if (o == COMPOSING) { + text.removeSpan(o); + continue; + } + final int fl = text.getSpanFlags(o); + if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) + != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) { + text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o), + (fl&Spanned.SPAN_POINT_MARK_MASK) + | Spanned.SPAN_COMPOSING + | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } + + text.setSpan(COMPOSING, 0, text.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); + } + + public static int getComposingSpanStart(Spannable text) { + return text.getSpanStart(COMPOSING); + } + + public static int getComposingSpanEnd(Spannable text) { + return text.getSpanEnd(COMPOSING); + } + + /** + * Return the target of edit operations. The default implementation + * returns its own fake editable that is just used for composing text; + * subclasses that are real text editors should override this and + * supply their own. + */ + public Editable getEditable() { + if (mEditable == null) { + mEditable = Editable.Factory.getInstance().newEditable(""); + Selection.setSelection(mEditable, 0); + } + return mEditable; + } + + /** + * Default implementation does nothing. + */ + public boolean beginBatchEdit() { + return false; + } + + /** + * Default implementation does nothing. + */ + public boolean endBatchEdit() { + return false; + } + + /** + * Default implementation uses + * {@link MetaKeyKeyListener#clearMetaKeyState(long, int) + * MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state. + */ + public boolean clearMetaKeyStates(int states) { + final Editable content = getEditable(); + if (content == null) return false; + MetaKeyKeyListener.clearMetaKeyState(content, states); + return true; + } + + /** + * Default implementation does nothing. + */ + public boolean commitCompletion(CompletionInfo text) { + return false; + } + + /** + * Default implementation replaces any existing composing text with + * the given text. In addition, only if dummy mode, a key event is + * sent for the new text and the current editable buffer cleared. + */ + public boolean commitText(CharSequence text, int newCursorPosition) { + if (DEBUG) Log.v(TAG, "commitText " + text); + replaceText(text, newCursorPosition, false); + sendCurrentText(); + return true; + } + + /** + * The default implementation performs the deletion around the current + * selection position of the editable text. + */ + public boolean deleteSurroundingText(int leftLength, int rightLength) { + if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength + + " / " + rightLength); + final Editable content = getEditable(); + if (content == null) return false; + + beginBatchEdit(); + + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + if (a > b) { + int tmp = a; + a = b; + b = tmp; + } + + // ignore the composing text. + int ca = getComposingSpanStart(content); + int cb = getComposingSpanEnd(content); + if (cb < ca) { + int tmp = ca; + ca = cb; + cb = tmp; + } + if (ca != -1 && cb != -1) { + if (ca < a) a = ca; + if (cb > b) b = cb; + } + + int deleted = 0; + + if (leftLength > 0) { + int start = a - leftLength; + if (start < 0) start = 0; + content.delete(start, a); + deleted = a - start; + } + + if (rightLength > 0) { + b = b - deleted; + + int end = b + rightLength; + if (end > content.length()) end = content.length(); + + content.delete(b, end); + } + + endBatchEdit(); + + return true; + } + + /** + * The default implementation removes the composing state from the + * current editable text. In addition, only if dummy mode, a key event is + * sent for the new text and the current editable buffer cleared. + */ + public boolean finishComposingText() { + if (DEBUG) Log.v(TAG, "finishComposingText"); + final Editable content = getEditable(); + if (content != null) { + beginBatchEdit(); + removeComposingSpans(content); + endBatchEdit(); + sendCurrentText(); + } + return true; + } + + /** + * The default implementation uses TextUtils.getCapsMode to get the + * cursor caps mode for the current selection position in the editable + * text, unless in dummy mode in which case 0 is always returned. + */ + public int getCursorCapsMode(int reqModes) { + if (mDummyMode) return 0; + + final Editable content = getEditable(); + if (content == null) return 0; + + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + if (a > b) { + int tmp = a; + a = b; + b = tmp; + } + + return TextUtils.getCapsMode(content, a, reqModes); + } + + /** + * The default implementation always returns null. + */ + public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { + return null; + } + + /** + * The default implementation returns the given amount of text from the + * current cursor position in the buffer. + */ + public CharSequence getTextBeforeCursor(int length, int flags) { + final Editable content = getEditable(); + if (content == null) return null; + + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + if (a > b) { + int tmp = a; + a = b; + b = tmp; + } + + if (length > a) { + length = a; + } + + if ((flags&GET_TEXT_WITH_STYLES) != 0) { + return content.subSequence(a - length, a); + } + return TextUtils.substring(content, a - length, a); + } + + /** + * The default implementation returns the given amount of text from the + * current cursor position in the buffer. + */ + public CharSequence getTextAfterCursor(int length, int flags) { + final Editable content = getEditable(); + if (content == null) return null; + + int a = Selection.getSelectionStart(content); + int b = Selection.getSelectionEnd(content); + + if (a > b) { + int tmp = a; + a = b; + b = tmp; + } + + if (b + length > content.length()) { + length = content.length() - b; + } + + + if ((flags&GET_TEXT_WITH_STYLES) != 0) { + return content.subSequence(b, b + length); + } + return TextUtils.substring(content, b, b + length); + } + + /** + * The default implementation does nothing. + */ + public boolean performContextMenuAction(int id) { + return false; + } + + /** + * The default implementation does nothing. + */ + public boolean performPrivateCommand(String action, Bundle data) { + return false; + } + + /** + * The default implementation places the given text into the editable, + * replacing any existing composing text. The new text is marked as + * in a composing state with the composing style. + */ + public boolean setComposingText(CharSequence text, int newCursorPosition) { + if (DEBUG) Log.v(TAG, "setComposingText " + text); + replaceText(text, newCursorPosition, true); + return true; + } + + /** + * The default implementation changes the selection position in the + * current editable text. + */ + public boolean setSelection(int start, int end) { + if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end); + final Editable content = getEditable(); + if (content == null) return false; + Selection.setSelection(content, start, end); + return true; + } + /** * Provides standard implementation for sending a key event to the window * attached to the input connection's view. @@ -82,4 +412,144 @@ public abstract class BaseInputConnection implements InputConnection { mIMM.updateStatusIcon(resId, packageName); return true; } + + private void sendCurrentText() { + if (!mDummyMode) { + return; + } + + Editable content = getEditable(); + if (content != null) { + if (content.length() == 1) { + // If it's 1 character, we have a chance of being + // able to generate normal key events... + if (mKeyCharacterMap == null) { + mKeyCharacterMap = KeyCharacterMap.load( + KeyCharacterMap.BUILT_IN_KEYBOARD); + } + char[] chars = new char[1]; + content.getChars(0, 1, chars, 0); + KeyEvent[] events = mKeyCharacterMap.getEvents(chars); + if (events != null) { + for (int i=0; i=0 && b>= 0 && a != b) { + if (b < a) { + int tmp = a; + a = b; + b = tmp; + } + } + } + + if (composing) { + Spannable sp = null; + if (!(text instanceof Spannable)) { + sp = new SpannableStringBuilder(text); + text = sp; + if (mDefaultComposingSpans == null) { + Context context; + if (mTargetView != null) { + context = mTargetView.getContext(); + } else if (mIMM.mServedView != null) { + context = mIMM.mServedView.getContext(); + } else { + context = null; + } + if (context != null) { + TypedArray ta = context.getTheme() + .obtainStyledAttributes(new int[] { + com.android.internal.R.attr.candidatesTextStyleSpans + }); + CharSequence style = ta.getText(0); + ta.recycle(); + if (style != null && style instanceof Spanned) { + mDefaultComposingSpans = ((Spanned)style).getSpans( + 0, style.length(), Object.class); + } + } + } + if (mDefaultComposingSpans != null) { + for (int i = 0; i < mDefaultComposingSpans.length; ++i) { + sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } else { + sp = (Spannable)text; + } + setComposingSpans(sp); + } + + // Adjust newCursorPosition to be relative the start of the text. + newCursorPosition += a; + + if (DEBUG) Log.v(TAG, "Replacing from " + a + " to " + b + " with \"" + + text + "\", composing=" + composing + + ", type=" + text.getClass().getCanonicalName()); + + if (DEBUG) { + LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); + lp.println("Current text:"); + TextUtils.dumpSpans(content, lp, " "); + lp.println("Composing text:"); + TextUtils.dumpSpans(text, lp, " "); + } + + content.replace(a, b, text); + if (newCursorPosition < 0) newCursorPosition = 0; + if (newCursorPosition > content.length()) + newCursorPosition = content.length(); + Selection.setSelection(content, newCursorPosition); + + if (DEBUG) { + LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); + lp.println("Final text:"); + TextUtils.dumpSpans(content, lp, " "); + } + + endBatchEdit(); + } } diff --git a/core/java/android/view/inputmethod/DefaultInputMethod.java b/core/java/android/view/inputmethod/DefaultInputMethod.java deleted file mode 100644 index 073b01c7b9ce6..0000000000000 --- a/core/java/android/view/inputmethod/DefaultInputMethod.java +++ /dev/null @@ -1,239 +0,0 @@ -package android.view.inputmethod; - -import android.graphics.Rect; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; -import android.view.KeyEvent; -import android.view.MotionEvent; - -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethod; -import com.android.internal.view.IInputMethodCallback; -import com.android.internal.view.IInputMethodSession; -import com.android.internal.view.InputConnectionWrapper; - -/** - * This is the default input method that runs in the same context of the - * application that requests text input. It does nothing but returns false for - * any key events, so that all key events will be processed by the key listener - * of the focused text box. - * {@hide} - */ -public class DefaultInputMethod implements InputMethod, InputMethodSession { - private static IInputMethod sInstance = new SimpleInputMethod( - new DefaultInputMethod()); - - private static InputMethodInfo sProperty = new InputMethodInfo( - "android.text.inputmethod", DefaultInputMethod.class.getName(), - "Default", "android.text.inputmethod.defaultImeSettings"); - - private InputConnection mInputConnection; - - public static IInputMethod getInstance() { - return sInstance; - } - - public static InputMethodInfo getMetaInfo() { - return sProperty; - } - - public void bindInput(InputBinding binding) { - mInputConnection = binding.getConnection(); - } - - public void unbindInput() { - } - - public void createSession(SessionCallback callback) { - callback.sessionCreated(this); - } - - public void setSessionEnabled(InputMethodSession session, boolean enabled) { - } - - public void revokeSession(InputMethodSession session) { - } - - public void finishInput() { - mInputConnection.hideStatusIcon(); - } - - public void displayCompletions(CompletionInfo[] completions) { - } - - public void updateExtractedText(int token, ExtractedText text) { - } - - public void updateSelection(int oldSelStart, int oldSelEnd, - int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { - } - - public void updateCursor(Rect newCursor) { - } - - public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) { - callback.finishedEvent(seq, false); - } - - public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) { - callback.finishedEvent(seq, false); - } - - public void restartInput(EditorInfo attribute) { - } - - public void attachToken(IBinder token) { - } - - public void startInput(EditorInfo attribute) { - mInputConnection - .showStatusIcon("android", com.android.internal.R.drawable.ime_qwerty); - } - - public void appPrivateCommand(String action, Bundle data) { - } - - public void hideSoftInput() { - } - - public void showSoftInput(int flags) { - } -} - -// ---------------------------------------------------------------------- - -class SimpleInputMethod extends IInputMethod.Stub { - final InputMethod mInputMethod; - - static class Session extends IInputMethodSession.Stub { - final InputMethodSession mSession; - - Session(InputMethodSession session) { - mSession = session; - } - - public void finishInput() { - mSession.finishInput(); - } - - public void updateSelection(int oldSelStart, int oldSelEnd, - int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { - mSession.updateSelection(oldSelStart, oldSelEnd, - newSelStart, newSelEnd, candidatesStart, candidatesEnd); - } - - public void updateCursor(Rect newCursor) { - mSession.updateCursor(newCursor); - } - - static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback { - final IInputMethodCallback mCb; - InputMethodEventCallbackWrapper(IInputMethodCallback cb) { - mCb = cb; - } - public void finishedEvent(int seq, boolean handled) { - try { - mCb.finishedEvent(seq, handled); - } catch (RemoteException e) { - } - } - } - - public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) { - mSession.dispatchKeyEvent(seq, event, - new InputMethodEventCallbackWrapper(callback)); - } - - public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) { - mSession.dispatchTrackballEvent(seq, event, - new InputMethodEventCallbackWrapper(callback)); - } - - public void displayCompletions(CompletionInfo[] completions) { - mSession.displayCompletions(completions); - } - - public void updateExtractedText(int token, ExtractedText text) { - mSession.updateExtractedText(token, text); - } - - public void appPrivateCommand(String action, Bundle data) { - mSession.appPrivateCommand(action, data); - } - } - - public SimpleInputMethod(InputMethod inputMethod) { - mInputMethod = inputMethod; - } - - public InputMethod getInternalInputMethod() { - return mInputMethod; - } - - public void attachToken(IBinder token) { - mInputMethod.attachToken(token); - } - - public void bindInput(InputBinding binding) { - InputConnectionWrapper ic = new InputConnectionWrapper( - IInputContext.Stub.asInterface(binding.getConnectionToken())); - InputBinding nu = new InputBinding(ic, binding); - mInputMethod.bindInput(nu); - } - - public void unbindInput() { - mInputMethod.unbindInput(); - } - - public void restartInput(EditorInfo attribute) { - mInputMethod.restartInput(attribute); - } - - public void startInput(EditorInfo attribute) { - mInputMethod.startInput(attribute); - } - - static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { - final IInputMethodCallback mCb; - InputMethodSessionCallbackWrapper(IInputMethodCallback cb) { - mCb = cb; - } - - public void sessionCreated(InputMethodSession session) { - try { - mCb.sessionCreated(new Session(session)); - } catch (RemoteException e) { - } - } - } - - public void createSession(IInputMethodCallback callback) throws RemoteException { - mInputMethod.createSession(new InputMethodSessionCallbackWrapper(callback)); - } - - public void setSessionEnabled(IInputMethodSession session, boolean enabled) throws RemoteException { - try { - InputMethodSession ls = ((Session)session).mSession; - mInputMethod.setSessionEnabled(ls, enabled); - } catch (ClassCastException e) { - Log.w("SimpleInputMethod", "Incoming session not of correct type: " + session, e); - } - } - - public void revokeSession(IInputMethodSession session) throws RemoteException { - try { - InputMethodSession ls = ((Session)session).mSession; - mInputMethod.revokeSession(ls); - } catch (ClassCastException e) { - Log.w("SimpleInputMethod", "Incoming session not of correct type: " + session, e); - } - } - - public void showSoftInput(boolean blah) { - } - - public void hideSoftInput() { - } -} diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index c0506913e3731..b2f26d7304dc1 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -5,6 +5,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.InputType; import android.text.TextUtils; +import android.util.Printer; /** * An EditorInfo describes several attributes of a text editing object @@ -21,7 +22,7 @@ public class EditorInfo implements InputType, Parcelable { * @see #TYPE_MASK_VARIATION * @see #TYPE_MASK_FLAGS */ - public int inputType = TYPE_CLASS_TEXT; + public int inputType = TYPE_NULL; /** * A string supplying additional information about the content type that @@ -70,6 +71,26 @@ public class EditorInfo implements InputType, Parcelable { */ public CharSequence label; + /** + * Name of the package that owns this editor. + */ + public String packageName; + + /** + * Identifier for the editor's field. This is optional, and may be + * 0. By default it is filled in with the result of + * {@link android.view.View#getId() View.getId()} on the View that + * is being edited. + */ + public int fieldId; + + /** + * Additional name for the editor's field. This can supply additional + * name information for the field. By default it is null. The actual + * contents have no meaning. + */ + public String fieldName; + /** * Any extra data to supply to the input method. This is for extended * communication with specific input methods; the name fields in the @@ -80,6 +101,24 @@ public class EditorInfo implements InputType, Parcelable { */ public Bundle extras; + /** + * Write debug output of this object. + */ + public void dump(Printer pw, String prefix) { + pw.println(prefix + "inputType=0x" + Integer.toHexString(inputType) + + " privateContentType=" + privateContentType); + pw.println(prefix + "initialSelStart=" + initialSelStart + + " initialSelEnd=" + initialSelEnd + + " initialCapsMode=0x" + + Integer.toHexString(initialCapsMode)); + pw.println(prefix + "hintText=" + hintText + + " label=" + label); + pw.println(prefix + "packageName=" + packageName + + " fieldId=" + fieldId + + " fieldName=" + fieldName); + pw.println(prefix + "extras=" + extras); + } + /** * Used to package this object into a {@link Parcel}. * @@ -94,6 +133,9 @@ public class EditorInfo implements InputType, Parcelable { dest.writeInt(initialCapsMode); TextUtils.writeToParcel(hintText, dest, flags); TextUtils.writeToParcel(label, dest, flags); + dest.writeString(packageName); + dest.writeInt(fieldId); + dest.writeString(fieldName); dest.writeBundle(extras); } @@ -110,6 +152,9 @@ public class EditorInfo implements InputType, Parcelable { res.initialCapsMode = source.readInt(); res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + res.packageName = source.readString(); + res.fieldId = source.readInt(); + res.fieldName = source.readString(); res.extras = source.readBundle(); return res; } diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java index 0ca3c79349d75..e5d3caeef1ced 100644 --- a/core/java/android/view/inputmethod/ExtractedText.java +++ b/core/java/android/view/inputmethod/ExtractedText.java @@ -18,6 +18,22 @@ public class ExtractedText implements Parcelable { */ public int startOffset; + /** + * If the content is a report of a partial text change, this is the + * offset where the change starts and it runs until + * {@link #partialEndOffset}. If the content is the full text, this + * field is -1. + */ + public int partialStartOffset; + + /** + * If the content is a report of a partial text change, this is the offset + * where the change ends. Note that the actual text may be larger or + * smaller than the difference between this and {@link #partialEndOffset}, + * meaning a reduction or increase, respectively, in the total text. + */ + public int partialEndOffset; + /** * The offset where the selection currently starts within the extracted * text. The real selection start position is at @@ -52,6 +68,8 @@ public class ExtractedText implements Parcelable { public void writeToParcel(Parcel dest, int flags) { TextUtils.writeToParcel(text, dest, flags); dest.writeInt(startOffset); + dest.writeInt(partialStartOffset); + dest.writeInt(partialEndOffset); dest.writeInt(selectionStart); dest.writeInt(selectionEnd); dest.writeInt(flags); @@ -65,6 +83,8 @@ public class ExtractedText implements Parcelable { ExtractedText res = new ExtractedText(); res.text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); res.startOffset = source.readInt(); + res.partialStartOffset = source.readInt(); + res.partialEndOffset = source.readInt(); res.selectionStart = source.readInt(); res.selectionEnd = source.readInt(); res.flags = source.readInt(); diff --git a/core/java/android/view/inputmethod/ExtractedTextRequest.java b/core/java/android/view/inputmethod/ExtractedTextRequest.java index d9623290ea7ad..e84b094557854 100644 --- a/core/java/android/view/inputmethod/ExtractedTextRequest.java +++ b/core/java/android/view/inputmethod/ExtractedTextRequest.java @@ -15,6 +15,13 @@ public class ExtractedTextRequest implements Parcelable { */ public int token; + /** + * Additional request flags, having the same possible values as the + * flags parameter of {@link InputConnection#getTextBeforeCursor + * InputConnection.getTextBeforeCursor()}. + */ + public int flags; + /** * Hint for the maximum number of lines to return. */ @@ -33,6 +40,7 @@ public class ExtractedTextRequest implements Parcelable { */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(token); + dest.writeInt(this.flags); dest.writeInt(hintMaxLines); dest.writeInt(hintMaxChars); } @@ -45,6 +53,7 @@ public class ExtractedTextRequest implements Parcelable { public ExtractedTextRequest createFromParcel(Parcel source) { ExtractedTextRequest res = new ExtractedTextRequest(); res.token = source.readInt(); + res.flags = source.readInt(); res.hintMaxLines = source.readInt(); res.hintMaxChars = source.readInt(); return res; diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index bd7b0507d85b0..8c30d3fd41280 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -31,6 +31,21 @@ import android.view.KeyEvent; * subclassing {@link BaseInputConnection}. */ public interface InputConnection { + /** + * Flag for use with {@link #getTextAfterCursor} and + * {@link #getTextBeforeCursor} to have style information returned along + * with the text. If not set, you will receive only the raw text. If + * set, you may receive a complex CharSequence of both text and style + * spans. + */ + static final int GET_TEXT_WITH_STYLES = 0x0001; + + /** + * Flag for use with {@link #getExtractedText} to indicate you would + * like to receive updates when the extracted text changes. + */ + public static final int GET_EXTRACTED_TEXT_MONITOR = 0x0001; + /** * Get n characters of text before the current cursor position. * @@ -40,11 +55,13 @@ public interface InputConnection { * In either case, a null is returned. * * @param n The expected length of the text. + * @param flags Supplies additional options controlling how the text is + * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}. * * @return Returns the text before the cursor position; the length of the * returned text might be less than n. */ - public CharSequence getTextBeforeCursor(int n); + public CharSequence getTextBeforeCursor(int n, int flags); /** * Get n characters of text after the current cursor position. @@ -55,11 +72,13 @@ public interface InputConnection { * In either case, a null is returned. * * @param n The expected length of the text. + * @param flags Supplies additional options controlling how the text is + * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}. * * @return Returns the text after the cursor position; the length of the * returned text might be less than n. */ - public CharSequence getTextAfterCursor(int n); + public CharSequence getTextAfterCursor(int n, int flags); /** * Retrieve the current capitalization mode in effect at the current @@ -82,8 +101,6 @@ public interface InputConnection { */ public int getCursorCapsMode(int reqModes); - public static final int EXTRACTED_TEXT_MONITOR = 0x0001; - /** * Retrieve the current text in the input connection's editor, and monitor * for any changes to it. This function returns with the current text, @@ -97,7 +114,7 @@ public interface InputConnection { * * @param request Description of how the text should be returned. * @param flags Additional options to control the client, either 0 or - * {@link #EXTRACTED_TEXT_MONITOR}. + * {@link #GET_EXTRACTED_TEXT_MONITOR}. * * @return Returns an ExtractedText object describing the state of the * text view and containing the extracted text itself. @@ -141,7 +158,7 @@ public interface InputConnection { /** * Have the text editor finish whatever composing text is currently - * active. This simple leaves the text as-is, removing any special + * active. This simply leaves the text as-is, removing any special * composing styling or other state that was around it. The cursor * position remains unchanged. */ @@ -176,6 +193,22 @@ public interface InputConnection { */ public boolean commitCompletion(CompletionInfo text); + /** + * Set the selection of the text editor. To set the cursor position, + * start and end should have the same value. + */ + public boolean setSelection(int start, int end); + + /** + * Perform a context menu action on the field. The given id may be one of: + * {@link android.R.id#selectAll}, + * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText}, + * {@link android.R.id#cut}, {@link android.R.id#copy}, + * {@link android.R.id#paste}, {@link android.R.id#copyUrl}, + * or {@link android.R.id#switchInputMethod} + */ + public boolean performContextMenuAction(int id); + /** * Tell the editor that you are starting a batch of editor operations. * The editor will try to avoid sending you updates about its state diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java deleted file mode 100644 index f65b2a1081c79..0000000000000 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package android.view.inputmethod; - -import android.os.Bundle; -import android.view.KeyEvent; - -/** - * Wrapper around InputConnection interface, calling through to another - * implementation of it. - */ -public class InputConnectionWrapper implements InputConnection { - InputConnection mBase; - - /** - * Create a new wrapper around an existing InputConnection implementation. - */ - public InputConnectionWrapper(InputConnection base) { - mBase = base; - } - - /** - * Return the base InputConnection that this class is wrapping. - */ - InputConnection getBase() { - return mBase; - } - - public CharSequence getTextBeforeCursor(int n) { - return mBase.getTextBeforeCursor(n); - } - - public CharSequence getTextAfterCursor(int n) { - return mBase.getTextAfterCursor(n); - } - - public int getCursorCapsMode(int reqModes) { - return mBase.getCursorCapsMode(reqModes); - } - - public ExtractedText getExtractedText(ExtractedTextRequest request, - int flags) { - return mBase.getExtractedText(request, flags); - } - - public boolean deleteSurroundingText(int leftLength, int rightLength) { - return mBase.deleteSurroundingText(leftLength, rightLength); - } - - public boolean setComposingText(CharSequence text, int newCursorPosition) { - return mBase.setComposingText(text, newCursorPosition); - } - - public boolean finishComposingText() { - return mBase.finishComposingText(); - } - - public boolean commitText(CharSequence text, int newCursorPosition) { - return mBase.commitText(text, newCursorPosition); - } - - public boolean commitCompletion(CompletionInfo text) { - return mBase.commitCompletion(text); - } - - public boolean beginBatchEdit() { - return mBase.beginBatchEdit(); - } - - public boolean endBatchEdit() { - return mBase.endBatchEdit(); - } - - public boolean sendKeyEvent(KeyEvent event) { - return mBase.sendKeyEvent(event); - } - - public boolean clearMetaKeyStates(int states) { - return mBase.clearMetaKeyStates(states); - } - - public boolean performPrivateCommand(String action, Bundle data) { - return mBase.performPrivateCommand(action, data); - } - - public boolean showStatusIcon(String packageName, int resId) { - return mBase.showStatusIcon(packageName, resId); - } - - public boolean hideStatusIcon() { - return mBase.hideStatusIcon(); - } -} diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index c0e6590c71102..740dca83d7ae6 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -113,12 +113,15 @@ public interface InputMethod { * is ready for this input method to process received events and send result * text back to the application. * - * @param attribute The attribute of the text box (typically, a EditText) + * @param inputConnection Optional specific input connection for + * communicating with the text box; if null, you should use the generic + * bound input connection. + * @param info Information about the text box (typically, an EditText) * that requests input. * * @see EditorInfo */ - public void startInput(EditorInfo attribute); + public void startInput(InputConnection inputConnection, EditorInfo info); /** * This method is called when the state of this input method needs to be @@ -128,12 +131,15 @@ public interface InputMethod { * Typically, this method is called when the input focus is moved from one * text box to another. * + * @param inputConnection Optional specific input connection for + * communicating with the text box; if null, you should use the generic + * bound input connection. * @param attribute The attribute of the text box (typically, a EditText) * that requests input. * * @see EditorInfo */ - public void restartInput(EditorInfo attribute); + public void restartInput(InputConnection inputConnection, EditorInfo attribute); /** * Create a new {@link InputMethodSession} that can be handed to client @@ -172,6 +178,13 @@ public interface InputMethod { */ public static final int SHOW_EXPLICIT = 0x00001; + /** + * Flag for {@link #showSoftInput(int)}: this show has been forced to + * happen by the user. If set, the input method should remain visible + * until deliberated dismissed by the user in its UI. + */ + public static final int SHOW_FORCED = 0x00002; + /** * Request that any soft input part of the input method be shown to the user. * diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index a9a95944dc4a9..99d5aa511df95 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -26,11 +26,14 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.util.PrintWriterPrinter; +import android.util.Printer; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewRoot; +import com.android.internal.os.HandlerCaller; import com.android.internal.view.IInputConnectionWrapper; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodCallback; @@ -39,7 +42,11 @@ import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Central system API to the overall input method framework (IMF) architecture, @@ -199,8 +206,7 @@ public final class InputMethodManager { // global lock. final H mH; - // The currently active input connection. - final MutableInputConnectionWrapper mInputConnectionWrapper; + // Our generic input connection if the current target does not have its own. final IInputContext mIInputContext; /** @@ -208,11 +214,6 @@ public final class InputMethodManager { */ boolean mActive = false; - /** - * The current base input connection, used when mActive is true. - */ - InputConnection mCurrentInputConnection; - // ----------------------------------------------------------- /** @@ -270,7 +271,7 @@ public final class InputMethodManager { // ----------------------------------------------------------- - static final int MSG_CHECK_FOCUS = 1; + static final int MSG_DUMP = 1; class H extends Handler { H(Looper looper) { @@ -280,85 +281,55 @@ public final class InputMethodManager { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_CHECK_FOCUS: - checkFocus(); + case MSG_DUMP: { + HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; + try { + doDump((FileDescriptor)args.arg1, + (PrintWriter)args.arg2, (String[])args.arg3); + } catch (RuntimeException e) { + ((PrintWriter)args.arg2).println("Exception: " + e); + } + synchronized (args.arg4) { + ((CountDownLatch)args.arg4).countDown(); + } return; + } } } } - static class NoOpInputConnection implements InputConnection { - - public boolean clearMetaKeyStates(int states) { - return false; + class ControlledInputConnectionWrapper extends IInputConnectionWrapper { + public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) { + super(mainLooper, conn); } - public boolean beginBatchEdit() { - return false; - } - - public boolean endBatchEdit() { - return false; - } - - public boolean commitCompletion(CompletionInfo text) { - return false; - } - - public boolean commitText(CharSequence text, int newCursorPosition) { - return false; - } - - public boolean deleteSurroundingText(int leftLength, int rightLength) { - return false; - } - - public int getCursorCapsMode(int reqModes) { - return 0; - } - - public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { - return null; - } - - public CharSequence getTextAfterCursor(int n) { - return null; - } - - public CharSequence getTextBeforeCursor(int n) { - return null; - } - - public boolean hideStatusIcon() { - return false; - } - - public boolean performPrivateCommand(String action, Bundle data) { - return false; - } - - public boolean sendKeyEvent(KeyEvent event) { - return false; - } - - public boolean setComposingText(CharSequence text, int newCursorPosition) { - return false; - } - - public boolean finishComposingText() { - return false; - } - - public boolean showStatusIcon(String packageName, int resId) { - return false; + public boolean isActive() { + return mActive; } } - final NoOpInputConnection mNoOpInputConnection = new NoOpInputConnection(); - final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { - public void setUsingInputMethod(boolean state) { + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + // No need to check for dump permission, since we only give this + // interface to the system. + CountDownLatch latch = new CountDownLatch(1); + HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs(); + sargs.arg1 = fd; + sargs.arg2 = fout; + sargs.arg3 = args; + sargs.arg4 = latch; + mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs)); + try { + if (!latch.await(5, TimeUnit.SECONDS)) { + fout.println("Timeout waiting for dump"); + } + } catch (InterruptedException e) { + fout.println("Interrupted waiting for dump"); + } + } + + public void setUsingInputMethod(boolean state) { } public void onBindMethod(InputBindResult res) { @@ -403,62 +374,17 @@ public final class InputMethodManager { public void setActive(boolean active) { mActive = active; - mInputConnectionWrapper.setBaseInputConnection(active - ? mCurrentInputConnection : mNoOpInputConnection); } }; - final InputConnection mDummyInputConnection = new BaseInputConnection(this) { - public boolean beginBatchEdit() { - return false; - } - public boolean endBatchEdit() { - return false; - } - public boolean commitText(CharSequence text, int newCursorPosition) { - return false; - } - public boolean commitCompletion(CompletionInfo text) { - return false; - } - public boolean deleteSurroundingText(int leftLength, int rightLength) { - return false; - } - public ExtractedText getExtractedText(ExtractedTextRequest request, - int flags) { - return null; - } - public CharSequence getTextAfterCursor(int n) { - return null; - } - public CharSequence getTextBeforeCursor(int n) { - return null; - } - public int getCursorCapsMode(int reqModes) { - return 0; - } - public boolean clearMetaKeyStates(int states) { - return false; - } - public boolean performPrivateCommand(String action, Bundle data) { - return false; - } - public boolean setComposingText(CharSequence text, int newCursorPosition) { - return false; - } - public boolean finishComposingText() { - return false; - } - }; + final InputConnection mDummyInputConnection = new BaseInputConnection(this, true); InputMethodManager(IInputMethodManager service, Looper looper) { mService = service; mMainLooper = looper; mH = new H(looper); - mInputConnectionWrapper = new MutableInputConnectionWrapper(mNoOpInputConnection); - mIInputContext = new IInputConnectionWrapper(looper, - mInputConnectionWrapper); - setCurrentInputConnection(mDummyInputConnection); + mIInputContext = new ControlledInputConnectionWrapper(looper, + mDummyInputConnection); if (mInstance == null) { mInstance = this; @@ -562,15 +488,6 @@ public final class InputMethodManager { mCurMethod = null; } - /** - * Record the desired input connection, but only set it if mActive is true. - */ - void setCurrentInputConnection(InputConnection connection) { - mCurrentInputConnection = connection; - mInputConnectionWrapper.setBaseInputConnection(mActive - ? connection : mNoOpInputConnection); - } - /** * Reset all of the state associated with a served view being connected * to an input method @@ -578,7 +495,6 @@ public final class InputMethodManager { void clearConnectionLocked() { mCurrentTextBoxAttribute = null; mServedInputConnection = null; - setCurrentInputConnection(mDummyInputConnection); } /** @@ -659,12 +575,19 @@ public final class InputMethodManager { } /** - * Flag for {@link #showSoftInput} to indicate that the this is an implicit + * Flag for {@link #showSoftInput} to indicate that this is an implicit * request to show the input window, not as the result of a direct request * by the user. The window may not be shown in this case. */ public static final int SHOW_IMPLICIT = 0x0001; + /** + * Flag for {@link #showSoftInput} to indicate that the user has forced + * the input method open (such as by long-pressing menu) so it should + * not be closed until they explicitly do so. + */ + public static final int SHOW_FORCED = 0x0002; + /** * Explicitly request that the current input method's soft input area be * shown to the user, if needed. Call this if the user interacts with @@ -689,6 +612,14 @@ public final class InputMethodManager { } } + /** @hide */ + public void showSoftInputUnchecked(int flags) { + try { + mService.showSoftInput(mClient, flags); + } catch (RemoteException e) { + } + } + /** * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft * input window should only be hidden if it was not explicitly shown @@ -696,6 +627,13 @@ public final class InputMethodManager { */ public static final int HIDE_IMPLICIT_ONLY = 0x0001; + /** + * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft + * input window should normally be hidden, unless it was originally + * shown with {@link #SHOW_FORCED}. + */ + public static final int HIDE_NOT_ALWAYS = 0x0002; + /** * Request to hide the soft input window from the context of the window * that is currently accepting input. This should be called as a result @@ -779,6 +717,8 @@ public final class InputMethodManager { // do its stuff. // Life is good: let's hook everything up! EditorInfo tba = new EditorInfo(); + tba.packageName = view.getContext().getPackageName(); + tba.fieldId = view.getId(); InputConnection ic = view.onCreateInputConnection(tba); if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic); @@ -801,22 +741,23 @@ public final class InputMethodManager { mCurrentTextBoxAttribute = tba; mServedConnecting = false; mServedInputConnection = ic; + IInputContext servedContext; if (ic != null) { mCursorSelStart = tba.initialSelStart; mCursorSelEnd = tba.initialSelEnd; mCursorCandStart = -1; mCursorCandEnd = -1; mCursorRect.setEmpty(); - setCurrentInputConnection(ic); + servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic); } else { - setCurrentInputConnection(mDummyInputConnection); + servedContext = null; } try { if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" + ic + " tba=" + tba + " initial=" + initial); - InputBindResult res = mService.startInput(mClient, tba, initial, - mCurMethod == null); + InputBindResult res = mService.startInput(mClient, + servedContext, tba, initial, mCurMethod == null); if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res != null) { if (res.id != null) { @@ -885,10 +826,20 @@ public final class InputMethodManager { + " winFocus=" + view.hasWindowFocus()); if (mServedView == view) { ic = mServedInputConnection; - if (view.hasWindowFocus()) { + // The following code would auto-hide the IME if we end up + // with no more views with focus. This can happen, however, + // whenever we go into touch mode, so it ends up hiding + // at times when we don't really want it to. For now it + // seems better to just turn it all off. + if (false && view.hasWindowFocus()) { mLastServedView = view; - mH.removeMessages(MSG_CHECK_FOCUS); - mH.sendEmptyMessage(MSG_CHECK_FOCUS); + Handler vh = view.getHandler(); + if (vh != null) { + // This will result in a call to checkFocus() below. + vh.removeMessages(ViewRoot.CHECK_FOCUS); + vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS, + view)); + } } } } @@ -898,8 +849,14 @@ public final class InputMethodManager { } } - void checkFocus() { + /** + * @hide + */ + public void checkFocus(View view) { synchronized (mH) { + if (view != mLastServedView) { + return; + } if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView + " last=" + mLastServedView); if (mServedView == mLastServedView) { @@ -915,7 +872,7 @@ public final class InputMethodManager { void closeCurrentInput() { try { - mService.hideSoftInput(mClient, 0); + mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS); } catch (RemoteException e) { } } @@ -1005,6 +962,32 @@ public final class InputMethodManager { } } + /** + * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) + * InputMethodSession.appPrivateCommand()} on the current Input Method. + * @param view Optional View that is sending the command, or null if + * you want to send the command regardless of the view that is attached + * to the input method. + * @param action Name of the command to be performed. This must + * be a scoped name, i.e. prefixed with a package name you own, so that + * different developers will not create conflicting commands. + * @param data Any data to include with the command. + */ + public void sendAppPrivateCommand(View view, String action, Bundle data) { + synchronized (mH) { + if ((view != null && mServedView != view) + || mCurrentTextBoxAttribute == null || mCurMethod == null) { + return; + } + try { + if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data); + mCurMethod.appPrivateCommand(action, data); + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId, e); + } + } + } + /** * Force switch to a new input method component. This can only be called * from the currently active input method, as validated by the given token. @@ -1048,7 +1031,7 @@ public final class InputMethodManager { synchronized (mH) { if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); - if (mCurMethod == null || mCurrentTextBoxAttribute == null) { + if (mCurMethod == null) { try { callback.finishedEvent(seq, false); } catch (RemoteException e) { @@ -1116,4 +1099,33 @@ public final class InputMethodManager { } } } + + void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { + final Printer p = new PrintWriterPrinter(fout); + p.println("Input method client state for " + this + ":"); + + p.println(" mService=" + mService); + p.println(" mMainLooper=" + mMainLooper); + p.println(" mIInputContext=" + mIInputContext); + p.println(" mActive=" + mActive + + " mBindSequence=" + mBindSequence + + " mCurId=" + mCurId); + p.println(" mCurMethod=" + mCurMethod); + p.println(" mServedView=" + mServedView); + p.println(" mLastServedView=" + mLastServedView); + p.println(" mServedConnecting=" + mServedConnecting); + if (mCurrentTextBoxAttribute != null) { + p.println(" mCurrentTextBoxAttribute:"); + mCurrentTextBoxAttribute.dump(p, " "); + } else { + p.println(" mCurrentTextBoxAttribute: null"); + } + p.println(" mServedInputConnection=" + mServedInputConnection); + p.println(" mCompletions=" + mCompletions); + p.println(" mCursorRect=" + mCursorRect); + p.println(" mCursorSelStart=" + mCursorSelStart + + " mCursorSelEnd=" + mCursorSelEnd + + " mCursorCandStart=" + mCursorCandStart + + " mCursorCandEnd=" + mCursorCandEnd); + } } diff --git a/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java b/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java deleted file mode 100644 index 025a0599aab0b..0000000000000 --- a/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package android.view.inputmethod; - - -/** - * Special version of {@link InputConnectionWrapper} that allows the base - * input connection to be modified after it is initially set. - */ -public class MutableInputConnectionWrapper extends InputConnectionWrapper { - public MutableInputConnectionWrapper(InputConnection base) { - super(base); - } - - /** - * Change the base InputConnection for this wrapper. All calls will then be - * delegated to the base input connection. - * - * @param base The new base InputConnection for this wrapper. - */ - public void setBaseInputConnection(InputConnection base) { - mBase = base; - } -} diff --git a/core/java/android/webkit/ByteArrayBuilder.java b/core/java/android/webkit/ByteArrayBuilder.java index 16d663ce390ba..806b458fa8c09 100644 --- a/core/java/android/webkit/ByteArrayBuilder.java +++ b/core/java/android/webkit/ByteArrayBuilder.java @@ -94,6 +94,14 @@ class ByteArrayBuilder { return mChunks.isEmpty(); } + public synchronized void clear() { + Chunk c = getFirstChunk(); + while (c != null) { + releaseChunk(c); + c = getFirstChunk(); + } + } + private Chunk appendChunk(int length) { if (length < mMinCapacity) { length = mMinCapacity; diff --git a/core/java/android/webkit/FileLoader.java b/core/java/android/webkit/FileLoader.java index 10343b2c0e241..54a4c1d12468c 100644 --- a/core/java/android/webkit/FileLoader.java +++ b/core/java/android/webkit/FileLoader.java @@ -76,8 +76,12 @@ class FileLoader extends StreamLoader { protected boolean setupStreamAndSendStatus() { try { if (mIsAsset) { - mDataStream = mContext.getAssets().open(mPath, - AssetManager.ACCESS_STREAMING); + try { + mDataStream = mContext.getAssets().open(mPath); + } catch (java.io.FileNotFoundException ex) { + // try the rest files included in the package + mDataStream = mContext.getAssets().openNonAsset(mPath); + } } else { if (!mAllowFileAccess) { mHandler.error(EventHandler.FILE_ERROR, diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index 7a3bbe6ca32ba..5e323ebb67a57 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -220,6 +220,7 @@ class FrameLoader { // Tell the Listener respond with the cache file CacheLoader cacheLoader = new CacheLoader(mListener, result); + mListener.setCacheLoader(cacheLoader); cacheLoader.load(); } diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index 3f2bbe5a79085..36949695b50ae 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -33,6 +33,8 @@ import android.util.Config; import android.util.Log; import android.webkit.CacheManager.CacheResult; +import com.android.internal.R; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -56,6 +58,9 @@ class LoadListener extends Handler implements EventHandler { private static final int MSG_CONTENT_ERROR = 130; private static final int MSG_LOCATION_CHANGED = 140; private static final int MSG_LOCATION_CHANGED_REQUEST = 150; + private static final int MSG_STATUS = 160; + private static final int MSG_SSL_CERTIFICATE = 170; + private static final int MSG_SSL_ERROR = 180; // Standard HTTP status codes in a more representative format private static final int HTTP_OK = 200; @@ -166,9 +171,7 @@ class LoadListener extends Handler implements EventHandler { * available. The headers are sent onto WebCore to see what we * should do with them. */ - if (mNativeLoader != 0) { - commitHeadersCheckRedirect(); - } + handleHeaders((Headers) msg.obj); break; case MSG_CONTENT_DATA: @@ -177,7 +180,7 @@ class LoadListener extends Handler implements EventHandler { * in it's data buffer. This data buffer could be filled from a * file (this thread) or from http (Network thread). */ - if (mNativeLoader != 0) { + if (mNativeLoader != 0 && !ignoreCallbacks()) { commitLoad(); } break; @@ -189,7 +192,7 @@ class LoadListener extends Handler implements EventHandler { * error. * */ - tearDown(); + handleEndData(); break; case MSG_CONTENT_ERROR: @@ -197,8 +200,7 @@ class LoadListener extends Handler implements EventHandler { * This message is sent when a load error has occured. The * LoadListener will clean itself up. */ - notifyError(); - tearDown(); + handleError(msg.arg1, (String) msg.obj); break; case MSG_LOCATION_CHANGED: @@ -224,6 +226,33 @@ class LoadListener extends Handler implements EventHandler { stopMsg, contMsg); break; + case MSG_STATUS: + /* + * This message is sent from the network thread when the http + * stack has received the status response from the server. + */ + HashMap status = (HashMap) msg.obj; + handleStatus(((Integer) status.get("major")).intValue(), + ((Integer) status.get("minor")).intValue(), + ((Integer) status.get("code")).intValue(), + (String) status.get("reason")); + break; + + case MSG_SSL_CERTIFICATE: + /* + * This message is sent when the network thread receives a ssl + * certificate. + */ + handleCertificate((SslCertificate) msg.obj); + break; + + case MSG_SSL_ERROR: + /* + * This message is sent when the network thread encounters a + * ssl error. + */ + handleSslError((SslError) msg.obj); + break; } } @@ -257,6 +286,11 @@ class LoadListener extends Handler implements EventHandler { */ public void headers(Headers headers) { if (Config.LOGV) Log.v(LOGTAG, "LoadListener.headers"); + sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers)); + } + + // Does the header parsing work on the WebCore thread. + private void handleHeaders(Headers headers) { if (mCancelled) return; mHeaders = headers; mMimeType = ""; @@ -375,7 +409,7 @@ class LoadListener extends Handler implements EventHandler { mCacheResult.encoding = mEncoding; } } - sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS)); + commitHeadersCheckRedirect(); } /** @@ -404,11 +438,20 @@ class LoadListener extends Handler implements EventHandler { + " code: " + code + " reason: " + reasonPhrase); } + HashMap status = new HashMap(); + status.put("major", majorVersion); + status.put("minor", minorVersion); + status.put("code", code); + status.put("reason", reasonPhrase); + sendMessageInternal(obtainMessage(MSG_STATUS, status)); + } + // Handle the status callback on the WebCore thread. + private void handleStatus(int major, int minor, int code, String reason) { if (mCancelled) return; mStatusCode = code; - mStatusText = reasonPhrase; + mStatusText = reason; mPermanent = false; } @@ -418,8 +461,15 @@ class LoadListener extends Handler implements EventHandler { * connection. In this context, can be called multiple * times if we have redirects * @param certificate The SSL certifcate + * IMPORTANT: as this is called from network thread, can't call native + * directly */ public void certificate(SslCertificate certificate) { + sendMessageInternal(obtainMessage(MSG_SSL_CERTIFICATE, certificate)); + } + + // Handle the certificate on the WebCore thread. + private void handleCertificate(SslCertificate certificate) { // if this is the top-most main-frame page loader if (mIsMainPageLoader) { // update the browser frame (ie, the main frame) @@ -436,14 +486,20 @@ class LoadListener extends Handler implements EventHandler { * directly */ public void error(int id, String description) { - mErrorID = id; - mErrorDescription = description; - sendMessageInternal(obtainMessage(MSG_CONTENT_ERROR)); if (Config.LOGV) { Log.v(LOGTAG, "LoadListener.error url:" + url() + " id:" + id + " description:" + description); } + sendMessageInternal(obtainMessage(MSG_CONTENT_ERROR, id, 0, description)); + } + + // Handle the error on the WebCore thread. + private void handleError(int id, String description) { + mErrorID = id; + mErrorDescription = description; detachRequestHandle(); + notifyError(); + tearDown(); } /** @@ -453,11 +509,15 @@ class LoadListener extends Handler implements EventHandler { * @param length The length of data. * IMPORTANT: as this is called from network thread, can't call native * directly + * XXX: Unlike the other network thread methods, this method can do the + * work of decoding the data and appending it to the data builder because + * mDataBuilder is a thread-safe structure. */ public void data(byte[] data, int length) { if (Config.LOGV) { Log.v(LOGTAG, "LoadListener.data(): url: " + url()); } + // Decode base64 data // Note: It's fine that we only decode base64 here and not in the other // data call because the only caller of the stream version is not @@ -479,7 +539,7 @@ class LoadListener extends Handler implements EventHandler { sendMessage = mDataBuilder.isEmpty(); mDataBuilder.append(data, 0, length); } - if (sendMessage && !ignoreCallbacks()) { + if (sendMessage) { // Send a message whenever data comes in after a write to WebCore sendMessageInternal(obtainMessage(MSG_CONTENT_DATA)); } @@ -495,7 +555,11 @@ class LoadListener extends Handler implements EventHandler { if (Config.LOGV) { Log.v(LOGTAG, "LoadListener.endData(): url: " + url()); } + sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED)); + } + // Handle the end of data. + private void handleEndData() { if (mCancelled) return; switch (mStatusCode) { @@ -505,17 +569,13 @@ class LoadListener extends Handler implements EventHandler { case HTTP_FOUND: case HTTP_SEE_OTHER: case HTTP_TEMPORARY_REDIRECT: - if (mMethod == null && mRequestHandle == null) { - Log.e(LOGTAG, "LoadListener.endData(): method is null!"); - Log.e(LOGTAG, "LoadListener.endData(): url = " + url()); - } // 301, 302, 303, and 307 - redirect if (mStatusCode == HTTP_TEMPORARY_REDIRECT) { if (mRequestHandle != null && mRequestHandle.getMethod().equals("POST")) { sendMessageInternal(obtainMessage( MSG_LOCATION_CHANGED_REQUEST)); - } else if (mMethod != null && mMethod.equals("POST")) { + } else if (mMethod.equals("POST")) { sendMessageInternal(obtainMessage( MSG_LOCATION_CHANGED_REQUEST)); } else { @@ -558,9 +618,14 @@ class LoadListener extends Handler implements EventHandler { default: break; } - - sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED)); detachRequestHandle(); + tearDown(); + } + + /* This method is called from CacheLoader when the initial request is + * serviced by the Cache. */ + /* package */ void setCacheLoader(CacheLoader c) { + mCacheLoader = c; } /** @@ -574,9 +639,16 @@ class LoadListener extends Handler implements EventHandler { CacheResult result = CacheManager.getCacheFile(url(), headers); + // Go ahead and set the cache loader to null in case the result is + // null. + mCacheLoader = null; + if (result != null) { - CacheLoader cacheLoader = - new CacheLoader(this, result); + // The contents of the cache may need to be revalidated so just + // remember the cache loader in the case that the server responds + // positively to the cached content. This is also used to detect if + // a redirect came from the cache. + mCacheLoader = new CacheLoader(this, result); // If I got a cachedUrl and the revalidation header was not // added, then the cached content valid, we should use it. @@ -589,14 +661,8 @@ class LoadListener extends Handler implements EventHandler { "and usable: " + url()); } // Load the cached file - cacheLoader.load(); + mCacheLoader.load(); return true; - } else { - // The contents of the cache need to be revalidated - // so just provide the listener with the cache loader - // in the case that the server response positively to - // the cached content. - setCacheLoader(cacheLoader); } } return false; @@ -615,7 +681,11 @@ class LoadListener extends Handler implements EventHandler { " primary error: " + error.getPrimaryError() + " certificate: " + error.getCertificate()); } + sendMessageInternal(obtainMessage(MSG_SSL_ERROR, error)); + } + // Handle the ssl error on the WebCore thread. + private void handleSslError(SslError error) { if (!mCancelled) { mSslError = error; Network.getInstance(mContext).handleSslErrorRequest(this); @@ -704,14 +774,6 @@ class LoadListener extends Handler implements EventHandler { } } - /** - * Set the CacheLoader for the case where we might want to load from cache - * @param result - */ - void setCacheLoader(CacheLoader result) { - mCacheLoader = result; - } - /** * This is called when a request can be satisfied by the cache, however, * the cache result could be a redirect. In this case we need to issue @@ -1002,6 +1064,11 @@ class LoadListener extends Handler implements EventHandler { clearNativeLoader(); } + // This count is transferred from RequestHandle to LoadListener when + // loading from the cache so that we can detect redirect loops that switch + // between the network and the cache. + private int mCacheRedirectCount; + /* * Perform the actual redirection. This involves setting up the new URL, * informing WebCore and then telling the Network to start loading again. @@ -1014,6 +1081,14 @@ class LoadListener extends Handler implements EventHandler { return; } + // Do the same check for a redirect loop that + // RequestHandle.setupRedirect does. + if (mCacheRedirectCount >= RequestHandle.MAX_REDIRECT_COUNT) { + handleError(EventHandler.ERROR_REDIRECT_LOOP, mContext.getString( + R.string.httpErrorRedirectLoop)); + return; + } + String redirectTo = mHeaders.getLocation(); if (redirectTo != null) { int nativeResponse = createNativeResponse(); @@ -1031,7 +1106,7 @@ class LoadListener extends Handler implements EventHandler { return; } else if (!URLUtil.isNetworkUrl(redirectTo)) { final String text = mContext - .getString(com.android.internal.R.string.open_permission_deny) + .getString(R.string.open_permission_deny) + "\n" + redirectTo; nativeAddData(text.getBytes(), text.length()); nativeFinished(); @@ -1057,6 +1132,13 @@ class LoadListener extends Handler implements EventHandler { if (mRequestHeaders == null) { mRequestHeaders = new HashMap(); } + boolean fromCache = false; + if (mCacheLoader != null) { + // This is a redirect from the cache loader. Increment the + // redirect count to avoid redirect loops. + mCacheRedirectCount++; + fromCache = true; + } if (!checkCache(mRequestHeaders)) { // mRequestHandle can be null when the request was satisfied // by the cache, and the cache returned a redirect @@ -1064,23 +1146,37 @@ class LoadListener extends Handler implements EventHandler { mRequestHandle.setupRedirect(redirectTo, mStatusCode, mRequestHeaders); } else { - String method = mMethod; - - if (method == null) { + // If the original request came from the cache, there is no + // RequestHandle, we have to create a new one through + // Network.requestURL. + Network network = Network.getInstance(getContext()); + if (!network.requestURL(mMethod, mRequestHeaders, + mPostData, this, mIsHighPriority)) { + // Signal a bad url error if we could not load the + // redirection. + handleError(EventHandler.ERROR_BAD_URL, + mContext.getString(R.string.httpErrorBadUrl)); return; } - - Network network = Network.getInstance(getContext()); - network.requestURL(method, mRequestHeaders, - mPostData, this, mIsHighPriority); } + if (fromCache) { + // If we are coming from a cache load, we need to transfer + // the redirect count to the new (or old) RequestHandle to + // keep the redirect count in sync. + mRequestHandle.setRedirectCount(mCacheRedirectCount); + } + } else if (!fromCache) { + // Switching from network to cache means we need to grab the + // redirect count from the RequestHandle to keep the count in + // sync. Add 1 to account for the current redirect. + mCacheRedirectCount = mRequestHandle.getRedirectCount() + 1; } + // Clear the buffered data since the redirect is valid. + mDataBuilder.clear(); } else { - // With a null redirect, commit the original headers, the buffered - // data, and then finish the load. commitHeaders(); commitLoad(); - nativeFinished(); + tearDown(); } if (Config.LOGV) { @@ -1167,14 +1263,16 @@ class LoadListener extends Handler implements EventHandler { quoted = !quoted; } else { if (!quoted) { - if (header.startsWith( - HttpAuthHeader.BASIC_TOKEN, i)) { + if (header.regionMatches(true, i, + HttpAuthHeader.BASIC_TOKEN, 0, + HttpAuthHeader.BASIC_TOKEN.length())) { pos[posLen++] = i; continue; } - if (header.startsWith( - HttpAuthHeader.DIGEST_TOKEN, i)) { + if (header.regionMatches(true, i, + HttpAuthHeader.DIGEST_TOKEN, 0, + HttpAuthHeader.DIGEST_TOKEN.length())) { pos[posLen++] = i; continue; } @@ -1186,8 +1284,9 @@ class LoadListener extends Handler implements EventHandler { if (posLen > 0) { // consider all digest schemes first (if any) for (int i = 0; i < posLen; i++) { - if (header.startsWith(HttpAuthHeader.DIGEST_TOKEN, - pos[i])) { + if (header.regionMatches(true, pos[i], + HttpAuthHeader.DIGEST_TOKEN, 0, + HttpAuthHeader.DIGEST_TOKEN.length())) { String sub = header.substring(pos[i], (i + 1 < posLen ? pos[i + 1] : headerLen)); @@ -1201,7 +1300,9 @@ class LoadListener extends Handler implements EventHandler { // ...then consider all basic schemes (if any) for (int i = 0; i < posLen; i++) { - if (header.startsWith(HttpAuthHeader.BASIC_TOKEN, pos[i])) { + if (header.regionMatches(true, pos[i], + HttpAuthHeader.BASIC_TOKEN, 0, + HttpAuthHeader.BASIC_TOKEN.length())) { String sub = header.substring(pos[i], (i + 1 < posLen ? pos[i + 1] : headerLen)); @@ -1262,9 +1363,8 @@ class LoadListener extends Handler implements EventHandler { // type (implying text/plain). if (URLUtil.isDataUrl(mUrl) && mMimeType.length() != 0) { cancel(); - final String text = mContext.getString( - com.android.internal.R.string.httpErrorBadUrl); - error(EventHandler.ERROR_BAD_URL, text); + final String text = mContext.getString(R.string.httpErrorBadUrl); + handleError(EventHandler.ERROR_BAD_URL, text); } else { // Note: This is ok because this is used only for the main content // of frames. If no content-type was specified, it is fine to diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java index 685e3528f7fac..c9cc20817c4fd 100644 --- a/core/java/android/webkit/MimeTypeMap.java +++ b/core/java/android/webkit/MimeTypeMap.java @@ -480,6 +480,7 @@ public /* package */ class MimeTypeMap { sMimeTypeMap.loadEntry("video/mpeg", "mpg", false); sMimeTypeMap.loadEntry("video/mpeg", "mpe", false); sMimeTypeMap.loadEntry("video/mp4", "mp4", false); + sMimeTypeMap.loadEntry("video/mpeg", "VOB", false); sMimeTypeMap.loadEntry("video/quicktime", "qt", false); sMimeTypeMap.loadEntry("video/quicktime", "mov", false); sMimeTypeMap.loadEntry("video/vnd.mpegurl", "mxu", false); diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java index b7b40b17059a5..9af30c5b740c1 100644 --- a/core/java/android/webkit/TextDialog.java +++ b/core/java/android/webkit/TextDialog.java @@ -33,7 +33,6 @@ import android.text.Selection; import android.text.Spannable; import android.text.TextPaint; import android.text.TextUtils; -import android.text.method.MetaKeyKeyListener; import android.text.method.MovementMethod; import android.text.method.PasswordTransformationMethod; import android.text.method.TextKeyListener; @@ -44,7 +43,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.View.MeasureSpec; import android.view.ViewConfiguration; import android.widget.AbsoluteLayout.LayoutParams; import android.widget.ArrayAdapter; @@ -69,10 +67,8 @@ import java.util.ArrayList; // on the enter key. The method for blocking unmatched key ups prevents // the shift key from working properly. private boolean mGotEnterDown; - // Determines whether we allow calls to requestRectangleOnScreen to - // propagate. We only want to scroll if the user is typing. If the - // user is simply navigating through a textfield, we do not want to - // scroll. + // mScrollToAccommodateCursor being set to false prevents us from scrolling + // the cursor on screen when using the trackball to select a textfield. private boolean mScrollToAccommodateCursor; private int mMaxLength; // Keep track of the text before the change so we know whether we actually @@ -183,6 +179,11 @@ import java.util.ArrayList; mWebView.shortPressOnTextField(); return true; } + // If we reached here, then this is a single line textfield, and + // the user pressed ENTER. In this case, we want to hide the + // soft input method. + InputMethodManager.getInstance(mContext) + .hideSoftInputFromWindow(getWindowToken(), 0); sendDomEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); sendDomEvent(event); } @@ -405,6 +406,12 @@ import java.util.ArrayList; //mWebView.setSelection(start, end); return true; } + // If the user is in a textfield, and the movement method is not + // handling the trackball events, it means they are at the end of the + // field and continuing to move the trackball. In this case, we should + // not scroll the cursor on screen bc the user may be attempting to + // scroll the page, possibly in the opposite direction of the cursor. + mScrollToAccommodateCursor = false; return false; } @@ -419,6 +426,17 @@ import java.util.ArrayList; mHandler.removeMessages(LONGPRESS); mWebView.removeView(this); mWebView.requestFocus(); + mScrollToAccommodateCursor = false; + } + + /* package */ void enableScrollOnScreen(boolean enable) { + mScrollToAccommodateCursor = enable; + } + + /* package */ void bringIntoView() { + if (getLayout() != null) { + bringPointIntoView(Selection.getSelectionEnd(getText())); + } } @Override @@ -437,8 +455,16 @@ import java.util.ArrayList; mWebView.passToJavaScript(getText().toString(), event); } + /** + * Always use this instead of setAdapter, as this has features specific to + * the TextDialog. + */ public void setAdapterCustom(AutoCompleteAdapter adapter) { - adapter.setTextView(this); + if (adapter != null) { + adapter.setTextView(this); + } else { + setInputType(EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE); + } super.setAdapter(adapter); } @@ -483,12 +509,12 @@ import java.util.ArrayList; PasswordTransformationMethod method; if (inPassword) { method = PasswordTransformationMethod.getInstance(); + setInputType(EditorInfo.TYPE_CLASS_TEXT|EditorInfo. + TYPE_TEXT_VARIATION_PASSWORD); } else { method = null; } setTransformationMethod(method); - setInputType(inPassword ? EditorInfo.TYPE_TEXT_VARIATION_PASSWORD : - EditorInfo.TYPE_CLASS_TEXT); } /* package */ void setMaxLength(int maxLength) { @@ -539,7 +565,6 @@ import java.util.ArrayList; // Set up a measure spec so a layout can always be recreated. mWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); mHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - mScrollToAccommodateCursor = false; requestFocus(); } @@ -547,18 +572,23 @@ import java.util.ArrayList; * Set whether this is a single-line textfield or a multi-line textarea. * Textfields scroll horizontally, and do not handle the enter key. * Textareas behave oppositely. + * Do NOT call this after calling setInPassword(true). This will result in + * removing the password input type. */ public void setSingleLine(boolean single) { if (mSingle != single) { TextKeyListener.Capitalize cap; + int inputType = EditorInfo.TYPE_CLASS_TEXT; if (single) { cap = TextKeyListener.Capitalize.NONE; } else { cap = TextKeyListener.Capitalize.SENTENCES; + inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; } setKeyListener(TextKeyListener.getInstance(!single, cap)); mSingle = single; setHorizontallyScrolling(single); + setInputType(inputType); } } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 1a7c4ff364776..5c470cf1bf15f 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -316,10 +316,15 @@ public class WebSettings { buffer.append("en"); } - final String device = Build.DEVICE; - if (device.length() > 0) { + final String model = Build.MODEL; + if (model.length() > 0) { buffer.append("; "); - buffer.append(device); + buffer.append(model); + } + final String id = Build.ID; + if (id.length() > 0) { + buffer.append(" Build/"); + buffer.append(id); } final String base = mContext.getResources().getText( com.android.internal.R.string.web_user_agent).toString(); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index cab278beb3755..3306700424680 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -68,6 +68,8 @@ import android.widget.RelativeLayout; import android.widget.Scroller; import android.widget.Toast; import android.widget.ZoomControls; +import android.widget.ZoomRingController; +import android.widget.FrameLayout; import android.widget.AdapterView.OnItemClickListener; import java.io.File; @@ -107,17 +109,14 @@ public class WebView extends AbsoluteLayout static final boolean DEBUG = false; static final boolean LOGV_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; - private class ExtendedZoomControls extends RelativeLayout { + private class ExtendedZoomControls extends FrameLayout { public ExtendedZoomControls(Context context, AttributeSet attrs) { super(context, attrs); - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(com.android.internal.R.layout.zoom_magnify, this - , true); - mZoomControls = (ZoomControls) findViewById - (com.android.internal.R.id.zoomControls); - mZoomMagnify = (ImageView) findViewById - (com.android.internal.R.id.zoomMagnify); + LayoutInflater inflater = (LayoutInflater) + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true); + mZoomControls = (ZoomControls) findViewById(com.android.internal.R.id.zoomControls); + mZoomMagnify = (ImageView) findViewById(com.android.internal.R.id.zoomMagnify); } public void show(boolean showZoom, boolean canZoomOut) { @@ -258,6 +257,27 @@ public class WebView extends AbsoluteLayout // Whether to forward the touch events to WebCore private boolean mForwardTouchEvents = false; + // Whether we are in the drag tap mode, which exists starting at the second + // tap's down, through its move, and includes its up. These events should be + // given to the method on the zoom controller. + private boolean mInZoomTapDragMode; + + // The event time of the previous touch up. + private long mPreviousUpTime; + + private Runnable mRemoveReleaseSingleTap = new Runnable() { + public void run() { + mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); + mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); + mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); + } + }; + + // Whether to prevent drag during touch. The initial value depends on + // mForwardTouchEvents. If WebCore wants touch events, we assume it will + // take control of touch events unless it says no for touch down event. + private boolean mPreventDrag; + // If updateTextEntry gets called while we are out of focus, use this // variable to remember to do it next time we gain focus. private boolean mNeedsUpdateTextEntry = false; @@ -269,8 +289,7 @@ public class WebView extends AbsoluteLayout * Customizable constant */ // pre-computed square of ViewConfiguration.getTouchSlop() - private static final int TOUCH_SLOP_SQUARE = - ViewConfiguration.getTouchSlop() * ViewConfiguration.getTouchSlop(); + private int mTouchSlopSquare; // This should be ViewConfiguration.getTapTimeout() // But system time out is 100ms, which is too short for the browser. // In the browser, if it switches out of tap too soon, jump tap won't work. @@ -360,6 +379,8 @@ public class WebView extends AbsoluteLayout static final int LONG_PRESS_ENTER = 23; static final int PREVENT_TOUCH_ID = 24; static final int WEBCORE_NEED_TOUCH_EVENTS = 25; + // obj=Rect in doc coordinates + static final int INVAL_RECT_MSG_ID = 26; // width which view is considered to be fully zoomed out static final int ZOOM_OUT_WIDTH = 1024; @@ -373,6 +394,9 @@ public class WebView extends AbsoluteLayout // initial scale in percent. 0 means using default. private int mInitialScale = 0; + // set to true temporarily while the zoom control is being dragged + private boolean mPreviewZoomOnly = false; + // computed scale and inverse, from mZoomWidth. private float mActualScale = 1; private float mInvActualScale = 1; @@ -490,7 +514,73 @@ public class WebView extends AbsoluteLayout return mExtra; } } + + private ZoomRingController mZoomRingController; + private ZoomRingController.OnZoomListener mZoomListener = + new ZoomRingController.OnZoomListener() { + + public void onCenter(int x, int y) { + // Don't translate when the control is invoked, hence we do nothing + // in this callback + } + + public boolean onPan(int deltaX, int deltaY) { + return pinScrollBy(deltaX, deltaY, false, 0); + } + + public void onVisibilityChanged(boolean visible) { + if (visible) { + mZoomControls.show(false, canZoomScrollOut()); + } else { + mZoomControls.hide(); + } + } + + public void onBeginDrag(float startAngle) { + mPreviewZoomOnly = true; + } + + public void onEndDrag(float endAngle) { + mPreviewZoomOnly = false; + setNewZoomScale(mActualScale, true); + } + + public boolean onDragZoom(int deltaZoomLevel, int centerX, + int centerY, float startAngle, float curAngle) { + + if (mZoomScale == mMinZoomScale && deltaZoomLevel < 0 || + mZoomScale == mMaxZoomScale && deltaZoomLevel > 0 || + deltaZoomLevel == 0) { + return false; + } + + int deltaX = centerX - getViewWidth() / 2; + int deltaY = centerY - getViewHeight() / 2; + + pinScrollBy(deltaX, deltaY, false, 0); + + while (deltaZoomLevel != 0) { + if (deltaZoomLevel > 0) { + if (!zoomIn()) return false; + deltaZoomLevel--; + } else { + if (!zoomOut()) return false; + deltaZoomLevel++; + } + } + + pinScrollBy(-deltaX, -deltaY, false, 0); + + return true; + } + + public void onSimpleZoom(boolean zoomIn) { + if (zoomIn) zoomIn(); + else zoomOut(); + } + }; + /** * Construct a new WebView with a Context object. * @param context A Context object used to access application assets. @@ -518,11 +608,6 @@ public class WebView extends AbsoluteLayout super(context, attrs, defStyle); init(); - TypedArray a = context.obtainStyledAttributes( - com.android.internal.R.styleable.View); - initializeScrollbars(a); - a.recycle(); - mCallbackProxy = new CallbackProxy(context, this); mWebViewCore = new WebViewCore(context, this, mCallbackProxy); mDatabase = WebViewDatabase.getInstance(context); @@ -532,6 +617,8 @@ public class WebView extends AbsoluteLayout mFocusData.mX = 0; mFocusData.mY = 0; mScroller = new Scroller(context); + mZoomRingController = new ZoomRingController(context, this); + mZoomRingController.setCallback(mZoomListener); } private void init() { @@ -541,9 +628,9 @@ public class WebView extends AbsoluteLayout setClickable(true); setLongClickable(true); - // should be conditional on if we're in the browser activity? - setHorizontalScrollBarEnabled(true); - setVerticalScrollBarEnabled(true); + final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + mTouchSlopSquare = slop * slop; + mMinLockSnapReverseDistance = slop; } /* package */ boolean onSavePassword(String schemePlusHost, String username, @@ -648,7 +735,7 @@ public class WebView extends AbsoluteLayout * to. */ private int getViewWidth() { - if (mOverlayVerticalScrollbar) { + if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) { return getWidth(); } else { return getWidth() - getVerticalScrollbarWidth(); @@ -660,7 +747,7 @@ public class WebView extends AbsoluteLayout * to. */ private int getViewHeight() { - if (mOverlayHorizontalScrollbar) { + if (!isHorizontalScrollBarEnabled() || mOverlayHorizontalScrollbar) { return getHeight(); } else { return getHeight() - getHorizontalScrollbarHeight(); @@ -732,7 +819,6 @@ public class WebView extends AbsoluteLayout */ public void destroy() { clearTextEntry(); - getViewTreeObserver().removeOnGlobalFocusChangeListener(this); if (mWebViewCore != null) { // Set the handlers to null before destroying WebViewCore so no // more messages will be posted. @@ -1165,7 +1251,7 @@ public class WebView extends AbsoluteLayout nativeClearFocus(-1, -1); if (top) { // go to the top of the document - return pinScrollTo(mScrollX, 0, true); + return pinScrollTo(mScrollX, 0, true, 0); } // Page up int h = getHeight(); @@ -1176,7 +1262,7 @@ public class WebView extends AbsoluteLayout y = -h / 2; } mUserScroll = true; - return mScroller.isFinished() ? pinScrollBy(0, y, true) + return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) : extendScroll(y); } @@ -1191,7 +1277,7 @@ public class WebView extends AbsoluteLayout } nativeClearFocus(-1, -1); if (bottom) { - return pinScrollTo(mScrollX, mContentHeight, true); + return pinScrollTo(mScrollX, mContentHeight, true, 0); } // Page down. int h = getHeight(); @@ -1202,7 +1288,7 @@ public class WebView extends AbsoluteLayout y = h / 2; } mUserScroll = true; - return mScroller.isFinished() ? pinScrollBy(0, y, true) + return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) : extendScroll(y); } @@ -1485,12 +1571,14 @@ public class WebView extends AbsoluteLayout if (mDrawHistory) { // If history Picture is drawn, don't update scroll. They will // be updated when we get out of that mode. - if (scale != mActualScale) { + if (scale != mActualScale && !mPreviewZoomOnly) { mCallbackProxy.onScaleChanged(mActualScale, scale); } mActualScale = scale; mInvActualScale = 1 / scale; - sendViewSizeZoom(); + if (!mPreviewZoomOnly) { + sendViewSizeZoom(); + } } else { // update our scroll so we don't appear to jump // i.e. keep the center of the doc in the center of the view @@ -1502,7 +1590,7 @@ public class WebView extends AbsoluteLayout float sy = ratio * oldY + (ratio - 1) * getViewHeight() * 0.5f; // now update our new scale and inverse - if (scale != mActualScale) { + if (scale != mActualScale && !mPreviewZoomOnly) { mCallbackProxy.onScaleChanged(mActualScale, scale); } mActualScale = scale; @@ -1514,8 +1602,10 @@ public class WebView extends AbsoluteLayout mScrollX = pinLocX(Math.round(sx)); mScrollY = pinLocY(Math.round(sy)); - sendViewSizeZoom(); - sendOurVisibleRect(); + if (!mPreviewZoomOnly) { + sendViewSizeZoom(); + sendOurVisibleRect(); + } } } } @@ -1716,8 +1806,8 @@ public class WebView extends AbsoluteLayout */ public void clearFormData() { if (inEditingMode()) { - ArrayAdapter adapter = null; - mTextEntry.setAdapter(adapter); + AutoCompleteAdapter adapter = null; + mTextEntry.setAdapterCustom(adapter); } } @@ -1812,7 +1902,7 @@ public class WebView extends AbsoluteLayout nativeSetFindIsDown(); // Now that the dialog has been removed, ensure that we scroll to a // location that is not beyond the end of the page. - pinScrollTo(mScrollX, mScrollY, false); + pinScrollTo(mScrollX, mScrollY, false, 0); invalidate(); } @@ -1855,13 +1945,13 @@ public class WebView extends AbsoluteLayout // helper to pin the scrollBy parameters (already in view coordinates) // returns true if the scroll was changed - private boolean pinScrollBy(int dx, int dy, boolean animate) { - return pinScrollTo(mScrollX + dx, mScrollY + dy, animate); + private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) { + return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration); } // helper to pin the scrollTo parameters (already in view coordinates) // returns true if the scroll was changed - private boolean pinScrollTo(int x, int y, boolean animate) { + private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) { x = pinLocX(x); y = pinLocY(y); int dx = x - mScrollX; @@ -1875,7 +1965,7 @@ public class WebView extends AbsoluteLayout // Log.d(LOGTAG, "startScroll: " + dx + " " + dy); mScroller.startScroll(mScrollX, mScrollY, dx, dy, - computeDuration(dx, dy)); + animationDuration > 0 ? animationDuration : computeDuration(dx, dy)); invalidate(); } else { mScroller.abortAnimation(); // just in case @@ -1910,10 +2000,10 @@ public class WebView extends AbsoluteLayout // vertical scroll? // Log.d(LOGTAG, "setContentScrollBy cy=" + cy); if (cy == 0 && cx != 0) { - pinScrollBy(cx, 0, true); + pinScrollBy(cx, 0, true, 0); } } else { - pinScrollBy(cx, cy, true); + pinScrollBy(cx, cy, true, 0); } } @@ -1933,7 +2023,7 @@ public class WebView extends AbsoluteLayout int vy = contentToView(cy); // Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" + // vx + " " + vy + "]"); - pinScrollTo(vx, vy, false); + pinScrollTo(vx, vy, false, 0); if (mScrollX != vx || mScrollY != vy) { return true; } else { @@ -1950,7 +2040,7 @@ public class WebView extends AbsoluteLayout } int vx = contentToView(cx); int vy = contentToView(cy); - pinScrollTo(vx, vy, true); + pinScrollTo(vx, vy, true, 0); } /** @@ -2692,12 +2782,9 @@ public class WebView extends AbsoluteLayout InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(mTextEntry, 0); + mTextEntry.enableScrollOnScreen(true); } - // Used to register the global focus change listener one time to avoid - // multiple references to WebView - private boolean mGlobalFocusChangeListenerAdded; - private void updateTextEntry() { if (mTextEntry == null) { mTextEntry = new TextDialog(mContext, WebView.this); @@ -2730,9 +2817,6 @@ public class WebView extends AbsoluteLayout // Note that sendOurVisibleRect calls viewToContent, so the coordinates // should be in content coordinates. if (!Rect.intersects(node.mBounds, visibleRect)) { - if (alreadyThere) { - mTextEntry.remove(); - } // Node is not on screen, so do not bother. return; } @@ -2785,21 +2869,32 @@ public class WebView extends AbsoluteLayout } } mTextEntry.setMaxLength(maxLength); - ArrayAdapter adapter = null; - mTextEntry.setAdapter(adapter); - mTextEntry.setInPassword(node.mIsPassword); + AutoCompleteAdapter adapter = null; + mTextEntry.setAdapterCustom(adapter); mTextEntry.setSingleLine(node.mIsTextField); + mTextEntry.setInPassword(node.mIsPassword); if (null == text) { mTextEntry.setText("", 0, 0); } else { - mTextEntry.setText(text, 0, text.length()); + // Change to true to enable the old style behavior, where + // entering a textfield/textarea always set the selection to the + // whole field. This was desirable for the case where the user + // intends to scroll past the field using the trackball. + // However, it causes a problem when replying to emails - the + // user expects the cursor to be at the beginning of the + // textarea. Testing out a new behavior, where textfields set + // selection at the end, and textareas at the beginning. + if (false) { + mTextEntry.setText(text, 0, text.length()); + } else if (node.mIsTextField) { + int length = text.length(); + mTextEntry.setText(text, length, length); + } else { + mTextEntry.setText(text, 0, 0); + } } mTextEntry.requestFocus(); } - if (!mGlobalFocusChangeListenerAdded) { - getViewTreeObserver().addOnGlobalFocusChangeListener(this); - mGlobalFocusChangeListenerAdded = true; - } } private class UpdateTextEntryAdapter implements Runnable { @@ -3114,24 +3209,17 @@ public class WebView extends AbsoluteLayout // Implementation for OnHierarchyChangeListener public void onChildViewAdded(View parent, View child) {} - // When we are removed, remove this as a global focus change listener. public void onChildViewRemoved(View p, View child) { if (child == this) { - p.getViewTreeObserver().removeOnGlobalFocusChangeListener(this); - mGlobalFocusChangeListenerAdded = false; + if (inEditingMode()) { + clearTextEntry(); + mNeedsUpdateTextEntry = true; + } } } - - // Use this to know when the textview has lost focus, and something other - // than the webview has gained focus. Stop drawing the focus ring, remove - // the TextView, and set a flag to put it back when we regain focus. + + @Deprecated public void onGlobalFocusChanged(View oldFocus, View newFocus) { - if (oldFocus == mTextEntry && newFocus != this) { - mDrawFocusRing = false; - mTextEntry.updateCachedTextfield(); - removeView(mTextEntry); - mNeedsUpdateTextEntry = true; - } } // To avoid drawing the focus ring, and remove the TextView when our window @@ -3153,16 +3241,10 @@ public class WebView extends AbsoluteLayout mDrawFocusRing = false; } } else { - // If our window has lost focus, stop drawing the focus ring, and - // remove the TextView if displayed, and flag it to be added when - // we regain focus. + // If our window has lost focus, stop drawing the focus ring mDrawFocusRing = false; mGotKeyDown = false; mShiftIsPressed = false; - if (inEditingMode()) { - clearTextEntry(); - mNeedsUpdateTextEntry = true; - } } invalidate(); super.onWindowFocusChanged(hasWindowFocus); @@ -3245,9 +3327,8 @@ public class WebView extends AbsoluteLayout // 3. If there is a same direction back and forth, lock it. // adjustable parameters + private int mMinLockSnapReverseDistance; private static final float MAX_SLOPE_FOR_DIAG = 1.5f; - private static final int MIN_LOCK_SNAP_REVERSE_DISTANCE = - ViewConfiguration.getTouchSlop(); private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80; @Override @@ -3261,6 +3342,27 @@ public class WebView extends AbsoluteLayout + mTouchMode); } + if (mZoomRingController.isVisible()) { + if (mInZoomTapDragMode) { + mZoomRingController.handleDoubleTapEvent(ev); + if (ev.getAction() == MotionEvent.ACTION_UP) { + // Just released the second tap, no longer in tap-drag mode + mInZoomTapDragMode = false; + } + return true; + } else { + // TODO: properly do this. + /* + * When the zoom widget is showing, the user can tap outside of + * it to dismiss it. Furthermore, he can drag outside of it to + * pan the browser. However, we do not want a tap on a link to + * open the link. + */ + post(mRemoveReleaseSingleTap); + // Continue through to normal processing + } + } + int action = ev.getAction(); float x = ev.getX(); float y = ev.getY(); @@ -3315,8 +3417,20 @@ public class WebView extends AbsoluteLayout nativeMoveSelection(viewToContent(mSelectX) , viewToContent(mSelectY), false); mTouchSelection = mExtendSelection = true; + } else if (!ZoomRingController.useOldZoom(mContext) && + eventTime - mPreviousUpTime < DOUBLE_TAP_TIMEOUT && + getSettings().supportZoom() && + mMinZoomScale < mMaxZoomScale) { + // Found doubletap, invoke the zoom controller + mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); + mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); + mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); + mZoomRingController.setVisible(true); + mInZoomTapDragMode = true; + mZoomRingController.handleDoubleTapEvent(ev); } else { mTouchMode = TOUCH_INIT_MODE; + mPreventDrag = mForwardTouchEvents; } if (mTouchMode == TOUCH_INIT_MODE) { mPrivateHandler.sendMessageDelayed(mPrivateHandler @@ -3359,8 +3473,8 @@ public class WebView extends AbsoluteLayout invalidate(); break; } - if ((deltaX * deltaX + deltaY * deltaY) - < TOUCH_SLOP_SQUARE) { + if (mPreventDrag || (deltaX * deltaX + deltaY * deltaY) + < mTouchSlopSquare) { break; } @@ -3371,6 +3485,9 @@ public class WebView extends AbsoluteLayout mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); } + // Prevent double-tap from being invoked + mPreviousUpTime = 0; + // if it starts nearly horizontal or vertical, enforce it int ax = Math.abs(deltaX); int ay = Math.abs(deltaY); @@ -3418,9 +3535,9 @@ public class WebView extends AbsoluteLayout // reverse direction means lock in the snap mode if ((ax > MAX_SLOPE_FOR_DIAG * ay) && ((mSnapPositive && - deltaX < -MIN_LOCK_SNAP_REVERSE_DISTANCE) + deltaX < -mMinLockSnapReverseDistance) || (!mSnapPositive && - deltaX > MIN_LOCK_SNAP_REVERSE_DISTANCE))) { + deltaX > mMinLockSnapReverseDistance))) { mSnapScrollMode = SNAP_X_LOCK; } } else { @@ -3432,9 +3549,9 @@ public class WebView extends AbsoluteLayout // reverse direction means lock in the snap mode if ((ay > MAX_SLOPE_FOR_DIAG * ax) && ((mSnapPositive && - deltaY < -MIN_LOCK_SNAP_REVERSE_DISTANCE) + deltaY < -mMinLockSnapReverseDistance) || (!mSnapPositive && - deltaY > MIN_LOCK_SNAP_REVERSE_DISTANCE))) { + deltaY > mMinLockSnapReverseDistance))) { mSnapScrollMode = SNAP_Y_LOCK; } } @@ -3457,16 +3574,18 @@ public class WebView extends AbsoluteLayout mUserScroll = true; } - boolean showPlusMinus = mMinZoomScale < mMaxZoomScale; - boolean showMagnify = canZoomScrollOut(); - if (mZoomControls != null && (showPlusMinus || showMagnify)) { - if (mZoomControls.getVisibility() == View.VISIBLE) { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - } else { - mZoomControls.show(showPlusMinus, showMagnify); + if (ZoomRingController.useOldZoom(mContext)) { + boolean showPlusMinus = mMinZoomScale < mMaxZoomScale; + boolean showMagnify = canZoomScrollOut(); + if (mZoomControls != null && (showPlusMinus || showMagnify)) { + if (mZoomControls.getVisibility() == View.VISIBLE) { + mPrivateHandler.removeCallbacks(mZoomControlRunnable); + } else { + mZoomControls.show(showPlusMinus, showMagnify); + } + mPrivateHandler.postDelayed(mZoomControlRunnable, + ZOOM_CONTROLS_TIMEOUT); } - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); } if (done) { // return false to indicate that we can't pan out of the @@ -3479,7 +3598,8 @@ public class WebView extends AbsoluteLayout switch (mTouchMode) { case TOUCH_INIT_MODE: // tap mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - if (getSettings().supportZoom()) { + if (getSettings().supportZoom() + && (mMinZoomScale < mMaxZoomScale)) { mPrivateHandler.sendMessageDelayed(mPrivateHandler .obtainMessage(RELEASE_SINGLE_TAP), DOUBLE_TAP_TIMEOUT); @@ -3559,6 +3679,7 @@ public class WebView extends AbsoluteLayout mVelocityTracker.recycle(); mVelocityTracker = null; } + mPreviousUpTime = eventTime; break; } case MotionEvent.ACTION_CANCEL: { @@ -3604,6 +3725,7 @@ public class WebView extends AbsoluteLayout private static final int TRACKBALL_WAIT = 100; private static final int TRACKBALL_SCALE = 400; private static final int TRACKBALL_SCROLL_COUNT = 5; + private static final int TRACKBALL_MOVE_COUNT = 10; private static final int TRACKBALL_MULTIPLIER = 3; private static final int SELECT_CURSOR_OFFSET = 16; private int mSelectX = 0; @@ -3658,7 +3780,11 @@ public class WebView extends AbsoluteLayout mTrackballDown = false; mTrackballUpTime = time; if (mShiftIsPressed) { - mExtendSelection = true; + if (mExtendSelection) { + commitCopy(); + } else { + mExtendSelection = true; + } } if (LOGV_ENABLED) { Log.v(LOGTAG, "onTrackballEvent up ev=" + ev @@ -3735,7 +3861,7 @@ public class WebView extends AbsoluteLayout int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET : 0; - pinScrollBy(scrollX, scrollY, true); + pinScrollBy(scrollX, scrollY, true, 0); Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1); requestRectangleOnScreen(select); invalidate(); @@ -3841,6 +3967,7 @@ public class WebView extends AbsoluteLayout KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN : mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT; + count = Math.min(count, TRACKBALL_MOVE_COUNT); if (LOGV_ENABLED) { Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode + " count=" + count @@ -3870,7 +3997,7 @@ public class WebView extends AbsoluteLayout yMove = 0; } if (xMove != 0 || yMove != 0) { - pinScrollBy(xMove, yMove, true); + pinScrollBy(xMove, yMove, true, 0); } mUserScroll = true; } @@ -4056,7 +4183,7 @@ public class WebView extends AbsoluteLayout View v = mTextEntry; int x = viewToContent((v.getLeft() + v.getRight()) >> 1); int y = viewToContent((v.getTop() + v.getBottom()) >> 1); - int contentSize = ViewConfiguration.getTouchSlop(); + int contentSize = ViewConfiguration.get(getContext()).getScaledTouchSlop(); nativeMotionUp(x, y, contentSize, true); } } @@ -4066,21 +4193,15 @@ public class WebView extends AbsoluteLayout return; } switchOutDrawHistory(); - // call uiOverride to check whether it is a special node, - // phone/email/address, which are not handled by WebKit + // FIXME: we don't know if the current (x,y) is on a focus node or + // not -- so playing the sound effect here is premature if (nativeUpdateFocusNode()) { - FocusNode node = mFocusNode; - if (!node.mIsTextField && !node.mIsTextArea) { - if (mCallbackProxy.uiOverrideUrlLoading(node.mText)) { - return; - } - } playSoundEffect(SoundEffectConstants.CLICK); } // mLastTouchX and mLastTouchY are the point in the current viewport int contentX = viewToContent((int) mLastTouchX + mScrollX); int contentY = viewToContent((int) mLastTouchY + mScrollY); - int contentSize = ViewConfiguration.getTouchSlop(); + int contentSize = ViewConfiguration.get(getContext()).getScaledTouchSlop(); nativeMotionUp(contentX, contentY, contentSize, true); } @@ -4208,7 +4329,7 @@ public class WebView extends AbsoluteLayout } if ((scrollYDelta | scrollXDelta) != 0) { - return pinScrollBy(scrollXDelta, scrollYDelta, !immediate); + return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0); } return false; @@ -4443,6 +4564,15 @@ public class WebView extends AbsoluteLayout } break; case UPDATE_TEXT_ENTRY_MSG_ID: + // this is sent after finishing resize in WebViewCore. Make + // sure the text edit box is still on the screen. + boolean alreadyThere = inEditingMode(); + if (alreadyThere && nativeUpdateFocusNode()) { + FocusNode node = mFocusNode; + if (node.mIsTextField || node.mIsTextArea) { + mTextEntry.bringIntoView(); + } + } updateTextEntry(); break; case RECOMPUTE_FOCUS_MSG_ID: @@ -4450,6 +4580,17 @@ public class WebView extends AbsoluteLayout nativeRecomputeFocus(); } break; + case INVAL_RECT_MSG_ID: { + Rect r = (Rect)msg.obj; + if (r == null) { + invalidate(); + } else { + // we need to scale r from content into view coords, + // which viewInvalidate() does for us + viewInvalidate(r.left, r.top, r.right, r.bottom); + } + break; + } case UPDATE_TEXT_ENTRY_ADAPTER: HashMap data = (HashMap) msg.obj; if (mTextEntry.isSameTextField(msg.arg1)) { @@ -4494,12 +4635,12 @@ public class WebView extends AbsoluteLayout break; case PREVENT_TOUCH_ID: - // update may have already been paused by touch; restore since - // this effectively aborts touch and skips logic in touch up - if (mTouchMode == TOUCH_DRAG_MODE) { - WebViewCore.resumeUpdate(mWebViewCore); + if (msg.arg1 == MotionEvent.ACTION_DOWN) { + mPreventDrag = msg.arg2 == 1; + if (mPreventDrag) { + mTouchMode = TOUCH_DONE_MODE; + } } - mTouchMode = TOUCH_DONE_MODE; break; default: @@ -4665,6 +4806,12 @@ public class WebView extends AbsoluteLayout listView.setSelection(mSelection); } } + dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + mWebViewCore.sendMessage( + EventHub.SINGLE_LISTBOX_CHOICE, -2, 0); + } + }); dialog.show(); } } @@ -4809,11 +4956,11 @@ public class WebView extends AbsoluteLayout // FIXME: Necessary because ScrollView/ListView do not scroll left/right int maxH = Math.min(viewFocus.right - visRect.right, maxXScroll); if (maxH > 0) { - pinScrollBy(maxH, 0, true); + pinScrollBy(maxH, 0, true, 0); } else { maxH = Math.max(viewFocus.left - visRect.left, -maxXScroll); if (maxH < 0) { - pinScrollBy(maxH, 0, true); + pinScrollBy(maxH, 0, true, 0); } } if (mLastFocusBounds.isEmpty()) return keyHandled; diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 323b44d923460..8f788872bfe1b 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -799,12 +799,11 @@ final class WebViewCore { case TOUCH_EVENT: { TouchEventData ted = (TouchEventData) msg.obj; - if (nativeHandleTouchEvent(ted.mAction, ted.mX, - ted.mY)) { - Message.obtain(mWebView.mPrivateHandler, - WebView.PREVENT_TOUCH_ID) - .sendToTarget(); - } + Message.obtain( + mWebView.mPrivateHandler, + WebView.PREVENT_TOUCH_ID, ted.mAction, + nativeHandleTouchEvent(ted.mAction, ted.mX, + ted.mY) ? 1 : 0).sendToTarget(); break; } @@ -1434,10 +1433,15 @@ final class WebViewCore { } } - // called by JNI + /* Called by JNI. The coordinates are in doc coordinates, so they need to + be scaled before they can be used by the view system, which happens + in WebView since it (and its thread) know the current scale factor. + */ private void sendViewInvalidate(int left, int top, int right, int bottom) { if (mWebView != null) { - mWebView.postInvalidate(left, top, right, bottom); + Message.obtain(mWebView.mPrivateHandler, + WebView.INVAL_RECT_MSG_ID, + new Rect(left, top, right, bottom)).sendToTarget(); } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 19ec77d89a607..378d2183a7db8 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -369,7 +369,6 @@ public abstract class AbsListView extends AdapterView implements Te private int mLastTouchMode = TOUCH_MODE_UNKNOWN; - // TODO: REMOVE WHEN WE'RE DONE WITH PROFILING private static final boolean PROFILE_SCROLLING = false; private boolean mScrollProfilingStarted = false; @@ -423,6 +422,8 @@ public abstract class AbsListView extends AdapterView implements Te */ private FastScroller mFastScroller; + private int mTouchSlop; + /** * Interface definition for a callback to be invoked when the list or grid * has been scrolled. @@ -558,6 +559,15 @@ public abstract class AbsListView extends AdapterView implements Te public boolean isFastScrollEnabled() { return mFastScrollEnabled; } + + /** + * If fast scroll is visible, then don't draw the vertical scrollbar. + * @hide + */ + @Override + protected boolean isVerticalScrollBarHidden() { + return mFastScroller != null ? mFastScroller.isVisible() : false; + } /** * When smooth scrollbar is enabled, the position and size of the scrollbar thumb @@ -696,6 +706,8 @@ public abstract class AbsListView extends AdapterView implements Te setWillNotDraw(false); setAlwaysDrawnWithCacheEnabled(false); setScrollingCacheEnabled(true); + + mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); } private void useDefaultSelector() { @@ -1758,8 +1770,7 @@ public abstract class AbsListView extends AdapterView implements Te // Check if we have moved far enough that it looks more like a // scroll than a tap final int distance = Math.abs(deltaY); - int touchSlop = ViewConfiguration.getTouchSlop(); - if (distance > touchSlop) { + if (distance > mTouchSlop) { createScrollingCache(); mTouchMode = TOUCH_MODE_SCROLL; mMotionCorrection = deltaY; @@ -1979,8 +1990,9 @@ public abstract class AbsListView extends AdapterView implements Te velocityTracker.computeCurrentVelocity(1000); int initialVelocity = (int)velocityTracker.getYVelocity(); - if ((Math.abs(initialVelocity) > ViewConfiguration.getMinimumFlingVelocity()) && - (getChildCount() > 0)){ + if ((Math.abs(initialVelocity) > + ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && + (getChildCount() > 0)) { if (mFlingRunnable == null) { mFlingRunnable = new FlingRunnable(); } @@ -2714,7 +2726,9 @@ public abstract class AbsListView extends AdapterView implements Te int screenHeight = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight(); final int[] xy = new int[2]; getLocationOnScreen(xy); - int bottomGap = screenHeight - xy[1] - getHeight() + 20; + // TODO: The 20 below should come from the theme and be expressed in dip + final float scale = getContext().getResources().getDisplayMetrics().density; + int bottomGap = screenHeight - xy[1] - getHeight() + (int) (scale * 20); mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, xy[0], bottomGap); // Make sure we get focus if we are showing the popup diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index fbb010515801a..cf9c588a21440 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.text.format.Time; import android.util.AttributeSet; import android.view.View; +import android.widget.RemoteViews.RemoteView; import java.util.TimeZone; @@ -35,6 +36,7 @@ import java.util.TimeZone; * This widget display an analogic clock with two hands for hours and * minutes. */ +@RemoteView public class AnalogClock extends View { private Time mCalendar; diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 7d52901755787..7a51676bf7681 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -110,8 +110,14 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private Validator mValidator = null; + private boolean mBlockCompletion; + private AutoCompleteTextView.ListSelectorHider mHideSelector; + // Indicates whether this AutoCompleteTextView is attached to a window or not + // The widget is attached to a window when mAttachCount > 0 + private int mAttachCount; + public AutoCompleteTextView(Context context) { this(context, null); } @@ -145,19 +151,13 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView, R.layout.simple_dropdown_hint); - // A little trickiness for backwards compatibility: if the app - // didn't specify an explicit content type, then we will fill in the - // auto complete flag for them. - int contentType = a.getInt( - R.styleable.AutoCompleteTextView_inputType, - EditorInfo.TYPE_NULL); - if (contentType == EditorInfo.TYPE_NULL) { - contentType = getInputType(); - if ((contentType&EditorInfo.TYPE_MASK_CLASS) - == EditorInfo.TYPE_CLASS_TEXT) { - contentType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; - setRawInputType(contentType); - } + // Always turn on the auto complete input type flag, since it + // makes no sense to use this widget without it. + int inputType = getInputType(); + if ((inputType&EditorInfo.TYPE_MASK_CLASS) + == EditorInfo.TYPE_CLASS_TEXT) { + inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; + setRawInputType(inputType); } a.recycle(); @@ -300,6 +300,15 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** *

    Changes the list of data used for auto completion. The provided list * must be a filterable list adapter.

    + * + *

    The caller is still responsible for managing any resources used by the adapter. + * Notably, when the AutoCompleteTextView is closed or released, the adapter is not notified. + * A common case is the use of {@link android.widget.CursorAdapter}, which + * contains a {@link android.database.Cursor} that must be closed. This can be done + * automatically (see + * {@link android.app.Activity#startManagingCursor(android.database.Cursor) + * startManagingCursor()}), + * or by manually closing the cursor when the AutoCompleteTextView is dismissed.

    * * @param adapter the adapter holding the auto completion data * @@ -368,7 +377,10 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) { int curIndex = mDropDownList.getSelectedItemPosition(); boolean consumed; - if (keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= 0) { + final boolean below = !mPopup.isAboveAnchor(); + if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= 0) || + (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= + mDropDownList.getAdapter().getCount() - 1)) { // When the selection is at the top, we block the key // event to prevent focus from moving. mDropDownList.hideSelector(); @@ -409,13 +421,15 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return true; } } else { - if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { // when the selection is at the bottom, we block the // event to avoid going to the next focusable widget Adapter adapter = mDropDownList.getAdapter(); if (adapter != null && curIndex == adapter.getCount() - 1) { return true; } + } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex == 0) { + return true; } } } @@ -462,6 +476,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } void doBeforeTextChanged() { + if (mBlockCompletion) return; + // when text is changed, inserted or deleted, we attempt to show // the drop down mOpenBefore = isPopupShowing(); @@ -469,6 +485,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } void doAfterTextChanged() { + if (mBlockCompletion) return; + // if the list was open before the keystroke, but closed afterwards, // then something in the keystroke processing (an input filter perhaps) // called performCompletion() and we shouldn't do any more processing. @@ -579,9 +597,13 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe performCompletion(null, -1, -1); } - @Override public void onCommitCompletion(CompletionInfo completion) { + @Override + public void onCommitCompletion(CompletionInfo completion) { if (isPopupShowing()) { + mBlockCompletion = true; replaceText(completion.getText()); + mBlockCompletion = false; + if (mItemClickListener != null) { final DropDownListView list = mDropDownList; // Note that we don't have a View here, so we will need to @@ -604,7 +626,10 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe Log.w(TAG, "performCompletion: no selected item"); return; } + + mBlockCompletion = true; replaceText(convertSelectionToString(selectedItem)); + mBlockCompletion = false; if (mItemClickListener != null) { final DropDownListView list = mDropDownList; @@ -620,6 +645,14 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe dismissDropDown(); } + + /** + * Identifies whether the view is currently performing a text completion, so subclasses + * can decide whether to respond to text changed events. + */ + public boolean isPerformingCompletion() { + return mBlockCompletion; + } /** *

    Performs the text completion by replacing the current text by the @@ -636,6 +669,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } public void onFilterComplete(int count) { + if (mAttachCount <= 0) return; + /* * This checks enoughToFilter() again because filtering requests * are asynchronous, so the result may come back after enough text @@ -670,9 +705,16 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mAttachCount++; + } + @Override protected void onDetachedFromWindow() { dismissDropDown(); + mAttachCount--; super.onDetachedFromWindow(); } @@ -694,7 +736,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe boolean result = super.setFrame(l, t, r, b); if (mPopup.isShowing()) { - mPopup.update(this, getMeasuredWidth() - mPaddingLeft - mPaddingRight, -1); + mPopup.update(this, r - l, -1); } return result; @@ -707,10 +749,10 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe int height = buildDropDown(); if (mPopup.isShowing()) { mPopup.update(this, mDropDownHorizontalOffset, mDropDownVerticalOffset, - getMeasuredWidth() - mPaddingLeft - mPaddingRight, height); + getWidth(), height); } else { mPopup.setWindowLayoutMode(0, ViewGroup.LayoutParams.WRAP_CONTENT); - mPopup.setWidth(getMeasuredWidth() - mPaddingLeft - mPaddingRight); + mPopup.setWidth(getWidth()); mPopup.setHeight(height); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); mPopup.setOutsideTouchable(true); @@ -739,7 +781,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe int N = mAdapter.getCount(); if (N > 20) N = 20; CompletionInfo[] completions = new CompletionInfo[N]; - for (int i=0; i 0 && totalItemCount / visibleItemCount < MIN_PAGES) { + if (mState != STATE_NONE) { + setState(STATE_NONE); + } + return; + } if (totalItemCount - visibleItemCount > 0 && mState != STATE_DRAGGING ) { mThumbY = ((mList.getHeight() - mThumbH) * firstVisibleItem) / (totalItemCount - visibleItemCount); diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index 7b9735c865edc..ffabb02270601 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -180,7 +180,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList public Gallery(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mGestureDetector = new GestureDetector(this); + mGestureDetector = new GestureDetector(context, this); mGestureDetector.setIsLongpressEnabled(true); TypedArray a = context.obtainStyledAttributes( diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java new file mode 100644 index 0000000000000..96fe595c29d68 --- /dev/null +++ b/core/java/android/widget/HorizontalScrollView.java @@ -0,0 +1,1197 @@ +/* + * Copyright (C) 2009 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.util.AttributeSet; +import android.graphics.Rect; +import android.view.View; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.KeyEvent; +import android.view.FocusFinder; +import android.view.MotionEvent; +import android.view.ViewParent; +import android.view.animation.AnimationUtils; +import android.content.Context; +import android.content.res.TypedArray; + +import java.util.List; + +/** + * Layout container for a view hierarchy that can be scrolled by the user, + * allowing it to be larger than the physical display. A HorizontalScrollView + * is a {@link FrameLayout}, meaning you should place one child in it + * containing the entire contents to scroll; this child may itself be a layout + * manager with a complex hierarchy of objects. A child that is often used + * is a {@link LinearLayout} in a horizontal orientation, presenting a horizontal + * array of top-level items that the user can scroll through. + * + *

    You should never use a HorizontalScrollView with a {@link ListView}, since + * ListView takes care of its own scrolling. Most importantly, doing this + * defeats all of the important optimizations in ListView for dealing with + * large lists, since it effectively forces the ListView to display its entire + * list of items to fill up the infinite container supplied by HorizontalScrollView. + * + *

    The {@link TextView} class also + * takes care of its own scrolling, so does not require a ScrollView, but + * using the two together is possible to achieve the effect of a text view + * within a larger container. + * + *

    HorizontalScrollView only supports horizontal scrolling. + */ +public class HorizontalScrollView extends FrameLayout { + private static final int ANIMATED_SCROLL_GAP = ScrollView.ANIMATED_SCROLL_GAP; + + private static final float MAX_SCROLL_FACTOR = ScrollView.MAX_SCROLL_FACTOR; + + + private long mLastScroll; + + private final Rect mTempRect = new Rect(); + private Scroller mScroller; + + /** + * Flag to indicate that we are moving focus ourselves. This is so the + * code that watches for focus changes initiated outside this ScrollView + * knows that it does not have to do anything. + */ + private boolean mScrollViewMovedFocus; + + /** + * Position of the last motion event. + */ + private float mLastMotionX; + + /** + * True when the layout has changed but the traversal has not come through yet. + * Ideally the view hierarchy would keep track of this for us. + */ + private boolean mIsLayoutDirty = true; + + /** + * The child to give focus to in the event that a child has requested focus while the + * layout is dirty. This prevents the scroll from being wrong if the child has not been + * laid out before requesting focus. + */ + private View mChildToScrollTo = null; + + /** + * True if the user is currently dragging this ScrollView around. This is + * not the same as 'is being flinged', which can be checked by + * mScroller.isFinished() (flinging begins when the user lifts his finger). + */ + private boolean mIsBeingDragged = false; + + /** + * Determines speed during touch scrolling + */ + private VelocityTracker mVelocityTracker; + + /** + * When set to true, the scroll view measure its child to make it fill the currently + * visible area. + */ + private boolean mFillViewport; + + /** + * Whether arrow scrolling is animated. + */ + private boolean mSmoothScrollingEnabled = true; + + private int mTouchSlop; + + public HorizontalScrollView(Context context) { + this(context, null); + } + + public HorizontalScrollView(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.horizontalScrollViewStyle); + } + + public HorizontalScrollView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initScrollView(); + + TypedArray a = context.obtainStyledAttributes(attrs, + android.R.styleable.HorizontalScrollView, defStyle, 0); + + setFillViewport(a.getBoolean(android.R.styleable.HorizontalScrollView_fillViewport, false)); + + a.recycle(); + } + + @Override + protected float getLeftFadingEdgeStrength() { + if (getChildCount() == 0) { + return 0.0f; + } + + final int length = getHorizontalFadingEdgeLength(); + if (mScrollX < length) { + return mScrollX / (float) length; + } + + return 1.0f; + } + + @Override + protected float getRightFadingEdgeStrength() { + if (getChildCount() == 0) { + return 0.0f; + } + + final int length = getHorizontalFadingEdgeLength(); + final int rightEdge = getWidth() - mPaddingRight; + final int span = getChildAt(0).getRight() - mScrollX - rightEdge; + if (span < length) { + return span / (float) length; + } + + return 1.0f; + } + + /** + * @return The maximum amount this scroll view will scroll in response to + * an arrow event. + */ + public int getMaxScrollAmount() { + return (int) (MAX_SCROLL_FACTOR * (mRight - mLeft)); + } + + + private void initScrollView() { + mScroller = new Scroller(getContext()); + setFocusable(true); + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); + setWillNotDraw(false); + mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + } + + @Override + public void addView(View child) { + if (getChildCount() > 0) { + throw new IllegalStateException("HorizontalScrollView can host only one direct child"); + } + + super.addView(child); + } + + @Override + public void addView(View child, int index) { + if (getChildCount() > 0) { + throw new IllegalStateException("HorizontalScrollView can host only one direct child"); + } + + super.addView(child, index); + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (getChildCount() > 0) { + throw new IllegalStateException("HorizontalScrollView can host only one direct child"); + } + + super.addView(child, params); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (getChildCount() > 0) { + throw new IllegalStateException("HorizontalScrollView can host only one direct child"); + } + + super.addView(child, index, params); + } + + /** + * @return Returns true this HorizontalScrollView can be scrolled + */ + private boolean canScroll() { + View child = getChildAt(0); + if (child != null) { + int childWidth = child.getWidth(); + return getWidth() < childWidth + mPaddingLeft + mPaddingRight ; + } + return false; + } + + /** + * Indicates whether this ScrollView's content is stretched to fill the viewport. + * + * @return True if the content fills the viewport, false otherwise. + */ + public boolean isFillViewport() { + return mFillViewport; + } + + /** + * Indicates this ScrollView whether it should stretch its content width to fill + * the viewport or not. + * + * @param fillViewport True to stretch the content's width to the viewport's + * boundaries, false otherwise. + */ + public void setFillViewport(boolean fillViewport) { + if (fillViewport != mFillViewport) { + mFillViewport = fillViewport; + requestLayout(); + } + } + + /** + * @return Whether arrow scrolling will animate its transition. + */ + public boolean isSmoothScrollingEnabled() { + return mSmoothScrollingEnabled; + } + + /** + * Set whether arrow scrolling will animate its transition. + * @param smoothScrollingEnabled whether arrow scrolling will animate its transition + */ + public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) { + mSmoothScrollingEnabled = smoothScrollingEnabled; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (!mFillViewport) { + return; + } + + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + if (widthMode == MeasureSpec.UNSPECIFIED) { + return; + } + + final View child = getChildAt(0); + int width = getMeasuredWidth(); + if (child.getMeasuredHeight() < width) { + final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, mPaddingTop + + mPaddingBottom, lp.height); + width -= mPaddingLeft; + width -= mPaddingRight; + int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + // Let the focused view and/or our descendants get the key first + boolean handled = super.dispatchKeyEvent(event); + if (handled) { + return true; + } + return executeKeyEvent(event); + } + + /** + * You can call this function yourself to have the scroll view perform + * scrolling from a key event, just as if the event had been dispatched to + * it by the view hierarchy. + * + * @param event The key event to execute. + * @return Return true if the event was handled, else false. + */ + public boolean executeKeyEvent(KeyEvent event) { + mTempRect.setEmpty(); + + if (!canScroll()) { + if (isFocused()) { + View currentFocused = findFocus(); + if (currentFocused == this) currentFocused = null; + View nextFocused = FocusFinder.getInstance().findNextFocus(this, + currentFocused, View.FOCUS_RIGHT); + return nextFocused != null && nextFocused != this && + nextFocused.requestFocus(View.FOCUS_RIGHT); + } + return false; + } + + boolean handled = false; + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_DPAD_LEFT: + if (!event.isAltPressed()) { + handled = arrowScroll(View.FOCUS_LEFT); + } else { + handled = fullScroll(View.FOCUS_LEFT); + } + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + if (!event.isAltPressed()) { + handled = arrowScroll(View.FOCUS_RIGHT); + } else { + handled = fullScroll(View.FOCUS_RIGHT); + } + break; + case KeyEvent.KEYCODE_SPACE: + pageScroll(event.isShiftPressed() ? View.FOCUS_LEFT : View.FOCUS_RIGHT); + break; + } + } + + return handled; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + /* + * This method JUST determines whether we want to intercept the motion. + * If we return true, onMotionEvent will be called and we do the actual + * scrolling there. + */ + + /* + * Shortcut the most recurring case: the user is in the dragging + * state and he is moving his finger. We want to intercept this + * motion. + */ + final int action = ev.getAction(); + if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { + return true; + } + + if (!canScroll()) { + mIsBeingDragged = false; + return false; + } + + final float x = ev.getX(); + + switch (action) { + case MotionEvent.ACTION_MOVE: + /* + * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check + * whether the user has moved far enough from his original down touch. + */ + + /* + * Locally do absolute value. mLastMotionX is set to the x value + * of the down event. + */ + final int xDiff = (int) Math.abs(x - mLastMotionX); + if (xDiff > mTouchSlop) { + mIsBeingDragged = true; + if (mParent != null) mParent.requestDisallowInterceptTouchEvent(true); + } + break; + + case MotionEvent.ACTION_DOWN: + /* Remember location of down touch */ + mLastMotionX = x; + + /* + * If being flinged and user touches the screen, initiate drag; + * otherwise don't. mScroller.isFinished should be false when + * being flinged. + */ + mIsBeingDragged = !mScroller.isFinished(); + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + /* Release the drag */ + mIsBeingDragged = false; + break; + } + + /* + * The only time we want to intercept motion events is if we are in the + * drag mode. + */ + return mIsBeingDragged; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + + if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { + // Don't handle edge touches immediately -- they may actually belong to one of our + // descendants. + return false; + } + + if (!canScroll()) { + return false; + } + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(ev); + + final int action = ev.getAction(); + final float x = ev.getX(); + + switch (action) { + case MotionEvent.ACTION_DOWN: + /* + * If being flinged and user touches, stop the fling. isFinished + * will be false if being flinged. + */ + if (!mScroller.isFinished()) { + mScroller.abortAnimation(); + } + + // Remember where the motion event started + mLastMotionX = x; + break; + case MotionEvent.ACTION_MOVE: + // Scroll to follow the motion event + final int deltaX = (int) (mLastMotionX - x); + mLastMotionX = x; + + if (deltaX < 0) { + if (mScrollX > 0) { + scrollBy(deltaX, 0); + } + } else if (deltaX > 0) { + final int rightEdge = getWidth() - mPaddingRight; + final int availableToScroll = getChildAt(0).getRight() - mScrollX - rightEdge; + if (availableToScroll > 0) { + scrollBy(Math.min(availableToScroll, deltaX), 0); + } + } + break; + case MotionEvent.ACTION_UP: + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000); + int initialVelocity = (int) velocityTracker.getXVelocity(); + + if ((Math.abs(initialVelocity) > + ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && + getChildCount() > 0) { + fling(-initialVelocity); + } + + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + return true; + } + + /** + *

    + * Finds the next focusable component that fits in this View's bounds + * (excluding fading edges) pretending that this View's left is located at + * the parameter left. + *

    + * + * @param leftFocus look for a candidate is the one at the left of the bounds + * if leftFocus is true, or at the right of the bounds if leftFocus + * is false + * @param left the left offset of the bounds in which a focusable must be + * found (the fading edge is assumed to start at this position) + * @param preferredFocusable the View that has highest priority and will be + * returned if it is within my bounds (null is valid) + * @return the next focusable component in the bounds or null if none can be found + */ + private View findFocusableViewInMyBounds(final boolean leftFocus, + final int left, View preferredFocusable) { + /* + * The fading edge's transparent side should be considered for focus + * since it's mostly visible, so we divide the actual fading edge length + * by 2. + */ + final int fadingEdgeLength = getHorizontalFadingEdgeLength() / 2; + final int leftWithoutFadingEdge = left + fadingEdgeLength; + final int rightWithoutFadingEdge = left + getWidth() - fadingEdgeLength; + + if ((preferredFocusable != null) + && (preferredFocusable.getLeft() < rightWithoutFadingEdge) + && (preferredFocusable.getRight() > leftWithoutFadingEdge)) { + return preferredFocusable; + } + + return findFocusableViewInBounds(leftFocus, leftWithoutFadingEdge, + rightWithoutFadingEdge); + } + + /** + *

    + * Finds the next focusable component that fits in the specified bounds. + *

    + * + * @param leftFocus look for a candidate is the one at the left of the bounds + * if leftFocus is true, or at the right of the bounds if + * leftFocus is false + * @param left the left offset of the bounds in which a focusable must be + * found + * @param right the right offset of the bounds in which a focusable must + * be found + * @return the next focusable component in the bounds or null if none can + * be found + */ + private View findFocusableViewInBounds(boolean leftFocus, int left, int right) { + + List focusables = getFocusables(View.FOCUS_FORWARD); + View focusCandidate = null; + + /* + * A fully contained focusable is one where its left is below the bound's + * left, and its right is above the bound's right. A partially + * contained focusable is one where some part of it is within the + * bounds, but it also has some part that is not within bounds. A fully contained + * focusable is preferred to a partially contained focusable. + */ + boolean foundFullyContainedFocusable = false; + + int count = focusables.size(); + for (int i = 0; i < count; i++) { + View view = focusables.get(i); + int viewLeft = view.getLeft(); + int viewRight = view.getRight(); + + if (left < viewRight && viewLeft < right) { + /* + * the focusable is in the target area, it is a candidate for + * focusing + */ + + final boolean viewIsFullyContained = (left < viewLeft) && + (viewRight < right); + + if (focusCandidate == null) { + /* No candidate, take this one */ + focusCandidate = view; + foundFullyContainedFocusable = viewIsFullyContained; + } else { + final boolean viewIsCloserToBoundary = + (leftFocus && viewLeft < focusCandidate.getLeft()) || + (!leftFocus && viewRight > focusCandidate.getRight()); + + if (foundFullyContainedFocusable) { + if (viewIsFullyContained && viewIsCloserToBoundary) { + /* + * We're dealing with only fully contained views, so + * it has to be closer to the boundary to beat our + * candidate + */ + focusCandidate = view; + } + } else { + if (viewIsFullyContained) { + /* Any fully contained view beats a partially contained view */ + focusCandidate = view; + foundFullyContainedFocusable = true; + } else if (viewIsCloserToBoundary) { + /* + * Partially contained view beats another partially + * contained view if it's closer + */ + focusCandidate = view; + } + } + } + } + } + + return focusCandidate; + } + + /** + *

    Handles scrolling in response to a "page up/down" shortcut press. This + * method will scroll the view by one page left or right and give the focus + * to the leftmost/rightmost component in the new visible area. If no + * component is a good candidate for focus, this scrollview reclaims the + * focus.

    + * + * @param direction the scroll direction: {@link android.view.View#FOCUS_LEFT} + * to go one page left or {@link android.view.View#FOCUS_RIGHT} + * to go one page right + * @return true if the key event is consumed by this method, false otherwise + */ + public boolean pageScroll(int direction) { + boolean right = direction == View.FOCUS_RIGHT; + int width = getWidth(); + + if (right) { + mTempRect.left = getScrollX() + width; + int count = getChildCount(); + if (count > 0) { + View view = getChildAt(count - 1); + if (mTempRect.left + width > view.getRight()) { + mTempRect.left = view.getRight() - width; + } + } + } else { + mTempRect.left = getScrollX() - width; + if (mTempRect.left < 0) { + mTempRect.left = 0; + } + } + mTempRect.right = mTempRect.left + width; + + return scrollAndFocus(direction, mTempRect.left, mTempRect.right); + } + + /** + *

    Handles scrolling in response to a "home/end" shortcut press. This + * method will scroll the view to the left or right and give the focus + * to the leftmost/rightmost component in the new visible area. If no + * component is a good candidate for focus, this scrollview reclaims the + * focus.

    + * + * @param direction the scroll direction: {@link android.view.View#FOCUS_LEFT} + * to go the left of the view or {@link android.view.View#FOCUS_RIGHT} + * to go the right + * @return true if the key event is consumed by this method, false otherwise + */ + public boolean fullScroll(int direction) { + boolean right = direction == View.FOCUS_RIGHT; + int width = getWidth(); + + mTempRect.left = 0; + mTempRect.right = width; + + if (right) { + int count = getChildCount(); + if (count > 0) { + View view = getChildAt(count - 1); + mTempRect.right = view.getRight(); + mTempRect.left = mTempRect.right - width; + } + } + + return scrollAndFocus(direction, mTempRect.left, mTempRect.right); + } + + /** + *

    Scrolls the view to make the area defined by left and + * right visible. This method attempts to give the focus + * to a component visible in this area. If no component can be focused in + * the new visible area, the focus is reclaimed by this scrollview.

    + * + * @param direction the scroll direction: {@link android.view.View#FOCUS_LEFT} + * to go left {@link android.view.View#FOCUS_RIGHT} to right + * @param left the left offset of the new area to be made visible + * @param right the right offset of the new area to be made visible + * @return true if the key event is consumed by this method, false otherwise + */ + private boolean scrollAndFocus(int direction, int left, int right) { + boolean handled = true; + + int width = getWidth(); + int containerLeft = getScrollX(); + int containerRight = containerLeft + width; + boolean goLeft = direction == View.FOCUS_LEFT; + + View newFocused = findFocusableViewInBounds(goLeft, left, right); + if (newFocused == null) { + newFocused = this; + } + + if (left >= containerLeft && right <= containerRight) { + handled = false; + } else { + int delta = goLeft ? (left - containerLeft) : (right - containerRight); + doScrollX(delta); + } + + if (newFocused != findFocus() && newFocused.requestFocus(direction)) { + mScrollViewMovedFocus = true; + mScrollViewMovedFocus = false; + } + + return handled; + } + + /** + * Handle scrolling in response to a left or right arrow click. + * + * @param direction The direction corresponding to the arrow key that was + * pressed + * @return True if we consumed the event, false otherwise + */ + public boolean arrowScroll(int direction) { + + View currentFocused = findFocus(); + if (currentFocused == this) currentFocused = null; + + View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); + + final int maxJump = getMaxScrollAmount(); + + if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump)) { + nextFocused.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(nextFocused, mTempRect); + int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); + doScrollX(scrollDelta); + nextFocused.requestFocus(direction); + } else { + // no new focus + int scrollDelta = maxJump; + + if (direction == View.FOCUS_LEFT && getScrollX() < scrollDelta) { + scrollDelta = getScrollX(); + } else if (direction == View.FOCUS_RIGHT) { + + int daRight = getChildAt(getChildCount() - 1).getRight(); + + int screenRight = getScrollX() + getWidth(); + + if (daRight - screenRight < maxJump) { + scrollDelta = daRight - screenRight; + } + } + if (scrollDelta == 0) { + return false; + } + doScrollX(direction == View.FOCUS_RIGHT ? scrollDelta : -scrollDelta); + } + + if (currentFocused != null && currentFocused.isFocused() + && isOffScreen(currentFocused)) { + // previously focused item still has focus and is off screen, give + // it up (take it back to ourselves) + // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are + // sure to + // get it) + final int descendantFocusability = getDescendantFocusability(); // save + setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); + requestFocus(); + setDescendantFocusability(descendantFocusability); // restore + } + return true; + } + + /** + * @return whether the descendant of this scroll view is scrolled off + * screen. + */ + private boolean isOffScreen(View descendant) { + return !isWithinDeltaOfScreen(descendant, 0); + } + + /** + * @return whether the descendant of this scroll view is within delta + * pixels of being on the screen. + */ + private boolean isWithinDeltaOfScreen(View descendant, int delta) { + descendant.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(descendant, mTempRect); + + return (mTempRect.right + delta) >= getScrollX() + && (mTempRect.left - delta) <= (getScrollX() + getWidth()); + } + + /** + * Smooth scroll by a X delta + * + * @param delta the number of pixels to scroll by on the X axis + */ + private void doScrollX(int delta) { + if (delta != 0) { + if (mSmoothScrollingEnabled) { + smoothScrollBy(delta, 0); + } else { + scrollBy(delta, 0); + } + } + } + + /** + * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. + * + * @param dx the number of pixels to scroll by on the X axis + * @param dy the number of pixels to scroll by on the Y axis + */ + public final void smoothScrollBy(int dx, int dy) { + long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll; + if (duration > ANIMATED_SCROLL_GAP) { + mScroller.startScroll(mScrollX, mScrollY, dx, dy); + invalidate(); + } else { + if (!mScroller.isFinished()) { + mScroller.abortAnimation(); + } + scrollBy(dx, dy); + } + mLastScroll = AnimationUtils.currentAnimationTimeMillis(); + } + + /** + * Like {@link #scrollTo}, but scroll smoothly instead of immediately. + * + * @param x the position where to scroll on the X axis + * @param y the position where to scroll on the Y axis + */ + public final void smoothScrollTo(int x, int y) { + smoothScrollBy(x - mScrollX, y - mScrollY); + } + + /** + *

    The scroll range of a scroll view is the overall width of all of its + * children.

    + */ + @Override + protected int computeHorizontalScrollRange() { + int count = getChildCount(); + return count == 0 ? getWidth() : getChildAt(0).getRight(); + } + + + @Override + protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { + ViewGroup.LayoutParams lp = child.getLayoutParams(); + + int childWidthMeasureSpec; + int childHeightMeasureSpec; + + childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + + mPaddingBottom, lp.height); + + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + @Override + protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, + int parentHeightMeasureSpec, int heightUsed) { + final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + + final int childHeightMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, + mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + + heightUsed, lp.height); + final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + lp.leftMargin + lp.rightMargin, MeasureSpec.UNSPECIFIED); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + @Override + public void computeScroll() { + if (mScroller.computeScrollOffset()) { + // This is called at drawing time by ViewGroup. We don't want to + // re-show the scrollbars at this point, which scrollTo will do, + // so we replicate most of scrollTo here. + // + // It's a little odd to call onScrollChanged from inside the drawing. + // + // It is, except when you remember that computeScroll() is used to + // animate scrolling. So unless we want to defer the onScrollChanged() + // until the end of the animated scrolling, we don't really have a + // choice here. + // + // I agree. The alternative, which I think would be worse, is to post + // something and tell the subclasses later. This is bad because there + // will be a window where mScrollX/Y is different from what the app + // thinks it is. + // + int oldX = mScrollX; + int oldY = mScrollY; + int x = mScroller.getCurrX(); + int y = mScroller.getCurrY(); + if (getChildCount() > 0) { + View child = getChildAt(0); + mScrollX = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth()); + mScrollY = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight()); + } else { + mScrollX = x; + mScrollY = y; + } + if (oldX != mScrollX || oldY != mScrollY) { + onScrollChanged(mScrollX, mScrollY, oldX, oldY); + } + + // Keep on drawing until the animation has finished. + postInvalidate(); + } + } + + /** + * Scrolls the view to the given child. + * + * @param child the View to scroll to + */ + private void scrollToChild(View child) { + child.getDrawingRect(mTempRect); + + /* Offset from child's local coordinates to ScrollView coordinates */ + offsetDescendantRectToMyCoords(child, mTempRect); + + int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); + + if (scrollDelta != 0) { + scrollBy(scrollDelta, 0); + } + } + + /** + * If rect is off screen, scroll just enough to get it (or at least the + * first screen size chunk of it) on screen. + * + * @param rect The rectangle. + * @param immediate True to scroll immediately without animation + * @return true if scrolling was performed + */ + private boolean scrollToChildRect(Rect rect, boolean immediate) { + final int delta = computeScrollDeltaToGetChildRectOnScreen(rect); + final boolean scroll = delta != 0; + if (scroll) { + if (immediate) { + scrollBy(delta, 0); + } else { + smoothScrollBy(delta, 0); + } + } + return scroll; + } + + /** + * Compute the amount to scroll in the X direction in order to get + * a rectangle completely on the screen (or, if taller than the screen, + * at least the first screen size chunk of it). + * + * @param rect The rect. + * @return The scroll delta. + */ + protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) { + + int width = getWidth(); + int screenLeft = getScrollX(); + int screenRight = screenLeft + width; + + int fadingEdge = getHorizontalFadingEdgeLength(); + + // leave room for left fading edge as long as rect isn't at very left + if (rect.left > 0) { + screenLeft += fadingEdge; + } + + // leave room for right fading edge as long as rect isn't at very right + if (rect.right < getChildAt(0).getWidth()) { + screenRight -= fadingEdge; + } + + int scrollXDelta = 0; + + if (rect.right > screenRight && rect.left > screenLeft) { + // need to move right to get it in view: move right just enough so + // that the entire rectangle is in view (or at least the first + // screen size chunk). + + if (rect.width() > width) { + // just enough to get screen size chunk on + scrollXDelta += (rect.left - screenLeft); + } else { + // get entire rect at right of screen + scrollXDelta += (rect.right - screenRight); + } + + // make sure we aren't scrolling beyond the end of our content + int right = getChildAt(getChildCount() - 1).getRight(); + int distanceToRight = right - screenRight; + scrollXDelta = Math.min(scrollXDelta, distanceToRight); + + } else if (rect.left < screenLeft && rect.right < screenRight) { + // need to move right to get it in view: move right just enough so that + // entire rectangle is in view (or at least the first screen + // size chunk of it). + + if (rect.width() > width) { + // screen size chunk + scrollXDelta -= (screenRight - rect.right); + } else { + // entire rect at left + scrollXDelta -= (screenLeft - rect.left); + } + + // make sure we aren't scrolling any further than the left our content + scrollXDelta = Math.max(scrollXDelta, -getScrollX()); + } + return scrollXDelta; + } + + @Override + public void requestChildFocus(View child, View focused) { + if (!mScrollViewMovedFocus) { + if (!mIsLayoutDirty) { + scrollToChild(focused); + } else { + // The child may not be laid out yet, we can't compute the scroll yet + mChildToScrollTo = focused; + } + } + super.requestChildFocus(child, focused); + } + + + /** + * When looking for focus in children of a scroll view, need to be a little + * more careful not to give focus to something that is scrolled off screen. + * + * This is more expensive than the default {@link android.view.ViewGroup} + * implementation, otherwise this behavior might have been made the default. + */ + @Override + protected boolean onRequestFocusInDescendants(int direction, + Rect previouslyFocusedRect) { + + // convert from forward / backward notation to up / down / left / right + // (ugh). + if (direction == View.FOCUS_FORWARD) { + direction = View.FOCUS_RIGHT; + } else if (direction == View.FOCUS_BACKWARD) { + direction = View.FOCUS_LEFT; + } + + final View nextFocus = previouslyFocusedRect == null ? + FocusFinder.getInstance().findNextFocus(this, null, direction) : + FocusFinder.getInstance().findNextFocusFromRect(this, + previouslyFocusedRect, direction); + + if (nextFocus == null) { + return false; + } + + if (isOffScreen(nextFocus)) { + return false; + } + + return nextFocus.requestFocus(direction, previouslyFocusedRect); + } + + @Override + public boolean requestChildRectangleOnScreen(View child, Rect rectangle, + boolean immediate) { + // offset into coordinate space of this scroll view + rectangle.offset(child.getLeft() - child.getScrollX(), + child.getTop() - child.getScrollY()); + + return scrollToChildRect(rectangle, immediate); + } + + @Override + public void requestLayout() { + mIsLayoutDirty = true; + super.requestLayout(); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + mIsLayoutDirty = false; + // Give a child focus if it needs it + if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) { + scrollToChild(mChildToScrollTo); + } + mChildToScrollTo = null; + + // Calling this with the present values causes it to re-clam them + scrollTo(mScrollX, mScrollY); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + View currentFocused = findFocus(); + if (null == currentFocused || this == currentFocused) + return; + + final int maxJump = mRight - mLeft; + + if (isWithinDeltaOfScreen(currentFocused, maxJump)) { + currentFocused.getDrawingRect(mTempRect); + offsetDescendantRectToMyCoords(currentFocused, mTempRect); + int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); + doScrollX(scrollDelta); + } + } + + /** + * Return true if child is an descendant of parent, (or equal to the parent). + */ + private boolean isViewDescendantOf(View child, View parent) { + if (child == parent) { + return true; + } + + final ViewParent theParent = child.getParent(); + return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); + } + + /** + * Fling the scroll view + * + * @param velocityX The initial velocity in the X direction. Positive + * numbers mean that the finger/curor is moving down the screen, + * which means we want to scroll towards the left. + */ + public void fling(int velocityX) { + int width = getWidth() - mPaddingRight - mPaddingLeft; + int right = getChildAt(0).getWidth(); + + mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0, right - width, 0, 0); + + final boolean movingRight = velocityX > 0; + + View newFocused = findFocusableViewInMyBounds(movingRight, + mScroller.getFinalX(), findFocus()); + + if (newFocused == null) { + newFocused = this; + } + + if (newFocused != findFocus() + && newFocused.requestFocus(movingRight ? View.FOCUS_RIGHT : View.FOCUS_LEFT)) { + mScrollViewMovedFocus = true; + mScrollViewMovedFocus = false; + } + + invalidate(); + } + + /** + * {@inheritDoc} + * + *

    This version also clamps the scrolling to the bounds of our child. + */ + public void scrollTo(int x, int y) { + // we rely on the fact the View.scrollBy calls scrollTo. + if (getChildCount() > 0) { + View child = getChildAt(0); + x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth()); + y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight()); + if (x != mScrollX || y != mScrollY) { + super.scrollTo(x, y); + } + } + } + + private int clamp(int n, int my, int child) { + if (my >= child || n < 0) { + return 0; + } + if ((my + n) > child) { + return child - my; + } + return n; + } +} diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 36ed8bd401d1e..85a7339167316 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -336,7 +336,7 @@ public class LinearLayout extends ViewGroup { // heightMode is either UNSPECIFIED OR AT_MOST, and this child // wanted to stretch to fill available space. Translate that to // WRAP_CONTENT so that it does not end up with a height of 0 - oldHeight = lp.height; + oldHeight = 0; lp.height = LayoutParams.WRAP_CONTENT; } @@ -475,8 +475,6 @@ public class LinearLayout extends ViewGroup { matchWidthLocally ? margin : measuredWidth); allFillParent = allFillParent && lp.width == LayoutParams.FILL_PARENT; - alternativeMaxWidth = Math.max(alternativeMaxWidth, - matchWidthLocally ? margin : measuredWidth); mTotalLength += child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin + getNextLocationOffset(child); @@ -607,7 +605,7 @@ public class LinearLayout extends ViewGroup { // widthMode is either UNSPECIFIED OR AT_MOST, and this child // wanted to stretch to fill available space. Translate that to // WRAP_CONTENT so that it does not end up with a width of 0 - oldWidth = lp.width; + oldWidth = 0; lp.width = LayoutParams.WRAP_CONTENT; } @@ -766,8 +764,6 @@ public class LinearLayout extends ViewGroup { matchHeightLocally ? margin : childHeight); allFillParent = allFillParent && lp.height == LayoutParams.FILL_PARENT; - alternativeMaxHeight = Math.max(alternativeMaxHeight, - matchHeightLocally ? margin : childHeight); if (baselineAligned) { final int childBaseline = child.getBaseline(); @@ -803,8 +799,7 @@ public class LinearLayout extends ViewGroup { maxHeight = Math.max(maxHeight, ascent + descent); } } else { - alternativeMaxHeight = Math.max(alternativeMaxHeight, - weightedMaxHeight); + alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight); } if (!allFillParent && heightMode != MeasureSpec.EXACTLY) { diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index dfc7bc3843183..9c7f600779f0e 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1718,12 +1718,11 @@ public class ListView extends AbsListView { } /** - * Sets the currently selected item + * Sets the currently selected item. If in touch mode, the item will not be selected + * but it will still be positioned appropriately. If the specified selection position + * is less than 0, then the item at position 0 will be selected. * * @param position Index (starting at 0) of the data item to be selected. - * - * If in touch mode, the item will not be selected but it will still be positioned - * appropriately. */ @Override public void setSelection(int position) { diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 50248c1064c0b..dada1051632ae 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -632,16 +632,35 @@ public class PopupWindow { WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken()); preparePopup(p); + mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff); + if (mBackground != null) { mPopupView.refreshDrawableState(); } - mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff); + if (mHeightMode < 0) p.height = mLastHeight = mHeightMode; if (mWidthMode < 0) p.width = mLastWidth = mWidthMode; + p.windowAnimations = computeAnimationResource(); + invokePopup(p); } + /** + * Indicates whether the popup is showing above (the y coordinate of the popup's bottom + * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate + * of the popup is greater than y coordinate of the anchor's bottom). + * + * The value returned + * by this method is meaningful only after {@link #showAsDropDown(android.view.View)} + * or {@link #showAsDropDown(android.view.View, int, int)} was invoked. + * + * @return True if this popup is showing above the anchor view, false otherwise. + */ + public boolean isAboveAnchor() { + return mAboveAnchor; + } + /** *

    Prepare the popup by embedding in into a new ViewGroup if the * background drawable is not null. If embedding is required, the layout @@ -662,18 +681,6 @@ public class PopupWindow { popupViewContainer.setBackgroundDrawable(mBackground); popupViewContainer.addView(mContentView, listParams); - if (p.height >= 0) { - // accomodate the popup's height to take into account the - // background's padding - p.height += popupViewContainer.getPaddingTop() + - popupViewContainer.getPaddingBottom(); - } - if (p.width >= 0) { - // accomodate the popup's width to take into account the - // background's padding - p.width += popupViewContainer.getPaddingLeft() + - popupViewContainer.getPaddingRight(); - } mPopupView = popupViewContainer; } else { mPopupView = mContentView; @@ -720,7 +727,8 @@ public class PopupWindow { p.flags = computeFlags(p.flags); p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; p.token = token; - + p.setTitle("PopupWindow:" + Integer.toHexString(hashCode())); + return p; } @@ -781,7 +789,9 @@ public class PopupWindow { * * @return true if the popup is translated upwards to fit on screen */ - private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff, int yoff) { + private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, + int xoff, int yoff) { + anchor.getLocationInWindow(mDrawingLocation); p.x = mDrawingLocation[0] + xoff; p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff; @@ -795,8 +805,7 @@ public class PopupWindow { anchor.getWindowVisibleDisplayFrame(displayFrame); final View root = anchor.getRootView(); - if (mScreenLocation[1] + anchor.getMeasuredHeight() + yoff + mPopupHeight > displayFrame.bottom - || p.x + mPopupWidth - root.getWidth() > 0) { + if (p.y + mPopupHeight > displayFrame.bottom || p.x + mPopupWidth - root.getWidth() > 0) { // if the drop down disappears at the bottom of the screen. we try to // scroll a parent scrollview or move the drop down back up on top of // the edit box @@ -815,11 +824,11 @@ public class PopupWindow { // determine whether there is more space above or below the anchor anchor.getLocationOnScreen(mScreenLocation); - onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getMeasuredHeight() - yoff) - < (mScreenLocation[1] - yoff - displayFrame.top); + onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getMeasuredHeight() - yoff) < + (mScreenLocation[1] - yoff - displayFrame.top); if (onTop) { p.gravity = Gravity.LEFT | Gravity.BOTTOM; - p.y = root.getHeight() - mDrawingLocation[1] - yoff; + p.y = root.getHeight() - mDrawingLocation[1] + yoff; } else { p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff; } @@ -841,15 +850,30 @@ public class PopupWindow { * shown. */ public int getMaxAvailableHeight(View anchor) { + return getMaxAvailableHeight(anchor, 0); + } + + /** + * Returns the maximum height that is available for the popup to be + * completely shown. It is recommended that this height be the maximum for + * the popup's height, otherwise it is possible that the popup will be + * clipped. + * + * @param anchor The view on which the popup window must be anchored. + * @param yOffset y offset from the view's bottom edge + * @return The maximum available height for the popup to be completely + * shown. + */ + public int getMaxAvailableHeight(View anchor, int yOffset) { final Rect displayFrame = new Rect(); anchor.getWindowVisibleDisplayFrame(displayFrame); final int[] anchorPos = mDrawingLocation; anchor.getLocationOnScreen(anchorPos); - final int distanceToBottom = displayFrame.bottom - - (anchorPos[1] + anchor.getHeight()); - final int distanceToTop = anchorPos[1] - displayFrame.top; + final int distanceToBottom = displayFrame.bottom - + (anchorPos[1] + anchor.getHeight()) - yOffset; + final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset; // anchorPos[1] is distance from anchor to top of screen int returnedHeight = Math.max(distanceToBottom, distanceToTop); @@ -939,10 +963,12 @@ public class PopupWindow { */ public void update(int x, int y, int width, int height) { if (width != -1) { + mLastWidth = width; setWidth(width); } if (height != -1) { + mLastHeight = height; setHeight(height); } @@ -990,22 +1016,6 @@ public class PopupWindow { } if (update) { - if (mPopupView != mContentView) { - final View popupViewContainer = mPopupView; - if (p.height >= 0) { - // accomodate the popup's height to take into account the - // background's padding - p.height += popupViewContainer.getPaddingTop() + - popupViewContainer.getPaddingBottom(); - } - if (p.width >= 0) { - // accomodate the popup's width to take into account the - // background's padding - p.width += popupViewContainer.getPaddingLeft() + - popupViewContainer.getPaddingRight(); - } - } - mWindowManager.updateViewLayout(mPopupView, p); } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 2e04b5d38d1bb..434e9f3c3f91f 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -761,7 +761,7 @@ public class ProgressBar extends View { if (dr == mProgressDrawable || dr == mIndeterminateDrawable) { final Rect dirty = dr.getBounds(); final int scrollX = mScrollX + mPaddingLeft; - final int scrollY = mScrollY + mPaddingRight; + final int scrollY = mScrollY + mPaddingTop; invalidate(dirty.left + scrollX, dirty.top + scrollY, dirty.right + scrollX, dirty.bottom + scrollY); diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index ed8df229e6b58..393346a314f7b 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -122,6 +122,23 @@ public class RadioGroup extends LinearLayout { } } + @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; diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 91d58051b8580..9ded52be3c51f 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -152,7 +152,7 @@ public class RelativeLayout extends ViewGroup { private void initFromAttributes(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RelativeLayout); - mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, 0); + mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID); mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity); a.recycle(); } @@ -263,7 +263,7 @@ public class RelativeLayout extends ViewGroup { int right = Integer.MIN_VALUE; int bottom = Integer.MIN_VALUE; - if ((horizontalGravity || verticalGravity) && mIgnoreGravity != 0) { + if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) { ignore = findViewById(mIgnoreGravity); } diff --git a/location/java/com/android/internal/location/protocol/GLatLng.java b/core/java/android/widget/RemoteViews.aidl similarity index 57% rename from location/java/com/android/internal/location/protocol/GLatLng.java rename to core/java/android/widget/RemoteViews.aidl index 90e23df54db83..ec86410cf89c1 100644 --- a/location/java/com/android/internal/location/protocol/GLatLng.java +++ b/core/java/android/widget/RemoteViews.aidl @@ -1,23 +1,19 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2007, The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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 + * 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 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.internal.location.protocol; - -public interface GLatLng { - static final int LAT_E7 = 1; - static final int LNG_E7 = 2; -} +package android.widget; +parcelable RemoteViews; diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 572109574aa02..a1023bdc5d7df 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.PorterDuff; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -421,6 +422,131 @@ public class RemoteViews implements Parcelable, Filter { public final static int TAG = 7; } + /** + * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, + * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, + * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view. + *

    + * These operations will be performed on the {@link Drawable} returned by the + * target {@link View#getBackground()} by default. If targetBackground is false, + * we assume the target is an {@link ImageView} and try applying the operations + * to {@link ImageView#getDrawable()}. + *

    + * You can omit specific calls by marking their values with null or -1. + */ + private class SetDrawableParameters extends Action { + public SetDrawableParameters(int id, boolean targetBackground, int alpha, + int colorFilter, PorterDuff.Mode mode, int level) { + this.viewId = id; + this.targetBackground = targetBackground; + this.alpha = alpha; + this.colorFilter = colorFilter; + this.filterMode = mode; + this.level = level; + } + + public SetDrawableParameters(Parcel parcel) { + viewId = parcel.readInt(); + targetBackground = parcel.readInt() != 0; + alpha = parcel.readInt(); + colorFilter = parcel.readInt(); + boolean hasMode = parcel.readInt() != 0; + if (hasMode) { + filterMode = PorterDuff.Mode.valueOf(parcel.readString()); + } else { + filterMode = null; + } + level = parcel.readInt(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(TAG); + dest.writeInt(viewId); + dest.writeInt(targetBackground ? 1 : 0); + dest.writeInt(alpha); + dest.writeInt(colorFilter); + if (filterMode != null) { + dest.writeInt(1); + dest.writeString(filterMode.toString()); + } else { + dest.writeInt(0); + } + dest.writeInt(level); + } + + @Override + public void apply(View root) { + final View target = root.findViewById(viewId); + if (target == null) { + return; + } + + // Pick the correct drawable to modify for this view + Drawable targetDrawable = null; + if (targetBackground) { + targetDrawable = target.getBackground(); + } else if (target instanceof ImageView) { + ImageView imageView = (ImageView) target; + targetDrawable = imageView.getDrawable(); + } + + // Perform modifications only if values are set correctly + if (alpha != -1) { + targetDrawable.setAlpha(alpha); + } + if (colorFilter != -1 && filterMode != null) { + targetDrawable.setColorFilter(colorFilter, filterMode); + } + if (level != -1) { + targetDrawable.setLevel(level); + } + } + + int viewId; + boolean targetBackground; + int alpha; + int colorFilter; + PorterDuff.Mode filterMode; + int level; + + public final static int TAG = 8; + } + + /** + * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}. + */ + private class SetTextColor extends Action { + public SetTextColor(int id, int color) { + this.viewId = id; + this.color = color; + } + + public SetTextColor(Parcel parcel) { + viewId = parcel.readInt(); + color = parcel.readInt(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(TAG); + dest.writeInt(viewId); + dest.writeInt(color); + } + + @Override + public void apply(View root) { + final View target = root.findViewById(viewId); + if (target instanceof TextView) { + final TextView textView = (TextView) target; + textView.setTextColor(color); + } + } + + int viewId; + int color; + + public final static int TAG = 9; + } + /** * Create a new RemoteViews object that will display the views contained * in the specified layout file. @@ -471,6 +597,12 @@ public class RemoteViews implements Parcelable, Filter { case SetOnClickPendingIntent.TAG: mActions.add(new SetOnClickPendingIntent(parcel)); break; + case SetDrawableParameters.TAG: + mActions.add(new SetDrawableParameters(parcel)); + break; + case SetTextColor.TAG: + mActions.add(new SetTextColor(parcel)); + break; default: throw new ActionException("Tag " + tag + "not found"); } @@ -594,6 +726,48 @@ public class RemoteViews implements Parcelable, Filter { addAction(new SetOnClickPendingIntent(viewId, pendingIntent)); } + /** + * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, + * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, + * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given + * view. + *

    + * You can omit specific calls by marking their values with null or -1. + * + * @param viewId The id of the view that contains the target + * {@link Drawable} + * @param targetBackground If true, apply these parameters to the + * {@link Drawable} returned by + * {@link android.view.View#getBackground()}. Otherwise, assume + * the target view is an {@link ImageView} and apply them to + * {@link ImageView#getDrawable()}. + * @param alpha Specify an alpha value for the drawable, or -1 to leave + * unchanged. + * @param colorFilter Specify a color for a + * {@link android.graphics.ColorFilter} for this drawable, or -1 + * to leave unchanged. + * @param mode Specify a PorterDuff mode for this drawable, or null to leave + * unchanged. + * @param level Specify the level for the drawable, or -1 to leave + * unchanged. + */ + public void setDrawableParameters(int viewId, boolean targetBackground, int alpha, + int colorFilter, PorterDuff.Mode mode, int level) { + addAction(new SetDrawableParameters(viewId, targetBackground, alpha, + colorFilter, mode, level)); + } + + /** + * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}. + * + * @param viewId The id of the view whose text should change + * @param color Sets the text color for all the states (normal, selected, + * focused) to be this color. + */ + public void setTextColor(int viewId, int color) { + addAction(new SetTextColor(viewId, color)); + } + /** * Inflates the view hierarchy represented by this object and applies * all of the actions. diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java index 17f912822e46a..3b113ae332dd9 100644 --- a/core/java/android/widget/ScrollBarDrawable.java +++ b/core/java/android/widget/ScrollBarDrawable.java @@ -152,10 +152,12 @@ public class ScrollBarDrawable extends Drawable { } else { track = mHorizontalTrack; } - if (mChanged) { - track.setBounds(bounds); + if (track != null) { + if (mChanged) { + track.setBounds(bounds); + } + track.draw(canvas); } - track.draw(canvas); } protected void drawThumb(Canvas canvas, Rect bounds, int offset, int length, boolean vertical) { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 20166cf72af69..c9b3751764eb2 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -44,13 +44,7 @@ import java.util.List; * manager with a complex hierarchy of objects. A child that is often used * is a {@link LinearLayout} in a vertical orientation, presenting a vertical * array of top-level items that the user can scroll through. - * - *

    You should never use a ScrollView with a {@link ListView}, since - * ListView takes care of its own scrolling. Most importantly, doing this - * defeats all of the important optimizations in ListView for dealing with - * large lists, since it effectively forces the ListView to display its entire - * list of items to fill up the infinite container supplied by ScrollView. - * + * *

    The {@link TextView} class also * takes care of its own scrolling, so does not require a ScrollView, but * using the two together is possible to achieve the effect of a text view @@ -62,13 +56,9 @@ public class ScrollView extends FrameLayout { static final String TAG = "ScrollView"; static final boolean localLOGV = false || Config.LOGV; - private static final int ANIMATED_SCROLL_GAP = 250; + static final int ANIMATED_SCROLL_GAP = 250; - /** - * When arrow scrolling, ListView will never scroll more than this factor - * times the height of the list. - */ - private static final float MAX_SCROLL_FACTOR = 0.5f; + static final float MAX_SCROLL_FACTOR = 0.5f; private long mLastScroll; @@ -124,6 +114,8 @@ public class ScrollView extends FrameLayout { */ private boolean mSmoothScrollingEnabled = true; + private int mTouchSlop; + public ScrollView(Context context) { this(context, null); } @@ -165,8 +157,8 @@ public class ScrollView extends FrameLayout { } final int length = getVerticalFadingEdgeLength(); - final int bottom = getChildAt(0).getBottom(); - final int span = bottom - mScrollY - getHeight(); + final int bottomEdge = getHeight() - mPaddingBottom; + final int span = getChildAt(0).getBottom() - mScrollY - bottomEdge; if (span < length) { return span / (float) length; } @@ -188,6 +180,7 @@ public class ScrollView extends FrameLayout { setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); + mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @Override @@ -401,7 +394,7 @@ public class ScrollView extends FrameLayout { * of the down event. */ final int yDiff = (int) Math.abs(y - mLastMotionY); - if (yDiff > ViewConfiguration.getTouchSlop()) { + if (yDiff > mTouchSlop) { mIsBeingDragged = true; } break; @@ -488,8 +481,9 @@ public class ScrollView extends FrameLayout { velocityTracker.computeCurrentVelocity(1000); int initialVelocity = (int) velocityTracker.getYVelocity(); - if ((Math.abs(initialVelocity) > ViewConfiguration.getMinimumFlingVelocity()) && - (getChildCount() > 0)) { + if ((Math.abs(initialVelocity) > + ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && + getChildCount() > 0) { fling(-initialVelocity); } @@ -812,7 +806,7 @@ public class ScrollView extends FrameLayout { /** * Smooth scroll by a Y delta * - * @param delta the number of pixels to scroll by on the X axis + * @param delta the number of pixels to scroll by on the Y axis */ private void doScrollY(int delta) { if (delta != 0) { @@ -923,8 +917,8 @@ public class ScrollView extends FrameLayout { int y = mScroller.getCurrY(); if (getChildCount() > 0) { View child = getChildAt(0); - mScrollX = clamp(x, this.getWidth(), child.getWidth()); - mScrollY = clamp(y, this.getHeight(), child.getHeight()); + mScrollX = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth()); + mScrollY = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight()); if (localLOGV) Log.v(TAG, "mScrollY=" + mScrollY + " y=" + y + " height=" + this.getHeight() + " child height=" + child.getHeight()); @@ -1167,8 +1161,8 @@ public class ScrollView extends FrameLayout { * which means we want to scroll towards the top. */ public void fling(int velocityY) { - int height = getHeight(); - int bottom = getChildAt(getChildCount() - 1).getBottom(); + int height = getHeight() - mPaddingBottom - mPaddingTop; + int bottom = getChildAt(0).getHeight(); mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, bottom - height); @@ -1198,8 +1192,8 @@ public class ScrollView extends FrameLayout { // we rely on the fact the View.scrollBy calls scrollTo. if (getChildCount() > 0) { View child = getChildAt(0); - x = clamp(x, this.getWidth(), child.getWidth()); - y = clamp(y, this.getHeight(), child.getHeight()); + x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth()); + y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight()); if (x != mScrollX || y != mScrollY) { super.scrollTo(x, y); } diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java index fbe5e576a52c2..febc95643a050 100644 --- a/core/java/android/widget/Scroller.java +++ b/core/java/android/widget/Scroller.java @@ -131,6 +131,26 @@ public class Scroller { return mCurrY; } + /** + * Returns the start X offset in the scroll. + * + * @return The start X offset as an absolute distance from the origin. + * @hide pending API council + */ + public final int getStartX() { + return mStartX; + } + + /** + * Returns the start Y offset in the scroll. + * + * @return The start Y offset as an absolute distance from the origin. + * @hide pending API council + */ + public final int getStartY() { + return mStartY; + } + /** * Returns where the scroll will end. Valid only for "fling" scrolls. * diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java index 261da9ff46a8a..093c24e0ca58e 100644 --- a/core/java/android/widget/SimpleAdapter.java +++ b/core/java/android/widget/SimpleAdapter.java @@ -36,12 +36,16 @@ import java.util.Map; * Binding data to views occurs in two phases. First, if a * {@link android.widget.SimpleAdapter.ViewBinder} is available, * {@link ViewBinder#setViewValue(android.view.View, Object, String)} - * is invoked. If the returned value is true, binding has occured. If the - * returned value is false and the view to bind is a TextView, - * {@link #setViewText(TextView, String)} is invoked. If the returned value - * is false and the view to bind is an ImageView, - * {@link #setViewImage(ImageView, int)} or {@link #setViewImage(ImageView, String)} is - * invoked. If no appropriate binding can be found, an {@link IllegalStateException} is thrown. + * is invoked. If the returned value is true, binding has occurred. + * If the returned value is false, the following views are then tried in order: + *

      + *
    • A view that implements Checkable (e.g. CheckBox). The expected bind value is a boolean. + *
    • TextView. The expected bind value is a string and {@link #setViewText(TextView, String)} + * is invoked. + *
    • ImageView. The expected bind value is a resource id or a string and + * {@link #setViewImage(ImageView, int)} or {@link #setViewImage(ImageView, String)} is invoked. + *
    + * If no appropriate binding can be found, an {@link IllegalStateException} is thrown. */ public class SimpleAdapter extends BaseAdapter implements Filterable { private int[] mTo; @@ -176,7 +180,16 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { } if (!bound) { - if (v instanceof TextView) { + if (v instanceof Checkable) { + if (data instanceof Boolean) { + ((Checkable) v).setChecked((Boolean) data); + } else { + throw new IllegalStateException(v.getClass().getName() + + " should be bound to a Boolean, not a " + data.getClass()); + } + } else if (v instanceof TextView) { + // Note: keep the instanceof TextView check at the bottom of these + // ifs since a lot of views are TextViews (e.g. CheckBoxes). setViewText((TextView) v, text); } else if (v instanceof ImageView) { if (data instanceof Integer) { diff --git a/core/java/com/android/internal/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java similarity index 91% rename from core/java/com/android/internal/widget/SlidingDrawer.java rename to core/java/android/widget/SlidingDrawer.java index a4045d553a024..e77c4ca482702 100644 --- a/core/java/com/android/internal/widget/SlidingDrawer.java +++ b/core/java/android/widget/SlidingDrawer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.widget; +package android.widget; import android.view.ViewGroup; import android.view.View; @@ -30,7 +30,7 @@ import android.graphics.Bitmap; import android.os.SystemClock; import android.os.Handler; import android.os.Message; -import com.android.internal.R; +import android.R; /** * SlidingDrawer hides content out of the screen and allows the user to drag a handle @@ -48,7 +48,7 @@ import com.android.internal.R; * content: * *
    - * <com.android.internal.widget.SlidingDrawer
    + * <SlidingDrawer
      *     android:id="@+id/drawer"
      *     android:layout_width="fill_parent"
      *     android:layout_height="fill_parent"
    @@ -66,16 +66,16 @@ import com.android.internal.R;
      *         android:layout_width="fill_parent"
      *         android:layout_height="fill_parent" />
      *
    - * </com.android.internal.widget.SlidingDrawer>
    + * </SlidingDrawer>
      * 
    * - * @attr ref com.android.internal.R.styleable#SlidingDrawer_content - * @attr ref com.android.internal.R.styleable#SlidingDrawer_handle - * @attr ref com.android.internal.R.styleable#SlidingDrawer_topOffset - * @attr ref com.android.internal.R.styleable#SlidingDrawer_bottomOffset - * @attr ref com.android.internal.R.styleable#SlidingDrawer_orientation - * @attr ref com.android.internal.R.styleable#SlidingDrawer_allowSingleTap - * @attr ref com.android.internal.R.styleable#SlidingDrawer_animateOnClick + * @attr ref android.R.styleable#SlidingDrawer_content + * @attr ref android.R.styleable#SlidingDrawer_handle + * @attr ref android.R.styleable#SlidingDrawer_topOffset + * @attr ref android.R.styleable#SlidingDrawer_bottomOffset + * @attr ref android.R.styleable#SlidingDrawer_orientation + * @attr ref android.R.styleable#SlidingDrawer_allowSingleTap + * @attr ref android.R.styleable#SlidingDrawer_animateOnClick */ public class SlidingDrawer extends ViewGroup { public static final int ORIENTATION_HORIZONTAL = 0; @@ -128,6 +128,13 @@ public class SlidingDrawer extends ViewGroup { private boolean mAllowSingleTap; private boolean mAnimateOnClick; + private final int mTapThreshold; + private final int mMaximumTapVelocity; + private final int mMaximumMinorVelocity; + private final int mMaximumMajorVelocity; + private final int mMaximumAcceleration; + private final int mVelocityUnits; + /** * Callback invoked when the drawer is opened. */ @@ -206,6 +213,14 @@ public class SlidingDrawer extends ViewGroup { mHandleId = handleId; mContentId = contentId; + final float density = getResources().getDisplayMetrics().density; + mTapThreshold = (int) (TAP_THRESHOLD * density + 0.5f); + mMaximumTapVelocity = (int) (MAXIMUM_TAP_VELOCITY * density + 0.5f); + mMaximumMinorVelocity = (int) (MAXIMUM_MINOR_VELOCITY * density + 0.5f); + mMaximumMajorVelocity = (int) (MAXIMUM_MAJOR_VELOCITY * density + 0.5f); + mMaximumAcceleration = (int) (MAXIMUM_ACCELERATION * density + 0.5f); + mVelocityUnits = (int) (VELOCITY_UNITS * density + 0.5f); + a.recycle(); setAlwaysDrawnWithCacheEnabled(false); @@ -383,7 +398,7 @@ public class SlidingDrawer extends ViewGroup { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(VELOCITY_UNITS); + velocityTracker.computeCurrentVelocity(mVelocityUnits); float yVelocity = velocityTracker.getYVelocity(); float xVelocity = velocityTracker.getXVelocity(); @@ -395,16 +410,16 @@ public class SlidingDrawer extends ViewGroup { if (xVelocity < 0) { xVelocity = -xVelocity; } - if (xVelocity > MAXIMUM_MINOR_VELOCITY) { - xVelocity = MAXIMUM_MINOR_VELOCITY; + if (xVelocity > mMaximumMinorVelocity) { + xVelocity = mMaximumMinorVelocity; } } else { negative = xVelocity < 0; if (yVelocity < 0) { yVelocity = -yVelocity; } - if (yVelocity > MAXIMUM_MINOR_VELOCITY) { - yVelocity = MAXIMUM_MINOR_VELOCITY; + if (yVelocity > mMaximumMinorVelocity) { + yVelocity = mMaximumMinorVelocity; } } @@ -416,13 +431,13 @@ public class SlidingDrawer extends ViewGroup { final int top = mHandle.getTop(); final int left = mHandle.getLeft(); - if (Math.abs(velocity) < MAXIMUM_TAP_VELOCITY) { - if (vertical ? (mExpanded && top < TAP_THRESHOLD + mTopOffset) || + if (Math.abs(velocity) < mMaximumTapVelocity) { + if (vertical ? (mExpanded && top < mTapThreshold + mTopOffset) || (!mExpanded && top > mBottomOffset + mBottom - mTop - - mHandleHeight - TAP_THRESHOLD) : - (mExpanded && left < TAP_THRESHOLD + mTopOffset) || + mHandleHeight - mTapThreshold) : + (mExpanded && left < mTapThreshold + mTopOffset) || (!mExpanded && left > mBottomOffset + mRight - mLeft - - mHandleWidth - TAP_THRESHOLD)) { + mHandleWidth - mTapThreshold)) { if (mAllowSingleTap) { playSoundEffect(SoundEffectConstants.CLICK); @@ -450,12 +465,12 @@ public class SlidingDrawer extends ViewGroup { private void animateClose(int position) { prepareTracking(position); - performFling(position, 2000.0f, true); + performFling(position, mMaximumAcceleration, true); } private void animateOpen(int position) { prepareTracking(position); - performFling(position, -2000.0f, true); + performFling(position, -mMaximumAcceleration, true); } private void performFling(int position, float velocity, boolean always) { @@ -463,35 +478,35 @@ public class SlidingDrawer extends ViewGroup { mAnimatedVelocity = velocity; if (mExpanded) { - if (always || (velocity > MAXIMUM_MAJOR_VELOCITY || + if (always || (velocity > mMaximumMajorVelocity || (position > mTopOffset + (mVertical ? mHandleHeight : mHandleWidth) && - velocity > -MAXIMUM_MAJOR_VELOCITY))) { + velocity > -mMaximumMajorVelocity))) { // We are expanded, but they didn't move sufficiently to cause // us to retract. Animate back to the expanded position. - mAnimatedAcceleration = MAXIMUM_ACCELERATION; + mAnimatedAcceleration = mMaximumAcceleration; if (velocity < 0) { mAnimatedVelocity = 0; } } else { // We are expanded and are now going to animate away. - mAnimatedAcceleration = -MAXIMUM_ACCELERATION; + mAnimatedAcceleration = -mMaximumAcceleration; if (velocity > 0) { mAnimatedVelocity = 0; } } } else { - if (!always && (velocity > MAXIMUM_MAJOR_VELOCITY || + if (!always && (velocity > mMaximumMajorVelocity || (position > (mVertical ? getHeight() : getWidth()) / 2 && - velocity > -MAXIMUM_MAJOR_VELOCITY))) { + velocity > -mMaximumMajorVelocity))) { // We are collapsed, and they moved enough to allow us to expand. - mAnimatedAcceleration = MAXIMUM_ACCELERATION; + mAnimatedAcceleration = mMaximumAcceleration; if (velocity < 0) { mAnimatedVelocity = 0; } } else { // We are collapsed, but they didn't move sufficiently to cause // us to retract. Animate back to the collapsed position. - mAnimatedAcceleration = -MAXIMUM_ACCELERATION; + mAnimatedAcceleration = -mMaximumAcceleration; if (velocity > 0) { mAnimatedVelocity = 0; } @@ -512,8 +527,8 @@ public class SlidingDrawer extends ViewGroup { mVelocityTracker = VelocityTracker.obtain(); boolean opening = !mExpanded; if (opening) { - mAnimatedAcceleration = MAXIMUM_ACCELERATION; - mAnimatedVelocity = 200; + mAnimatedAcceleration = mMaximumAcceleration; + mAnimatedVelocity = mMaximumMajorVelocity; mAnimationPosition = mBottomOffset + (mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth); moveHandle((int) mAnimationPosition); @@ -782,6 +797,7 @@ public class SlidingDrawer extends ViewGroup { private void closeDrawer() { moveHandle(COLLAPSED_FULL_CLOSED); mContent.setVisibility(View.GONE); + mContent.destroyDrawingCache(); if (!mExpanded) { return; @@ -796,8 +812,6 @@ public class SlidingDrawer extends ViewGroup { private void openDrawer() { moveHandle(EXPANDED_FULL_OPEN); mContent.setVisibility(View.VISIBLE); - // TODO: Should we uncomment to preserve memory, but increase memory churn? - // mContent.destroyDrawingCache(); if (mExpanded) { return; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index aa70663ff649a..d21c01722d967 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -17,6 +17,7 @@ package android.widget; import android.content.Context; +import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; @@ -42,6 +43,7 @@ import android.text.GraphicsOperations; import android.text.ClipboardManager; import android.text.InputFilter; import android.text.Layout; +import android.text.ParcelableSpan; import android.text.Selection; import android.text.SpanWatcher; import android.text.Spannable; @@ -69,7 +71,6 @@ import android.text.method.TransformationMethod; import android.text.style.ParagraphStyle; import android.text.style.URLSpan; import android.text.style.UpdateAppearance; -import android.text.style.UpdateLayout; import android.text.util.Linkify; import android.util.AttributeSet; import android.util.Log; @@ -168,6 +169,9 @@ import org.xmlpull.v1.XmlPullParserException; */ @RemoteView public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { + static final String TAG = "TextView"; + static final boolean DEBUG_EXTRACT = false; + private static int PRIORITY = 100; private ColorStateList mTextColor; @@ -178,6 +182,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mFreezesText; private boolean mFrozenWithFocus; + private boolean mEatTouchRelease = false; + private boolean mScrolled = false; + private Editable.Factory mEditableFactory = Editable.Factory.getInstance(); private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance(); @@ -213,6 +220,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private CharSequence mError; private boolean mErrorWasChanged; private PopupWindow mPopup; + /** + * This flag is set if the TextView tries to display an error before it + * is attached to the window (so its position is still unknown). + * It causes the error to be shown later, when onAttachedToWindow() + * is called. + */ + private boolean mShowErrorAfterAttach; private CharWrapper mCharWrapper = null; @@ -235,7 +249,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener float[] mTmpOffset = new float[2]; ExtractedTextRequest mExtracting; final ExtractedText mTmpExtracted = new ExtractedText(); - boolean mBatchEditing; + int mBatchEditNesting; + boolean mCursorChanged; + boolean mContentChanged; + int mChangedStart, mChangedEnd, mChangedDelta; } InputMethodState mInputMethodState; @@ -2342,9 +2359,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Sets the string value of the TextView. TextView does not accept * HTML-like formatting, which you can do with text strings in XML resource files. * To style your strings, attach android.text.style.* objects to a - * {@link android.text.SpannableString SpannableString}, or see - * - * String Resources for an example of setting formatted text in the XML resource file. + * {@link android.text.SpannableString SpannableString}, or see the + * + * Available Resource Types documentation for an example of setting + * formatted text in the XML resource file. * * @attr ref android.R.styleable#TextView_text */ @@ -2698,20 +2716,33 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public void setInputType(int type) { setInputType(type, false); - if ((type&(EditorInfo.TYPE_MASK_CLASS + final boolean isPassword = (type&(EditorInfo.TYPE_MASK_CLASS |EditorInfo.TYPE_MASK_VARIATION)) == (EditorInfo.TYPE_CLASS_TEXT - |EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) { + |EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + boolean forceUpdate = false; + if (isPassword) { setTransformationMethod(PasswordTransformationMethod.getInstance()); setTypefaceByIndex(MONOSPACE, 0); + } else if (mTransformation == PasswordTransformationMethod.getInstance()) { + // We need to clean up if we were previously in password mode. + setTypefaceByIndex(-1, -1); + forceUpdate = true; } + boolean multiLine = (type&(EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE)) == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE); - if (mSingleLine == multiLine) { - setSingleLine(!multiLine); + + // We need to update the single line mode if it has changed or we + // were previously in password mode. + if (mSingleLine == multiLine || forceUpdate) { + // Change single line mode, but only change the transformation if + // we are not in password mode. + applySingleLine(!multiLine, !isPassword); } + InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) imm.restartInput(this); } @@ -2814,7 +2845,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * given integer is the resource ID of an XML resource holding an * {@link android.R.styleable#InputExtras <input-extras>} XML tree. * - * @see #getInputExtras() + * @see #getInputExtras(boolean) * @see EditorInfo#extras * @attr ref android.R.styleable#TextView_editorExtras */ @@ -2832,7 +2863,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @param create If true, the extras will be created if they don't already * exist. Otherwise, null will be returned if none have been created. - * @see #setInputExtras(int) + * @see #setInputExtras(int)View * @see EditorInfo#extras * @attr ref android.R.styleable#TextView_editorExtras */ @@ -2916,6 +2947,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void showError() { + if (getWindowToken() == null) { + mShowErrorAfterAttach = true; + return; + } + if (mPopup == null) { LayoutInflater inflater = LayoutInflater.from(getContext()); TextView err = (TextView) inflater.inflate(com.android.internal.R.layout.textview_hint, @@ -2979,6 +3015,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mPopup.dismiss(); } } + + mShowErrorAfterAttach = false; } private void chooseSize(PopupWindow pop, CharSequence text, TextView tv) { @@ -3268,6 +3306,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return !changed; } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mShowErrorAfterAttach) { + showError(); + mShowErrorAfterAttach = false; + } + } + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); @@ -3310,6 +3358,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return (int) Math.max(0, mShadowDx + mShadowRadius); } + @Override + protected boolean verifyDrawable(Drawable who) { + final boolean verified = super.verifyDrawable(who); + if (!verified && mDrawables != null) { + return who == mDrawables.mDrawableLeft || who == mDrawables.mDrawableTop || + who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom; + } + return verified; + } + @Override protected void onDraw(Canvas canvas) { // Draw the background for this view @@ -3505,38 +3563,48 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } */ - InputMethodManager imm = InputMethodManager.peekInstance(); - if (highlight != null && mInputMethodState != null - && !mInputMethodState.mBatchEditing && imm != null) { - if (imm.isActive(this)) { - int candStart = -1; - int candEnd = -1; - if (mText instanceof Spannable) { - Spannable sp = (Spannable)mText; - candStart = EditableInputConnection.getComposingSpanStart(sp); - candEnd = EditableInputConnection.getComposingSpanEnd(sp); + final InputMethodState ims = mInputMethodState; + if (ims != null && ims.mBatchEditNesting == 0) { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + if (imm.isActive(this)) { + boolean reported = false; + if (ims.mContentChanged) { + // We are in extract mode and the content has changed + // in some way... just report complete new text to the + // input method. + reported = reportExtractedText(); + } + if (!reported && highlight != null) { + int candStart = -1; + int candEnd = -1; + if (mText instanceof Spannable) { + Spannable sp = (Spannable)mText; + candStart = EditableInputConnection.getComposingSpanStart(sp); + candEnd = EditableInputConnection.getComposingSpanEnd(sp); + } + imm.updateSelection(this, selStart, selEnd, candStart, candEnd); + } + } + + if (imm.isWatchingCursor(this) && highlight != null) { + highlight.computeBounds(ims.mTmpRectF, true); + ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0; + + canvas.getMatrix().mapPoints(ims.mTmpOffset); + ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]); + + ims.mTmpRectF.offset(0, voffsetCursor - voffsetText); + + ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5), + (int)(ims.mTmpRectF.top + 0.5), + (int)(ims.mTmpRectF.right + 0.5), + (int)(ims.mTmpRectF.bottom + 0.5)); + + imm.updateCursor(this, + ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top, + ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom); } - imm.updateSelection(this, selStart, selEnd, candStart, candEnd); - } - - if (imm.isWatchingCursor(this)) { - final InputMethodState ims = mInputMethodState; - highlight.computeBounds(ims.mTmpRectF, true); - ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0; - - canvas.getMatrix().mapPoints(ims.mTmpOffset); - ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]); - - ims.mTmpRectF.offset(0, voffsetCursor - voffsetText); - - ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5), - (int)(ims.mTmpRectF.top + 0.5), - (int)(ims.mTmpRectF.right + 0.5), - (int)(ims.mTmpRectF.bottom + 0.5)); - - imm.updateCursor(this, - ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top, - ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom); } } @@ -3634,7 +3702,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - int which = doKeyDown(keyCode, event); + int which = doKeyDown(keyCode, event, null); if (which == 0) { // Go through default dispatching. return super.onKeyDown(keyCode, event); @@ -3647,12 +3715,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN); - int which = doKeyDown(keyCode, down); + int which = doKeyDown(keyCode, down, event); if (which == 0) { // Go through default dispatching. return super.onKeyMultiple(keyCode, repeatCount, event); } + if (which == -1) { + // Consumed the whole thing. + return true; + } + repeatCount--; + // We are going to dispatch the remaining events to either the input // or movement method. To do this, we will just send a repeated stream // of down and up events until we have done the complete repeatCount. @@ -3680,7 +3754,34 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } - private int doKeyDown(int keyCode, KeyEvent event) { + /** + * Returns true if pressing ENTER in this field advances focus instead + * of inserting the character. This is true mostly in single-line fields, + * but also in mail addresses and subjects which will display on multiple + * lines but where it doesn't make sense to insert newlines. + */ + private boolean advanceFocusOnEnter() { + if (mInput == null) { + return false; + } + + if (mSingleLine) { + return true; + } + + if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { + int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION; + + if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS || + variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT) { + return true; + } + } + + return false; + } + + private int doKeyDown(int keyCode, KeyEvent event, KeyEvent otherEvent) { if (!isEnabled()) { return 0; } @@ -3688,7 +3789,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: - if (mSingleLine && mInput != null) { + if (advanceFocusOnEnter()) { return 0; } } @@ -3702,20 +3803,63 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ mErrorWasChanged = false; - if (mInput.onKeyDown(this, (Editable) mText, keyCode, event)) { - if (mError != null && !mErrorWasChanged) { - setError(null, null); + boolean doDown = true; + if (otherEvent != null) { + try { + beginBatchEdit(); + boolean handled = mInput.onKeyOther(this, (Editable) mText, + otherEvent); + if (mError != null && !mErrorWasChanged) { + setError(null, null); + } + doDown = false; + if (handled) { + return -1; + } + } catch (AbstractMethodError e) { + // onKeyOther was added after 1.0, so if it isn't + // implemented we need to try to dispatch as a regular down. + } finally { + endBatchEdit(); } - return 1; + } + + if (doDown) { + beginBatchEdit(); + if (mInput.onKeyDown(this, (Editable) mText, keyCode, event)) { + endBatchEdit(); + if (mError != null && !mErrorWasChanged) { + setError(null, null); + } + return 1; + } + endBatchEdit(); } } // bug 650865: sometimes we get a key event before a layout. // don't try to move around if we don't know the layout. - if (mMovement != null && mLayout != null) - if (mMovement.onKeyDown(this, (Spannable)mText, keyCode, event)) - return 2; + if (mMovement != null && mLayout != null) { + boolean doDown = true; + if (otherEvent != null) { + try { + boolean handled = mMovement.onKeyOther(this, (Editable) mText, + otherEvent); + doDown = false; + if (handled) { + return -1; + } + } catch (AbstractMethodError e) { + // onKeyOther was added after 1.0, so if it isn't + // implemented we need to try to dispatch as a regular down. + } + } + if (doDown) { + if (mMovement.onKeyDown(this, (Spannable)mText, keyCode, event)) + return 2; + } + } return 0; } @@ -3728,8 +3872,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: + /* + * If there is a click listener, just call through to + * super, which will invoke it. + * + * If there isn't a click listener, try to show the soft + * input method. (It will also + * call performClick(), but that won't do anything in + * this case.) + */ + if (mOnClickListener == null) { + if (mMovement != null && mText instanceof Editable + && mLayout != null && onCheckIsTextEditor()) { + InputMethodManager imm = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(this, 0); + } + } + return super.onKeyUp(keyCode, event); + case KeyEvent.KEYCODE_ENTER: - if (mSingleLine && mInput != null) { + if (advanceFocusOnEnter()) { /* * If there is a click listener, just call through to * super, which will invoke it. @@ -3803,14 +3966,56 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * If this TextView contains editable content, extract a portion of it * based on the information in request in to outText. - * @return Returns true if the text is editable and was successfully - * extracted, else false. + * @return Returns true if the text was successfully extracted, else false. */ public boolean extractText(ExtractedTextRequest request, ExtractedText outText) { - Editable content = getEditableText(); + return extractTextInternal(request, -1, -1, -1, outText); + } + + boolean extractTextInternal(ExtractedTextRequest request, + int partialStartOffset, int partialEndOffset, int delta, + ExtractedText outText) { + final CharSequence content = mText; if (content != null) { - outText.text = content.subSequence(0, content.length()); + final int N = content.length(); + if (partialStartOffset < 0) { + outText.partialStartOffset = outText.partialEndOffset = -1; + partialStartOffset = 0; + partialEndOffset = N; + } else { + // Adjust offsets to ensure we contain full spans. + if (content instanceof Spanned) { + Spanned spanned = (Spanned)content; + Object[] spans = spanned.getSpans(partialStartOffset, + partialEndOffset, ParcelableSpan.class); + int i = spans.length; + while (i > 0) { + i--; + int j = spanned.getSpanStart(spans[i]); + if (j < partialStartOffset) partialStartOffset = j; + j = spanned.getSpanEnd(spans[i]); + if (j > partialEndOffset) partialEndOffset = j; + } + } + outText.partialStartOffset = partialStartOffset; + outText.partialEndOffset = partialEndOffset; + // Now use the delta to determine the actual amount of text + // we need. + partialEndOffset += delta; + if (partialEndOffset > N) { + partialEndOffset = N; + } else if (partialEndOffset < 0) { + partialEndOffset = 0; + } + } + if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) { + outText.text = content.subSequence(partialStartOffset, + partialEndOffset); + } else { + outText.text = TextUtils.substring(content, partialStartOffset, + partialEndOffset); + } outText.startOffset = 0; outText.selectionStart = Selection.getSelectionStart(content); outText.selectionEnd = Selection.getSelectionEnd(content); @@ -3819,19 +4024,45 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - void reportExtractedText() { - if (mInputMethodState != null) { + boolean reportExtractedText() { + final InputMethodState ims = mInputMethodState; + if (ims != null && ims.mContentChanged) { + ims.mContentChanged = false; final ExtractedTextRequest req = mInputMethodState.mExtracting; if (req != null) { InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { - if (extractText(req, mInputMethodState.mTmpExtracted)) { + if (DEBUG_EXTRACT) Log.v(TAG, "Retrieving extracted start=" + + ims.mChangedStart + " end=" + ims.mChangedEnd + + " delta=" + ims.mChangedDelta); + if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd, + ims.mChangedDelta, ims.mTmpExtracted)) { + if (DEBUG_EXTRACT) Log.v(TAG, "Reporting extracted start=" + + ims.mTmpExtracted.partialStartOffset + + " end=" + ims.mTmpExtracted.partialEndOffset + + ": " + ims.mTmpExtracted.text); imm.updateExtractedText(this, req.token, mInputMethodState.mTmpExtracted); + return true; } } } } + return false; + } + + /** + * This is used to remove all style-impacting spans from text before new + * extracted text is being replaced into it, so that we don't have any + * lingering spans applied during the replace. + */ + static void removeParcelableSpans(Spannable spannable, int start, int end) { + Object[] spans = spannable.getSpans(start, end, ParcelableSpan.class); + int i = spans.length; + while (i > 0) { + i--; + spannable.removeSpan(spans[i]); + } } /** @@ -3839,7 +4070,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * returned by {@link #extractText(ExtractedTextRequest, ExtractedText)}. */ public void setExtractedText(ExtractedText text) { - setText(text.text, TextView.BufferType.EDITABLE); + Editable content = getEditableText(); + if (content == null) { + setText(text.text, TextView.BufferType.EDITABLE); + } else if (text.partialStartOffset < 0) { + removeParcelableSpans(content, 0, content.length()); + content.replace(0, content.length(), text.text); + } else { + final int N = content.length(); + int start = text.partialStartOffset; + if (start > N) start = N; + int end = text.partialEndOffset; + if (end > N) end = N; + removeParcelableSpans(content, start, end); + content.replace(start, end, text.text); + } Selection.setSelection((Spannable)getText(), text.selectionStart, text.selectionEnd); } @@ -3866,36 +4111,91 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void onCommitCompletion(CompletionInfo text) { } + public void beginBatchEdit() { + final InputMethodState ims = mInputMethodState; + if (ims != null) { + int nesting = ++ims.mBatchEditNesting; + if (nesting == 1) { + ims.mCursorChanged = false; + ims.mChangedDelta = 0; + if (ims.mContentChanged) { + // We already have a pending change from somewhere else, + // so turn this into a full update. + ims.mChangedStart = 0; + ims.mChangedEnd = mText.length(); + } else { + ims.mChangedStart = -1; + ims.mChangedEnd = -1; + ims.mContentChanged = false; + } + onBeginBatchEdit(); + } + } + } + + public void endBatchEdit() { + final InputMethodState ims = mInputMethodState; + if (ims != null) { + int nesting = --ims.mBatchEditNesting; + if (nesting == 0) { + finishBatchEdit(ims); + } + } + } + + void ensureEndedBatchEdit() { + final InputMethodState ims = mInputMethodState; + if (ims != null && ims.mBatchEditNesting != 0) { + ims.mBatchEditNesting = 0; + finishBatchEdit(ims); + } + } + + void finishBatchEdit(final InputMethodState ims) { + onEndBatchEdit(); + + if (ims.mContentChanged) { + updateAfterEdit(); + reportExtractedText(); + } else if (ims.mCursorChanged) { + // Cheezy way to get us to report the current cursor location. + invalidateCursor(); + } + } + + void updateAfterEdit() { + invalidate(); + int curs = Selection.getSelectionStart(mText); + + if (curs >= 0 || (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == + Gravity.BOTTOM) { + registerForPreDraw(); + } + + if (curs >= 0) { + mHighlightPathBogus = true; + + if (isFocused()) { + mShowCursor = SystemClock.uptimeMillis(); + makeBlink(); + } + } + + checkForResize(); + } + /** * Called by the framework in response to a request to begin a batch - * of edit operations from the current input method, as a result of - * it calling {@link InputConnection#beginBatchEdit - * InputConnection.beginBatchEdit()}. The default implementation sets - * up the TextView's internal state to take care of this; if overriding - * you should call through to the super class. + * of edit operations through a call to link {@link #beginBatchEdit()}. */ public void onBeginBatchEdit() { - if (mInputMethodState != null) { - // XXX we should be smarter here, such as not doing invalidates - // until all edits are done. - mInputMethodState.mBatchEditing = true; - } } /** * Called by the framework in response to a request to end a batch - * of edit operations from the current input method, as a result of - * it calling {@link InputConnection#endBatchEdit - * InputConnection.endBatchEdit()}. The default implementation sets - * up the TextView's internal state to take care of this; if overriding - * you should call through to the super class. + * of edit operations through a call to link {@link #endBatchEdit}. */ public void onEndBatchEdit() { - if (mInputMethodState != null) { - mInputMethodState.mBatchEditing = false; - // Cheezy way to get us to report the current cursor location. - invalidateCursor(); - } } /** @@ -4542,9 +4842,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Returns true if anything changed. + * Move the point, specified by the offset, into the view if it is needed. + * This has to be called after layout. Returns true if anything changed. */ - private boolean bringPointIntoView(int offset) { + public boolean bringPointIntoView(int offset) { boolean changed = false; int line = mLayout.getLineForOffset(offset); @@ -4794,7 +5095,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_singleLine */ public void setSingleLine(boolean singleLine) { - mSingleLine = singleLine; if ((mInputType&EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { if (singleLine) { @@ -4803,19 +5103,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; } } + applySingleLine(singleLine, true); + } + private void applySingleLine(boolean singleLine, boolean applyTransformation) { + mSingleLine = singleLine; if (singleLine) { setLines(1); setHorizontallyScrolling(true); - setTransformationMethod(SingleLineTransformationMethod. - getInstance()); + if (applyTransformation) { + setTransformationMethod(SingleLineTransformationMethod. + getInstance()); + } } else { setMaxLines(Integer.MAX_VALUE); setHorizontallyScrolling(false); - setTransformationMethod(null); + if (applyTransformation) { + setTransformationMethod(null); + } } } - + /** * Causes words in the text that are longer than the view is wide * to be ellipsized instead of broken in the middle. You may also @@ -4938,6 +5246,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener float mScroll; Marquee(TextView v) { + final float density = v.getContext().getResources().getDisplayMetrics().density; + mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / (float) MARQUEE_RESOLUTION; mView = new WeakReference(v); } @@ -5006,7 +5316,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (textView != null && textView.mLayout != null) { mStatus = MARQUEE_STARTING; mScroll = 0.0f; - mScrollUnit = MARQUEE_PIXELS_PER_SECOND / (float) MARQUEE_RESOLUTION; mMaxScroll = textView.mLayout.getLineWidth(0) - (textView.getWidth() - textView.getCompoundPaddingLeft() - textView.getCompoundPaddingRight()); textView.invalidate(); @@ -5045,6 +5354,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int start, int before, int after) { } + /** + * This method is called when the selection has changed, in case any + * subclasses would like to know. + * + * @param selStart The new selection start location. + * @param selEnd The new selection end location. + */ + protected void onSelectionChanged(int selStart, int selEnd) { + } + /** * Adds a TextWatcher to the list of those whose methods are called * whenever this TextView's text changes. @@ -5123,26 +5442,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ void handleTextChanged(CharSequence buffer, int start, int before, int after) { - invalidate(); - - int curs = Selection.getSelectionStart(buffer); - - if (curs >= 0 || (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == - Gravity.BOTTOM) { - registerForPreDraw(); + final InputMethodState ims = mInputMethodState; + if (ims == null || ims.mBatchEditNesting == 0) { + updateAfterEdit(); } - - if (curs >= 0) { - mHighlightPathBogus = true; - - if (isFocused()) { - mShowCursor = SystemClock.uptimeMillis(); - makeBlink(); + if (ims != null) { + ims.mContentChanged = true; + if (ims.mChangedStart < 0) { + ims.mChangedStart = start; + ims.mChangedEnd = start+before; + } else { + if (ims.mChangedStart > start) ims.mChangedStart = start; + if (ims.mChangedEnd < (start+before)) ims.mChangedEnd = start+before; } + ims.mChangedDelta += after-before; } - - checkForResize(); - + sendOnTextChanged(buffer, start, before, after); onTextChanged(buffer, start, before, after); } @@ -5151,19 +5466,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Not private so it can be called from an inner class without going * through a thunk. */ - void spanChange(Spanned buf, Object what, int o, int n) { + void spanChange(Spanned buf, Object what, int oldStart, int newStart, + int oldEnd, int newEnd) { // XXX Make the start and end move together if this ends up // spending too much time invalidating. + boolean selChanged = false; + int newSelStart=-1, newSelEnd=-1; + + final InputMethodState ims = mInputMethodState; + if (what == Selection.SELECTION_END) { mHighlightPathBogus = true; + selChanged = true; + newSelEnd = newStart; if (!isFocused()) { mSelectionMoved = true; } - if (o >= 0 || n >= 0) { - invalidateCursor(Selection.getSelectionStart(buf), o, n); + if (oldStart >= 0 || newStart >= 0) { + invalidateCursor(Selection.getSelectionStart(buf), oldStart, newStart); registerForPreDraw(); if (isFocused()) { @@ -5175,28 +5498,81 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (what == Selection.SELECTION_START) { mHighlightPathBogus = true; + selChanged = true; + newSelStart = newStart; if (!isFocused()) { mSelectionMoved = true; } - if (o >= 0 || n >= 0) { - invalidateCursor(Selection.getSelectionEnd(buf), o, n); + if (oldStart >= 0 || newStart >= 0) { + int end = Selection.getSelectionEnd(buf); + invalidateCursor(end, oldStart, newStart); } } + if (selChanged) { + if ((buf.getSpanFlags(what)&Spanned.SPAN_INTERMEDIATE) == 0) { + if (newSelStart < 0) { + newSelStart = Selection.getSelectionStart(buf); + } + if (newSelEnd < 0) { + newSelEnd = Selection.getSelectionEnd(buf); + } + onSelectionChanged(newSelStart, newSelEnd); + } + } + if (what instanceof UpdateAppearance || what instanceof ParagraphStyle) { - invalidate(); - mHighlightPathBogus = true; - checkForResize(); + if (ims == null || ims.mBatchEditNesting == 0) { + invalidate(); + mHighlightPathBogus = true; + checkForResize(); + } else { + ims.mContentChanged = true; + } } if (MetaKeyKeyListener.isMetaTracker(buf, what)) { mHighlightPathBogus = true; if (Selection.getSelectionStart(buf) >= 0) { - invalidateCursor(); + if (ims == null || ims.mBatchEditNesting == 0) { + invalidateCursor(); + } else { + ims.mCursorChanged = true; + } + } + } + + if (what instanceof ParcelableSpan) { + // If this is a span that can be sent to a remote process, + // the current extract editor would be interested in it. + if (ims != null && ims.mExtracting != null) { + if (ims.mBatchEditNesting != 0) { + if (oldStart >= 0) { + if (ims.mChangedStart > oldStart) { + ims.mChangedStart = oldStart; + } + if (ims.mChangedStart > oldEnd) { + ims.mChangedStart = oldEnd; + } + } + if (newStart >= 0) { + if (ims.mChangedStart > newStart) { + ims.mChangedStart = newStart; + } + if (ims.mChangedStart > newEnd) { + ims.mChangedStart = newEnd; + } + } + } else { + if (DEBUG_EXTRACT) Log.v(TAG, "Span change outside of batch: " + + oldStart + "-" + oldEnd + "," + + newStart + "-" + newEnd + what); + ims.mContentChanged = true; + } } } } @@ -5205,36 +5581,45 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener implements TextWatcher, SpanWatcher { public void beforeTextChanged(CharSequence buffer, int start, int before, int after) { + if (DEBUG_EXTRACT) Log.v(TAG, "beforeTextChanged start=" + start + + " before=" + before + " after=" + after + ": " + buffer); TextView.this.sendBeforeTextChanged(buffer, start, before, after); } public void onTextChanged(CharSequence buffer, int start, int before, int after) { + if (DEBUG_EXTRACT) Log.v(TAG, "onTextChanged start=" + start + + " before=" + before + " after=" + after + ": " + buffer); TextView.this.handleTextChanged(buffer, start, before, after); } public void afterTextChanged(Editable buffer) { + if (DEBUG_EXTRACT) Log.v(TAG, "afterTextChanged: " + buffer); TextView.this.sendAfterTextChanged(buffer); if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) { MetaKeyKeyListener.stopSelecting(TextView.this, buffer); } - - TextView.this.reportExtractedText(); } public void onSpanChanged(Spannable buf, Object what, int s, int e, int st, int en) { - TextView.this.spanChange(buf, what, s, st); + if (DEBUG_EXTRACT) Log.v(TAG, "onSpanChanged s=" + s + " e=" + e + + " st=" + st + " en=" + en + " what=" + what + ": " + buf); + TextView.this.spanChange(buf, what, s, st, e, en); } public void onSpanAdded(Spannable buf, Object what, int s, int e) { - TextView.this.spanChange(buf, what, -1, s); + if (DEBUG_EXTRACT) Log.v(TAG, "onSpanAdded s=" + s + " e=" + e + + " what=" + what + ": " + buf); + TextView.this.spanChange(buf, what, -1, s, -1, e); } public void onSpanRemoved(Spannable buf, Object what, int s, int e) { - TextView.this.spanChange(buf, what, s, -1); + if (DEBUG_EXTRACT) Log.v(TAG, "onSpanRemoved s=" + s + " e=" + e + + " what=" + what + ": " + buf); + TextView.this.spanChange(buf, what, s, -1, e, -1); } } @@ -5258,6 +5643,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { mShowCursor = SystemClock.uptimeMillis(); + ensureEndedBatchEdit(); + if (focused) { int selStart = getSelectionStart(); int selEnd = getSelectionEnd(); @@ -5362,22 +5749,28 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final boolean superResult = super.onTouchEvent(event); + final int action = event.getAction(); + /* * Don't handle the release after a long press, because it will * move the selection away from whatever the menu action was * trying to affect. */ - if (mEatTouchRelease && event.getAction() == MotionEvent.ACTION_UP) { + if (mEatTouchRelease && action == MotionEvent.ACTION_UP) { mEatTouchRelease = false; return superResult; } - if (mMovement != null && mText instanceof Spannable && - mLayout != null) { + if (mMovement != null && mText instanceof Spannable && mLayout != null) { + + if (action == MotionEvent.ACTION_DOWN) { + mScrolled = false; + } + boolean moved = mMovement.onTouchEvent(this, (Spannable) mText, event); if (mText instanceof Editable && onCheckIsTextEditor()) { - if (event.getAction() == MotionEvent.ACTION_UP && isFocused()) { + if (action == MotionEvent.ACTION_UP && isFocused() && !mScrolled) { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(this, 0); @@ -5392,6 +5785,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return superResult; } + @Override + public void cancelLongPress() { + super.cancelLongPress(); + mScrolled = true; + } + @Override public boolean onTrackballEvent(MotionEvent event) { if (mMovement != null && mText instanceof Spannable && @@ -5568,28 +5967,28 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (keyCode) { case KeyEvent.KEYCODE_A: if (canSelectAll()) { - return onMenu(ID_SELECT_ALL); + return onTextContextMenuItem(ID_SELECT_ALL); } break; case KeyEvent.KEYCODE_X: if (canCut()) { - return onMenu(ID_CUT); + return onTextContextMenuItem(ID_CUT); } break; case KeyEvent.KEYCODE_C: if (canCopy()) { - return onMenu(ID_COPY); + return onTextContextMenuItem(ID_COPY); } break; case KeyEvent.KEYCODE_V: if (canPaste()) { - return onMenu(ID_PASTE); + return onTextContextMenuItem(ID_PASTE); } break; @@ -5655,6 +6054,38 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } + /** + * Returns a word to add to the dictionary from the context menu, + * or null if there is no cursor or no word at the cursor. + */ + private String getWordForDictionary() { + int end = getSelectionEnd(); + + if (end < 0) { + return null; + } + + int start = end; + char c; + int len = mText.length(); + + while (start > 0 && (((c = mTransformed.charAt(start - 1)) == '\'') || + (Character.isLetterOrDigit(c)))) { + start--; + } + + while (end < len && (((c = mTransformed.charAt(end)) == '\'') || + (Character.isLetterOrDigit(c)))) { + end++; + } + + if (start == end) { + return null; + } + + return TextUtils.substring(mTransformed, start, end); + } + @Override protected void onCreateContextMenu(ContextMenu menu) { super.onCreateContextMenu(menu); @@ -5696,7 +6127,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setOnMenuItemClickListener(handler); added = true; } else { - menu.add(0, ID_SELECT_TEXT, 0, + menu.add(0, ID_START_SELECTING_TEXT, 0, com.android.internal.R.string.selectText). setOnMenuItemClickListener(handler); added = true; @@ -5755,34 +6186,60 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && imm.isActive(this)) { - menu.add(1, ID_SWITCH_IME, 0, com.android.internal.R.string.inputMethod). + if (isInputMethodTarget()) { + menu.add(1, ID_SWITCH_INPUT_METHOD, 0, com.android.internal.R.string.inputMethod). setOnMenuItemClickListener(handler); added = true; } + String word = getWordForDictionary(); + if (word != null) { + menu.add(1, ID_ADD_TO_DICTIONARY, 0, + getContext().getString(com.android.internal.R.string.addToDictionary, word)). + setOnMenuItemClickListener(handler); + added = true; + + } + if (added) { menu.setHeaderTitle(com.android.internal.R.string.editTextMenuTitle); } } - private static final int ID_SELECT_ALL = com.android.internal.R.id.selectAll; - private static final int ID_SELECT_TEXT = com.android.internal.R.id.selectText; - private static final int ID_STOP_SELECTING_TEXT = com.android.internal.R.id.stopSelectingText; - private static final int ID_CUT = com.android.internal.R.id.cut; - private static final int ID_COPY = com.android.internal.R.id.copy; - private static final int ID_PASTE = com.android.internal.R.id.paste; - private static final int ID_COPY_URL = com.android.internal.R.id.copyUrl; - private static final int ID_SWITCH_IME = com.android.internal.R.id.inputMethod; + /** + * Returns whether this text view is a current input method target. The + * default implementation just checks with {@link InputMethodManager}. + */ + public boolean isInputMethodTarget() { + InputMethodManager imm = InputMethodManager.peekInstance(); + return imm != null && imm.isActive(this); + } + + private static final int ID_SELECT_ALL = android.R.id.selectAll; + private static final int ID_START_SELECTING_TEXT = android.R.id.startSelectingText; + private static final int ID_STOP_SELECTING_TEXT = android.R.id.stopSelectingText; + private static final int ID_CUT = android.R.id.cut; + private static final int ID_COPY = android.R.id.copy; + private static final int ID_PASTE = android.R.id.paste; + private static final int ID_COPY_URL = android.R.id.copyUrl; + private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod; + private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary; private class MenuHandler implements MenuItem.OnMenuItemClickListener { public boolean onMenuItemClick(MenuItem item) { - return onMenu(item.getItemId()); + return onTextContextMenuItem(item.getItemId()); } } - private boolean onMenu(int id) { + /** + * Called when a context menu option for the text view is selected. Currently + * this will be one of: {@link android.R.id#selectAll}, + * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText}, + * {@link android.R.id#cut}, {@link android.R.id#copy}, + * {@link android.R.id#paste}, {@link android.R.id#copyUrl}, + * or {@link android.R.id#switchInputMethod}. + */ + public boolean onTextContextMenuItem(int id) { int selStart = getSelectionStart(); int selEnd = getSelectionEnd(); @@ -5807,10 +6264,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (id) { case ID_SELECT_ALL: Selection.setSelection((Spannable) mText, 0, - mText.length()); + mText.length()); return true; - case ID_SELECT_TEXT: + case ID_START_SELECTING_TEXT: MetaKeyKeyListener.startSelecting(this, (Spannable) mText); return true; @@ -5865,12 +6322,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; - case ID_SWITCH_IME: + case ID_SWITCH_INPUT_METHOD: InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { imm.showInputMethodPicker(); } return true; + + case ID_ADD_TO_DICTIONARY: + String word = getWordForDictionary(); + + if (word != null) { + Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT"); + i.putExtra("word", word); + getContext().startActivity(i); + } + + return true; } return false; @@ -5885,8 +6353,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - private boolean mEatTouchRelease = false; - @ViewDebug.ExportedProperty private CharSequence mText; private CharSequence mTransformed; diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java index acc9c4607ef22..8c652e5ead345 100644 --- a/core/java/android/widget/ViewAnimator.java +++ b/core/java/android/widget/ViewAnimator.java @@ -144,6 +144,56 @@ public class ViewAnimator extends FrameLayout { } } + @Override + public void removeAllViews() { + super.removeAllViews(); + mWhichChild = 0; + mFirstTime = true; + } + + @Override + public void removeView(View view) { + final int index = indexOfChild(view); + if (index >= 0) { + removeViewAt(index); + } + } + + @Override + public void removeViewAt(int index) { + super.removeViewAt(index); + final int childCount = getChildCount(); + if (childCount == 0) { + mWhichChild = 0; + mFirstTime = true; + } else if (mWhichChild >= childCount) { + // Displayed is above child count, so float down to top of stack + setDisplayedChild(childCount - 1); + } else if (mWhichChild == index) { + // Displayed was removed, so show the new child living in its place + setDisplayedChild(mWhichChild); + } + } + + public void removeViewInLayout(View view) { + removeView(view); + } + + public void removeViews(int start, int count) { + super.removeViews(start, count); + if (getChildCount() == 0) { + mWhichChild = 0; + mFirstTime = true; + } else if (mWhichChild >= start && mWhichChild < start + count) { + // Try showing new displayed child, wrapping if needed + setDisplayedChild(mWhichChild); + } + } + + public void removeViewsInLayout(int start, int count) { + removeViews(start, count); + } + /** * Returns the View corresponding to the currently displayed child. * diff --git a/core/java/android/widget/ZoomButton.java b/core/java/android/widget/ZoomButton.java index 5df8c8adf58a6..df3f307a3cbcf 100644 --- a/core/java/android/widget/ZoomButton.java +++ b/core/java/android/widget/ZoomButton.java @@ -54,7 +54,7 @@ public class ZoomButton extends ImageButton implements OnLongClickListener { public ZoomButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mHandler = new Handler(); - mGestureDetector = new GestureDetector(new SimpleOnGestureListener() { + mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() { @Override public void onLongPress(MotionEvent e) { onLongClick(ZoomButton.this); diff --git a/core/java/android/widget/ZoomControls.java b/core/java/android/widget/ZoomControls.java index 1fd662c70e551..fdc05b6e4b20f 100644 --- a/core/java/android/widget/ZoomControls.java +++ b/core/java/android/widget/ZoomControls.java @@ -81,7 +81,9 @@ public class ZoomControls extends LinearLayout { } public void show() { - fade(View.VISIBLE, 0.0f, 1.0f); + if (ZoomRingController.useOldZoom(mContext)) { + fade(View.VISIBLE, 0.0f, 1.0f); + } } public void hide() { diff --git a/core/java/android/widget/ZoomRing.java b/core/java/android/widget/ZoomRing.java new file mode 100644 index 0000000000000..20d605617075f --- /dev/null +++ b/core/java/android/widget/ZoomRing.java @@ -0,0 +1,423 @@ +package android.widget; + +import com.android.internal.R; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + +/** + * @hide + */ +public class ZoomRing extends View { + + // TODO: move to ViewConfiguration? + private static final int DOUBLE_TAP_DISMISS_TIMEOUT = ViewConfiguration.getJumpTapTimeout(); + // TODO: get from theme + private static final int DISABLED_ALPHA = 160; + + private static final String TAG = "ZoomRing"; + + // TODO: xml + private static final int THUMB_DISTANCE = 63; + + /** To avoid floating point calculations, we multiply radians by this value. */ + public static final int RADIAN_INT_MULTIPLIER = 100000000; + /** PI using our multiplier. */ + public static final int PI_INT_MULTIPLIED = (int) (Math.PI * RADIAN_INT_MULTIPLIER); + /** PI/2 using our multiplier. */ + private static final int HALF_PI_INT_MULTIPLIED = PI_INT_MULTIPLIED / 2; + + private static final int THUMB_GRAB_SLOP = PI_INT_MULTIPLIED / 4; + + /** The cached X of our center. */ + private int mCenterX; + /** The cached Y of our center. */ + private int mCenterY; + + /** The angle of the thumb (in int radians) */ + private int mThumbAngle; + private boolean mIsThumbAngleValid; + private int mThumbCenterX; + private int mThumbCenterY; + private int mThumbHalfWidth; + private int mThumbHalfHeight; + + private int mCallbackThreshold = Integer.MAX_VALUE; + + /** The accumulated amount of drag for the thumb (in int radians). */ + private int mAcculumalatedThumbDrag = 0; + + /** The inner radius of the track. */ + private int mBoundInnerRadiusSquared = 0; + /** The outer radius of the track. */ + private int mBoundOuterRadiusSquared = Integer.MAX_VALUE; + + private int mPreviousWidgetDragX; + private int mPreviousWidgetDragY; + + private boolean mDrawThumb = true; + private Drawable mThumbDrawable; + + private static final int MODE_IDLE = 0; + private static final int MODE_DRAG_THUMB = 1; + private static final int MODE_MOVE_ZOOM_RING = 2; + private static final int MODE_TAP_DRAG = 3; + private int mMode; + + private long mPreviousTapTime; + + private Handler mHandler = new Handler(); + + private Disabler mDisabler = new Disabler(); + + private OnZoomRingCallback mCallback; + + private boolean mResetThumbAutomatically = true; + private int mThumbDragStartAngle; + + public ZoomRing(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + // TODO get drawable from style instead + Resources res = context.getResources(); + mThumbDrawable = res.getDrawable(R.drawable.zoom_ring_thumb); + + // TODO: add padding to drawable + setBackgroundResource(R.drawable.zoom_ring_track); + // TODO get from style + setBounds(30, Integer.MAX_VALUE); + + mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2; + mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2; + + mCallbackThreshold = PI_INT_MULTIPLIED / 6; + } + + public ZoomRing(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ZoomRing(Context context) { + this(context, null); + } + + public void setCallback(OnZoomRingCallback callback) { + mCallback = callback; + } + + // TODO: rename + public void setCallbackThreshold(int callbackThreshold) { + mCallbackThreshold = callbackThreshold; + } + + // TODO: from XML too + public void setBounds(int innerRadius, int outerRadius) { + mBoundInnerRadiusSquared = innerRadius * innerRadius; + if (mBoundInnerRadiusSquared < innerRadius) { + // Prevent overflow + mBoundInnerRadiusSquared = Integer.MAX_VALUE; + } + + mBoundOuterRadiusSquared = outerRadius * outerRadius; + if (mBoundOuterRadiusSquared < outerRadius) { + // Prevent overflow + mBoundOuterRadiusSquared = Integer.MAX_VALUE; + } + } + + public void setThumbAngle(int angle) { + mThumbAngle = angle; + mThumbCenterX = (int) (Math.cos(1f * angle / RADIAN_INT_MULTIPLIER) * THUMB_DISTANCE) + + mCenterX; + mThumbCenterY = (int) (Math.sin(1f * angle / RADIAN_INT_MULTIPLIER) * THUMB_DISTANCE) + * -1 + mCenterY; + invalidate(); + } + + public void resetThumbAngle() { + if (mResetThumbAutomatically) { + setThumbAngle(HALF_PI_INT_MULTIPLIED); + } + } + + public void setResetThumbAutomatically(boolean resetThumbAutomatically) { + mResetThumbAutomatically = resetThumbAutomatically; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec), + resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec)); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, + int bottom) { + super.onLayout(changed, left, top, right, bottom); + + // Cache the center point + mCenterX = (right - left) / 2; + mCenterY = (bottom - top) / 2; + + // Done here since we now have center, which is needed to calculate some + // aux info for thumb angle + if (mThumbAngle == Integer.MIN_VALUE) { + resetThumbAngle(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return handleTouch(event.getAction(), event.getEventTime(), + (int) event.getX(), (int) event.getY(), (int) event.getRawX(), + (int) event.getRawY()); + } + + private void resetState() { + mMode = MODE_IDLE; + mPreviousWidgetDragX = mPreviousWidgetDragY = Integer.MIN_VALUE; + mAcculumalatedThumbDrag = 0; + mIsThumbAngleValid = false; + } + + public void setTapDragMode(boolean tapDragMode, int x, int y) { + resetState(); + mMode = tapDragMode ? MODE_TAP_DRAG : MODE_IDLE; + mIsThumbAngleValid = false; + + if (tapDragMode && mCallback != null) { + onThumbDragStarted(getAngle(x - mCenterX, y - mCenterY)); + } + } + + public boolean handleTouch(int action, long time, int x, int y, int rawX, int rawY) { + switch (action) { + + case MotionEvent.ACTION_DOWN: + if (mPreviousTapTime + DOUBLE_TAP_DISMISS_TIMEOUT >= time) { + if (mCallback != null) { + mCallback.onZoomRingDismissed(); + } + } else { + mPreviousTapTime = time; + } + resetState(); + return true; + + case MotionEvent.ACTION_MOVE: + // Fall through to code below switch + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (mCallback != null) { + if (mMode == MODE_MOVE_ZOOM_RING) { + mCallback.onZoomRingMovingStopped(); + } else if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) { + onThumbDragStopped(getAngle(x - mCenterX, y - mCenterY)); + } + } + mDisabler.setEnabling(true); + return true; + + default: + return false; + } + + // local{X,Y} will be where the center of the widget is (0,0) + int localX = x - mCenterX; + int localY = y - mCenterY; + boolean isTouchingThumb = true; + boolean isInBounds = true; + int touchAngle = getAngle(localX, localY); + + int radiusSquared = localX * localX + localY * localY; + if (radiusSquared < mBoundInnerRadiusSquared || + radiusSquared > mBoundOuterRadiusSquared) { + // Out-of-bounds + isTouchingThumb = false; + isInBounds = false; + } + + int deltaThumbAndTouch = getDelta(touchAngle, mThumbAngle); + int absoluteDeltaThumbAndTouch = deltaThumbAndTouch >= 0 ? + deltaThumbAndTouch : -deltaThumbAndTouch; + if (isTouchingThumb && + absoluteDeltaThumbAndTouch > THUMB_GRAB_SLOP) { + // Didn't grab close enough to the thumb + isTouchingThumb = false; + } + + if (mMode == MODE_IDLE) { + mMode = isTouchingThumb ? MODE_DRAG_THUMB : MODE_MOVE_ZOOM_RING; + + if (mCallback != null) { + if (mMode == MODE_DRAG_THUMB) { + onThumbDragStarted(touchAngle); + } else if (mMode == MODE_MOVE_ZOOM_RING) { + mCallback.onZoomRingMovingStarted(); + } + } + } + + if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) { + if (isInBounds) { + onThumbDragged(touchAngle, mIsThumbAngleValid ? deltaThumbAndTouch : 0); + } else { + mIsThumbAngleValid = false; + } + } else if (mMode == MODE_MOVE_ZOOM_RING) { + onZoomRingMoved(rawX, rawY); + } + + return true; + } + + private int getDelta(int angle1, int angle2) { + int delta = angle1 - angle2; + + // Assume this is a result of crossing over the discontinuous 0 -> 2pi + if (delta > PI_INT_MULTIPLIED || delta < -PI_INT_MULTIPLIED) { + // Bring both the radians and previous angle onto a continuous range + if (angle1 < HALF_PI_INT_MULTIPLIED) { + // Same as deltaRadians = (radians + 2PI) - previousAngle + delta += PI_INT_MULTIPLIED * 2; + } else if (angle2 < HALF_PI_INT_MULTIPLIED) { + // Same as deltaRadians = radians - (previousAngle + 2PI) + delta -= PI_INT_MULTIPLIED * 2; + } + } + + return delta; + } + + private void onThumbDragStarted(int startAngle) { + mThumbDragStartAngle = startAngle; + mCallback.onZoomRingThumbDraggingStarted(startAngle); + } + + private void onThumbDragged(int touchAngle, int deltaAngle) { + mAcculumalatedThumbDrag += deltaAngle; + if (mAcculumalatedThumbDrag > mCallbackThreshold + || mAcculumalatedThumbDrag < -mCallbackThreshold) { + if (mCallback != null) { + boolean canStillZoom = mCallback.onZoomRingThumbDragged( + mAcculumalatedThumbDrag / mCallbackThreshold, + mAcculumalatedThumbDrag, mThumbDragStartAngle, touchAngle); + mDisabler.setEnabling(canStillZoom); + } + mAcculumalatedThumbDrag = 0; + } + + setThumbAngle(touchAngle); + mIsThumbAngleValid = true; + } + + private void onThumbDragStopped(int stopAngle) { + mCallback.onZoomRingThumbDraggingStopped(stopAngle); + } + + private void onZoomRingMoved(int x, int y) { + if (mPreviousWidgetDragX != Integer.MIN_VALUE) { + int deltaX = x - mPreviousWidgetDragX; + int deltaY = y - mPreviousWidgetDragY; + + if (mCallback != null) { + mCallback.onZoomRingMoved(deltaX, deltaY); + } + } + + mPreviousWidgetDragX = x; + mPreviousWidgetDragY = y; + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + + if (!hasWindowFocus && mCallback != null) { + mCallback.onZoomRingDismissed(); + } + } + + private int getAngle(int localX, int localY) { + int radians = (int) (Math.atan2(localY, localX) * RADIAN_INT_MULTIPLIER); + + // Convert from [-pi,pi] to {0,2pi] + if (radians < 0) { + return -radians; + } else if (radians > 0) { + return 2 * PI_INT_MULTIPLIED - radians; + } else { + return 0; + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mDrawThumb) { + mThumbDrawable.setBounds(mThumbCenterX - mThumbHalfWidth, mThumbCenterY + - mThumbHalfHeight, mThumbCenterX + mThumbHalfWidth, mThumbCenterY + + mThumbHalfHeight); + mThumbDrawable.draw(canvas); + } + } + + private class Disabler implements Runnable { + private static final int DELAY = 15; + private static final float ENABLE_RATE = 1.05f; + private static final float DISABLE_RATE = 0.95f; + + private int mAlpha = 255; + private boolean mEnabling; + + public int getAlpha() { + return mAlpha; + } + + public void setEnabling(boolean enabling) { + if ((enabling && mAlpha != 255) || (!enabling && mAlpha != DISABLED_ALPHA)) { + mEnabling = enabling; + post(this); + } + } + + public void run() { + mAlpha *= mEnabling ? ENABLE_RATE : DISABLE_RATE; + if (mAlpha < DISABLED_ALPHA) { + mAlpha = DISABLED_ALPHA; + } else if (mAlpha > 255) { + mAlpha = 255; + } else { + // Still more to go + postDelayed(this, DELAY); + } + + getBackground().setAlpha(mAlpha); + invalidate(); + } + } + + public interface OnZoomRingCallback { + void onZoomRingMovingStarted(); + boolean onZoomRingMoved(int deltaX, int deltaY); + void onZoomRingMovingStopped(); + + void onZoomRingThumbDraggingStarted(int startAngle); + boolean onZoomRingThumbDragged(int numLevels, int dragAmount, int startAngle, int curAngle); + void onZoomRingThumbDraggingStopped(int endAngle); + + void onZoomRingDismissed(); + } + +} diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java new file mode 100644 index 0000000000000..2ca03741b7b5a --- /dev/null +++ b/core/java/android/widget/ZoomRingController.java @@ -0,0 +1,819 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.provider.Settings; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; + +// TODO: make sure no px values exist, only dip (scale if necessary from Viewconfiguration) + +/** + * TODO: Docs + * @hide + */ +public class ZoomRingController implements ZoomRing.OnZoomRingCallback, + View.OnTouchListener, View.OnKeyListener { + + private static final int SHOW_TUTORIAL_TOAST_DELAY = 1000; + + private static final int ZOOM_RING_RECENTERING_DURATION = 500; + + private static final String TAG = "ZoomRing"; + + public static final boolean USE_OLD_ZOOM = false; + public static boolean useOldZoom(Context context) { + return Settings.System.getInt(context.getContentResolver(), "zoom", 1) == 0; + } + + private static final int ZOOM_CONTROLS_TIMEOUT = + (int) ViewConfiguration.getZoomControlsTimeout(); + + // TODO: move these to ViewConfiguration or re-use existing ones + // TODO: scale px values based on latest from ViewConfiguration + private static final int SECOND_TAP_TIMEOUT = 500; + private static final int ZOOM_RING_DISMISS_DELAY = SECOND_TAP_TIMEOUT / 2; + private static final int SECOND_TAP_SLOP = 70; + private static final int SECOND_TAP_MOVE_SLOP = 15; + private static final int MAX_PAN_GAP = 30; + + private static final String SETTING_NAME_SHOWN_TOAST = "shown_zoom_ring_toast"; + + private Context mContext; + private WindowManager mWindowManager; + + /** + * The view that is being zoomed by this zoom ring. + */ + private View mOwnerView; + + /** + * The bounds of the owner view in global coordinates. This is recalculated + * each time the zoom ring is shown. + */ + private Rect mOwnerViewBounds = new Rect(); + + /** + * The container that is added as a window. + */ + private FrameLayout mContainer; + private LayoutParams mContainerLayoutParams; + + /* + * Tap-drag is an interaction where the user first taps and then (quickly) + * does the clockwise or counter-clockwise drag. In reality, this is: (down, + * up, down, move in circles, up). This differs from the usual events of: + * (down, up, down, up, down, move in circles, up). While the only + * difference is the omission of an (up, down), for power-users this is a + * pretty big improvement as it now only requires them to focus on the + * screen once (for the first tap down) instead of twice (for the first tap + * down and then to grab the thumb). + */ + private int mTapDragStartX; + private int mTapDragStartY; + + private static final int TOUCH_MODE_IDLE = 0; + private static final int TOUCH_MODE_WAITING_FOR_SECOND_TAP = 1; + private static final int TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT = 2; + private static final int TOUCH_MODE_FORWARDING_FOR_TAP_DRAG = 3; + private int mTouchMode; + + private boolean mIsZoomRingVisible; + + private ZoomRing mZoomRing; + private int mZoomRingWidth; + private int mZoomRingHeight; + + /** Invokes panning of owner view if the zoom ring is touching an edge. */ + private Panner mPanner = new Panner(); + + private ImageView mPanningArrows; + private Animation mPanningArrowsEnterAnimation; + private Animation mPanningArrowsExitAnimation; + + private Rect mTempRect = new Rect(); + + private OnZoomListener mCallback; + + private ViewConfiguration mViewConfig; + + /** + * When the zoom ring is centered on screen, this will be the x value used + * for the container's layout params. + */ + private int mCenteredContainerX = Integer.MIN_VALUE; + + /** + * When the zoom ring is centered on screen, this will be the y value used + * for the container's layout params. + */ + private int mCenteredContainerY = Integer.MIN_VALUE; + + /** + * Scroller used to re-center the zoom ring if the user had dragged it to a + * corner and then double-taps any point on the owner view (the owner view + * will center the double-tapped point, but we should re-center the zoom + * ring). + *

    + * The (x,y) of the scroller is the (x,y) of the container's layout params. + */ + private Scroller mScroller; + + /** + * When showing the zoom ring, we add the view as a new window. However, + * there is logic that needs to know the size of the zoom ring which is + * determined after it's laid out. Therefore, we must post this logic onto + * the UI thread so it will be exceuted AFTER the layout. This is the logic. + */ + private Runnable mPostedVisibleInitializer; + + // TODO: need a better way to persist this value, becuase right now this + // requires the WRITE_SETTINGS perimssion which the app may not have +// private Runnable mShowTutorialToast = new Runnable() { +// public void run() { +// if (Settings.System.getInt(mContext.getContentResolver(), +// SETTING_NAME_SHOWN_TOAST, 0) == 1) { +// return; +// } +// try { +// Settings.System.putInt(mContext.getContentResolver(), SETTING_NAME_SHOWN_TOAST, 1); +// } catch (SecurityException e) { +// // The app does not have permission to clear this flag, oh well! +// } +// +// Toast.makeText(mContext, +// com.android.internal.R.string.tutorial_double_tap_to_zoom_message, +// Toast.LENGTH_LONG).show(); +// } +// }; + + private IntentFilter mConfigurationChangedFilter = + new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED); + + private BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!mIsZoomRingVisible) return; + + mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED); + mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED); + } + }; + + /** Keeps the scroller going (or starts it). */ + private static final int MSG_SCROLLER_TICK = 1; + /** When configuration changes, this is called after the UI thread is idle. */ + private static final int MSG_POST_CONFIGURATION_CHANGED = 2; + /** Used to delay the zoom ring dismissal. */ + private static final int MSG_DISMISS_ZOOM_RING = 3; + + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SCROLLER_TICK: + onScrollerTick(); + break; + + case MSG_POST_CONFIGURATION_CHANGED: + onPostConfigurationChanged(); + break; + + case MSG_DISMISS_ZOOM_RING: + setVisible(false); + break; + } + + } + }; + + public ZoomRingController(Context context, View ownerView) { + mContext = context; + mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + + mOwnerView = ownerView; + + mZoomRing = new ZoomRing(context); + mZoomRing.setLayoutParams(new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); + mZoomRing.setCallback(this); + + createPanningArrows(); + + mContainer = new FrameLayout(context); + mContainer.setMeasureAllChildren(true); + mContainer.setOnTouchListener(this); + + mContainer.addView(mZoomRing); + mContainer.addView(mPanningArrows); + mContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + + mContainerLayoutParams = new LayoutParams(); + mContainerLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; + mContainerLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | + LayoutParams.FLAG_NOT_FOCUSABLE | + LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_LAYOUT_NO_LIMITS; + mContainerLayoutParams.height = LayoutParams.WRAP_CONTENT; + mContainerLayoutParams.width = LayoutParams.WRAP_CONTENT; + mContainerLayoutParams.type = LayoutParams.TYPE_APPLICATION_PANEL; + mContainerLayoutParams.format = PixelFormat.TRANSLUCENT; + // TODO: make a new animation for this + mContainerLayoutParams.windowAnimations = com.android.internal.R.style.Animation_Dialog; + + mScroller = new Scroller(context, new DecelerateInterpolator()); + + mViewConfig = ViewConfiguration.get(context); + +// mHandler.postDelayed(mShowTutorialToast, SHOW_TUTORIAL_TOAST_DELAY); + } + + private void createPanningArrows() { + // TODO: style + mPanningArrows = new ImageView(mContext); + mPanningArrows.setImageResource(com.android.internal.R.drawable.zoom_ring_arrows); + mPanningArrows.setLayoutParams(new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, + Gravity.CENTER)); + mPanningArrows.setVisibility(View.GONE); + + mPanningArrowsEnterAnimation = AnimationUtils.loadAnimation(mContext, + com.android.internal.R.anim.fade_in); + mPanningArrowsExitAnimation = AnimationUtils.loadAnimation(mContext, + com.android.internal.R.anim.fade_out); + } + + /** + * Sets the angle (in radians) a user must travel in order for the client to + * get a callback. Once there is a callback, the accumulator resets. For + * example, if you set this to PI/6, it will give a callback every time the + * user moves PI/6 amount on the ring. + * + * @param callbackThreshold The angle for the callback threshold, in radians + */ + public void setZoomCallbackThreshold(float callbackThreshold) { + mZoomRing.setCallbackThreshold((int) (callbackThreshold * ZoomRing.RADIAN_INT_MULTIPLIER)); + } + + public void setCallback(OnZoomListener callback) { + mCallback = callback; + } + + public void setThumbAngle(float angle) { + mZoomRing.setThumbAngle((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER)); + } + + public void setResetThumbAutomatically(boolean resetThumbAutomatically) { + mZoomRing.setResetThumbAutomatically(resetThumbAutomatically); + } + + public boolean isVisible() { + return mIsZoomRingVisible; + } + + public void setVisible(boolean visible) { + + if (useOldZoom(mContext)) return; + + if (visible) { + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + } else { + mPanner.stop(); + } + + if (mIsZoomRingVisible == visible) { + return; + } + + if (visible) { + if (mContainerLayoutParams.token == null) { + mContainerLayoutParams.token = mOwnerView.getWindowToken(); + } + + mWindowManager.addView(mContainer, mContainerLayoutParams); + + if (mPostedVisibleInitializer == null) { + mPostedVisibleInitializer = new Runnable() { + public void run() { + refreshPositioningVariables(); + resetZoomRing(); + + // TODO: remove this 'update' and just center zoom ring before the + // 'add', but need to make sure we have the width and height (which + // probably can only be retrieved after it's measured, which happens + // after it's added). + mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); + } + }; + } + + mHandler.post(mPostedVisibleInitializer); + + // Handle configuration changes when visible + mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter); + + // Steal key events from the owner + mOwnerView.setOnKeyListener(this); + + } else { + // Don't want to steal any more keys + mOwnerView.setOnKeyListener(null); + + // No longer care about configuration changes + mContext.unregisterReceiver(mConfigurationChangedReceiver); + + mWindowManager.removeView(mContainer); + } + + mIsZoomRingVisible = visible; + + if (mCallback != null) { + mCallback.onVisibilityChanged(visible); + } + } + + private void dismissZoomRingDelayed(int delay) { + mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); + mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_RING, delay); + } + + private void resetZoomRing() { + mScroller.abortAnimation(); + + mContainerLayoutParams.x = mCenteredContainerX; + mContainerLayoutParams.y = mCenteredContainerY; + + // Reset the thumb + mZoomRing.resetThumbAngle(); + } + + /** + * Should be called by the client for each event belonging to the second tap + * (the down, move, up, and cancel events). + * + * @param event The event belonging to the second tap. + * @return Whether the event was consumed. + */ + public boolean handleDoubleTapEvent(MotionEvent event) { + int action = event.getAction(); + + if (action == MotionEvent.ACTION_DOWN) { + mTouchMode = TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT; + int x = (int) event.getX(); + int y = (int) event.getY(); + + refreshPositioningVariables(); + setVisible(true); + centerPoint(x, y); + ensureZoomRingIsCentered(); + + // Tap drag mode stuff + mTapDragStartX = x; + mTapDragStartY = y; + + } else if (action == MotionEvent.ACTION_CANCEL) { + mTouchMode = TOUCH_MODE_IDLE; + + } else { // action is move or up + switch (mTouchMode) { + case TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT: { + switch (action) { + case MotionEvent.ACTION_MOVE: + int x = (int) event.getX(); + int y = (int) event.getY(); + if (Math.abs(x - mTapDragStartX) > mViewConfig.getScaledTouchSlop() || + Math.abs(y - mTapDragStartY) > + mViewConfig.getScaledTouchSlop()) { + mZoomRing.setTapDragMode(true, x, y); + mTouchMode = TOUCH_MODE_FORWARDING_FOR_TAP_DRAG; + } + return true; + + case MotionEvent.ACTION_UP: + mTouchMode = TOUCH_MODE_IDLE; + break; + } + break; + } + + case TOUCH_MODE_FORWARDING_FOR_TAP_DRAG: { + switch (action) { + case MotionEvent.ACTION_MOVE: + giveTouchToZoomRing(event); + return true; + + case MotionEvent.ACTION_UP: + mTouchMode = TOUCH_MODE_IDLE; + mZoomRing.setTapDragMode(false, (int) event.getX(), (int) event.getY()); + break; + } + break; + } + } + } + + return true; + } + + private void ensureZoomRingIsCentered() { + LayoutParams lp = mContainerLayoutParams; + + if (lp.x != mCenteredContainerX || lp.y != mCenteredContainerY) { + int width = mContainer.getWidth(); + int height = mContainer.getHeight(); + mScroller.startScroll(lp.x, lp.y, mCenteredContainerX - lp.x, + mCenteredContainerY - lp.y, ZOOM_RING_RECENTERING_DURATION); + mHandler.sendEmptyMessage(MSG_SCROLLER_TICK); + } + } + + private void refreshPositioningVariables() { + mZoomRingWidth = mZoomRing.getWidth(); + mZoomRingHeight = mZoomRing.getHeight(); + + // Calculate the owner view's bounds + mOwnerView.getGlobalVisibleRect(mOwnerViewBounds); + + // Get the center + Gravity.apply(Gravity.CENTER, mContainer.getWidth(), mContainer.getHeight(), + mOwnerViewBounds, mTempRect); + mCenteredContainerX = mTempRect.left; + mCenteredContainerY = mTempRect.top; + + } + + // MOVE ALL THIS TO GESTURE DETECTOR +// public boolean onTouch(View v, MotionEvent event) { +// int action = event.getAction(); +// +// if (mListenForInvocation) { +// switch (mTouchMode) { +// case TOUCH_MODE_IDLE: { +// if (action == MotionEvent.ACTION_DOWN) { +// setFirstTap(event); +// } +// break; +// } +// +// case TOUCH_MODE_WAITING_FOR_SECOND_TAP: { +// switch (action) { +// case MotionEvent.ACTION_DOWN: +// if (isSecondTapWithinSlop(event)) { +// handleDoubleTapEvent(event); +// } else { +// setFirstTap(event); +// } +// break; +// +// case MotionEvent.ACTION_MOVE: +// int deltaX = (int) event.getX() - mFirstTapX; +// if (deltaX < -SECOND_TAP_MOVE_SLOP || +// deltaX > SECOND_TAP_MOVE_SLOP) { +// mTouchMode = TOUCH_MODE_IDLE; +// } else { +// int deltaY = (int) event.getY() - mFirstTapY; +// if (deltaY < -SECOND_TAP_MOVE_SLOP || +// deltaY > SECOND_TAP_MOVE_SLOP) { +// mTouchMode = TOUCH_MODE_IDLE; +// } +// } +// break; +// } +// break; +// } +// +// case TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT: +// case TOUCH_MODE_FORWARDING_FOR_TAP_DRAG: { +// handleDoubleTapEvent(event); +// break; +// } +// } +// +// if (action == MotionEvent.ACTION_CANCEL) { +// mTouchMode = TOUCH_MODE_IDLE; +// } +// } +// +// return false; +// } +// +// private void setFirstTap(MotionEvent event) { +// mFirstTapTime = event.getEventTime(); +// mFirstTapX = (int) event.getX(); +// mFirstTapY = (int) event.getY(); +// mTouchMode = TOUCH_MODE_WAITING_FOR_SECOND_TAP; +// } +// +// private boolean isSecondTapWithinSlop(MotionEvent event) { +// return mFirstTapTime + SECOND_TAP_TIMEOUT > event.getEventTime() && +// Math.abs((int) event.getX() - mFirstTapX) < SECOND_TAP_SLOP && +// Math.abs((int) event.getY() - mFirstTapY) < SECOND_TAP_SLOP; +// } + + /** + * Centers the point (in owner view's coordinates). + */ + private void centerPoint(int x, int y) { + if (mCallback != null) { + mCallback.onCenter(x, y); + } + } + + private void giveTouchToZoomRing(MotionEvent event) { + int rawX = (int) event.getRawX(); + int rawY = (int) event.getRawY(); + int x = rawX - mContainerLayoutParams.x - mZoomRing.getLeft(); + int y = rawY - mContainerLayoutParams.y - mZoomRing.getTop(); + mZoomRing.handleTouch(event.getAction(), event.getEventTime(), x, y, rawX, rawY); + } + + public void onZoomRingMovingStarted() { + mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); + mScroller.abortAnimation(); + setPanningArrowsVisible(true); + } + + private void setPanningArrowsVisible(boolean visible) { + mPanningArrows.startAnimation(visible ? mPanningArrowsEnterAnimation + : mPanningArrowsExitAnimation); + mPanningArrows.setVisibility(visible ? View.VISIBLE : View.GONE); + } + + public boolean onZoomRingMoved(int deltaX, int deltaY) { + WindowManager.LayoutParams lp = mContainerLayoutParams; + Rect ownerBounds = mOwnerViewBounds; + + int zoomRingLeft = mZoomRing.getLeft(); + int zoomRingTop = mZoomRing.getTop(); + + int newX = lp.x + deltaX; + int newZoomRingX = newX + zoomRingLeft; + newZoomRingX = (newZoomRingX <= ownerBounds.left) ? ownerBounds.left : + (newZoomRingX + mZoomRingWidth > ownerBounds.right) ? + ownerBounds.right - mZoomRingWidth : newZoomRingX; + lp.x = newZoomRingX - zoomRingLeft; + + int newY = lp.y + deltaY; + int newZoomRingY = newY + zoomRingTop; + newZoomRingY = (newZoomRingY <= ownerBounds.top) ? ownerBounds.top : + (newZoomRingY + mZoomRingHeight > ownerBounds.bottom) ? + ownerBounds.bottom - mZoomRingHeight : newZoomRingY; + lp.y = newZoomRingY - zoomRingTop; + + mWindowManager.updateViewLayout(mContainer, lp); + + // Check for pan + int leftGap = newZoomRingX - ownerBounds.left; + if (leftGap < MAX_PAN_GAP) { + mPanner.setHorizontalStrength(-getStrengthFromGap(leftGap)); + } else { + int rightGap = ownerBounds.right - (lp.x + mZoomRingWidth + zoomRingLeft); + if (rightGap < MAX_PAN_GAP) { + mPanner.setHorizontalStrength(getStrengthFromGap(rightGap)); + } else { + mPanner.setHorizontalStrength(0); + } + } + + int topGap = newZoomRingY - ownerBounds.top; + if (topGap < MAX_PAN_GAP) { + mPanner.setVerticalStrength(-getStrengthFromGap(topGap)); + } else { + int bottomGap = ownerBounds.bottom - (lp.y + mZoomRingHeight + zoomRingTop); + if (bottomGap < MAX_PAN_GAP) { + mPanner.setVerticalStrength(getStrengthFromGap(bottomGap)); + } else { + mPanner.setVerticalStrength(0); + } + } + + return true; + } + + public void onZoomRingMovingStopped() { + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + mPanner.stop(); + setPanningArrowsVisible(false); + } + + private int getStrengthFromGap(int gap) { + return gap > MAX_PAN_GAP ? 0 : + (MAX_PAN_GAP - gap) * 100 / MAX_PAN_GAP; + } + + public void onZoomRingThumbDraggingStarted(int startAngle) { + mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); + if (mCallback != null) { + mCallback.onBeginDrag((float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER); + } + } + + public boolean onZoomRingThumbDragged(int numLevels, int dragAmount, int startAngle, + int curAngle) { + if (mCallback != null) { + int deltaZoomLevel = -numLevels; + int globalZoomCenterX = mContainerLayoutParams.x + mZoomRing.getLeft() + + mZoomRingWidth / 2; + int globalZoomCenterY = mContainerLayoutParams.y + mZoomRing.getTop() + + mZoomRingHeight / 2; + + return mCallback.onDragZoom(deltaZoomLevel, globalZoomCenterX - mOwnerViewBounds.left, + globalZoomCenterY - mOwnerViewBounds.top, + (float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER, + (float) curAngle / ZoomRing.RADIAN_INT_MULTIPLIER); + } + + return false; + } + + public void onZoomRingThumbDraggingStopped(int endAngle) { + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + if (mCallback != null) { + mCallback.onEndDrag((float) endAngle / ZoomRing.RADIAN_INT_MULTIPLIER); + } + } + + public void onZoomRingDismissed() { + dismissZoomRingDelayed(ZOOM_RING_DISMISS_DELAY); + } + + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { + // If the user touches outside of the zoom ring, dismiss the zoom ring + dismissZoomRingDelayed(ZOOM_RING_DISMISS_DELAY); + return true; + } + + return false; + } + + /** Steals key events from the owner view. */ + public boolean onKey(View v, int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_DPAD_RIGHT: + // Eat these + return true; + + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_DPAD_DOWN: + // Keep the zoom alive a little longer + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + + if (mCallback != null && event.getAction() == KeyEvent.ACTION_DOWN) { + mCallback.onSimpleZoom(keyCode == KeyEvent.KEYCODE_DPAD_UP); + } + + return true; + } + + return false; + } + + private void onScrollerTick() { + if (!mScroller.computeScrollOffset() || !mIsZoomRingVisible) return; + + mContainerLayoutParams.x = mScroller.getCurrX(); + mContainerLayoutParams.y = mScroller.getCurrY(); + mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); + + mHandler.sendEmptyMessage(MSG_SCROLLER_TICK); + } + + private void onPostConfigurationChanged() { + dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + refreshPositioningVariables(); + ensureZoomRingIsCentered(); + } + + private class Panner implements Runnable { + private static final int RUN_DELAY = 15; + private static final float STOP_SLOWDOWN = 0.8f; + + private final Handler mUiHandler = new Handler(); + + private int mVerticalStrength; + private int mHorizontalStrength; + + private boolean mStopping; + + /** The time this current pan started. */ + private long mStartTime; + + /** The time of the last callback to pan the map/browser/etc. */ + private long mPreviousCallbackTime; + + /** -100 (full left) to 0 (none) to 100 (full right) */ + public void setHorizontalStrength(int horizontalStrength) { + if (mHorizontalStrength == 0 && mVerticalStrength == 0 && horizontalStrength != 0) { + start(); + } else if (mVerticalStrength == 0 && horizontalStrength == 0) { + stop(); + } + + mHorizontalStrength = horizontalStrength; + mStopping = false; + } + + /** -100 (full up) to 0 (none) to 100 (full down) */ + public void setVerticalStrength(int verticalStrength) { + if (mHorizontalStrength == 0 && mVerticalStrength == 0 && verticalStrength != 0) { + start(); + } else if (mHorizontalStrength == 0 && verticalStrength == 0) { + stop(); + } + + mVerticalStrength = verticalStrength; + mStopping = false; + } + + private void start() { + mUiHandler.post(this); + mPreviousCallbackTime = 0; + mStartTime = 0; + } + + public void stop() { + mStopping = true; + } + + public void run() { + if (mStopping) { + mHorizontalStrength *= STOP_SLOWDOWN; + mVerticalStrength *= STOP_SLOWDOWN; + } + + if (mHorizontalStrength == 0 && mVerticalStrength == 0) { + return; + } + + boolean firstRun = mPreviousCallbackTime == 0; + long curTime = SystemClock.elapsedRealtime(); + int panAmount = getPanAmount(mStartTime, mPreviousCallbackTime, curTime); + mPreviousCallbackTime = curTime; + + if (firstRun) { + mStartTime = curTime; + } else { + int panX = panAmount * mHorizontalStrength / 100; + int panY = panAmount * mVerticalStrength / 100; + + if (mCallback != null) { + mCallback.onPan(panX, panY); + } + } + + mUiHandler.postDelayed(this, RUN_DELAY); + } + + // TODO make setter for this value so zoom clients can have different pan rates, if they want + private static final int PAN_VELOCITY_PX_S = 30; + private int getPanAmount(long startTime, long previousTime, long currentTime) { + return (int) ((currentTime - previousTime) * PAN_VELOCITY_PX_S / 100); + } + } + + public interface OnZoomListener { + void onBeginDrag(float startAngle); + boolean onDragZoom(int deltaZoomLevel, int centerX, int centerY, float startAngle, + float curAngle); + void onEndDrag(float endAngle); + void onSimpleZoom(boolean deltaZoomLevel); + boolean onPan(int deltaX, int deltaY); + void onCenter(int x, int y); + void onVisibilityChanged(boolean visible); + } +} diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java new file mode 100644 index 0000000000000..000f6c49ab499 --- /dev/null +++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.IMountService; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.Environment; +import android.widget.Toast; +import android.util.Log; + +/** + * This activity is shown to the user to confirm formatting of external media. + * It uses the alert dialog style. It will be launched from a notification, or from settings + */ +public class ExternalMediaFormatActivity extends AlertActivity implements DialogInterface.OnClickListener { + + private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1; + + /** Used to detect when the media state changes, in case we need to call finish() */ + private BroadcastReceiver mStorageReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.d("ExternalMediaFormatActivity", "got action " + action); + + if (action == Intent.ACTION_MEDIA_REMOVED || + action == Intent.ACTION_MEDIA_CHECKING || + action == Intent.ACTION_MEDIA_MOUNTED || + action == Intent.ACTION_MEDIA_SHARED) { + finish(); + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Log.d("ExternalMediaFormatActivity", "onCreate!"); + // Set up the "dialog" + final AlertController.AlertParams p = mAlertParams; + p.mIconId = com.android.internal.R.drawable.stat_sys_warning; + p.mTitle = getString(com.android.internal.R.string.extmedia_format_title); + p.mMessage = getString(com.android.internal.R.string.extmedia_format_message); + p.mPositiveButtonText = getString(com.android.internal.R.string.extmedia_format_button_format); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(com.android.internal.R.string.cancel); + p.mNegativeButtonListener = this; + setupAlert(); + } + + @Override + protected void onResume() { + super.onResume(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_MEDIA_REMOVED); + filter.addAction(Intent.ACTION_MEDIA_CHECKING); + filter.addAction(Intent.ACTION_MEDIA_MOUNTED); + filter.addAction(Intent.ACTION_MEDIA_SHARED); + registerReceiver(mStorageReceiver, filter); + } + + @Override + protected void onPause() { + super.onPause(); + + unregisterReceiver(mStorageReceiver); + } + + /** + * {@inheritDoc} + */ + public void onClick(DialogInterface dialog, int which) { + + if (which == POSITIVE_BUTTON) { + IMountService mountService = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + if (mountService != null) { + try { + mountService.formatMedia(Environment.getExternalStorageDirectory().toString()); + } catch (RemoteException e) { + } + } + } + + // No matter what, finish the activity + finish(); + } +} diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 434850c439ba9..eb232c7ad03e1 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -22,8 +22,14 @@ interface IBatteryStats { BatteryStatsImpl getStatistics(); void noteStartWakelock(int uid, String name, int type); void noteStopWakelock(int uid, String name, int type); - void noteStartSensor(int uid, int sensor); - void noteStopSensor(int uid, int sensor); + void noteStartSensor(int uid, String name, int sensor); + void noteStopSensor(int uid, String name, int sensor); + void noteRequestGpsOn(int uid); + void noteRequestGpsOff(int uid); + void noteStartGps(int uid); + void noteStopGps(int uid); + void noteScreenOn(); + void noteScreenOff(); void setOnBattery(boolean onBattery); long getAwakeTimeBattery(); long getAwakeTimePlugged(); diff --git a/tests/GadgetHost/src/com/android/gadgethost/TestGadgetProvider.java b/core/java/com/android/internal/app/IUsageStats.aidl old mode 100644 new mode 100755 similarity index 62% rename from tests/GadgetHost/src/com/android/gadgethost/TestGadgetProvider.java rename to core/java/com/android/internal/app/IUsageStats.aidl index 8f9641b6fe8c7..6b053d5c99751 --- a/tests/GadgetHost/src/com/android/gadgethost/TestGadgetProvider.java +++ b/core/java/com/android/internal/app/IUsageStats.aidl @@ -14,19 +14,14 @@ * limitations under the License. */ -package com.android.gadgethost; +package com.android.internal.app; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.util.Log; +import android.content.ComponentName; +import com.android.internal.os.PkgUsageStats; -public class TestGadgetProvider extends BroadcastReceiver { - - static final String TAG = "TestGadgetProvider"; - - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "intent=" + intent); - } +interface IUsageStats { + void noteResumeComponent(in ComponentName componentName); + void notePauseComponent(in ComponentName componentName); + PkgUsageStats getPkgUsageStats(in ComponentName componentName); + PkgUsageStats[] getAllPkgUsageStats(); } - diff --git a/core/java/com/android/internal/app/UsbStorageStopActivity.java b/core/java/com/android/internal/app/UsbStorageStopActivity.java new file mode 100644 index 0000000000000..30523c4bd9e46 --- /dev/null +++ b/core/java/com/android/internal/app/UsbStorageStopActivity.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.IMountService; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.widget.Toast; + +/** + * This activity is shown to the user for him/her to disable USB mass storage. + * It uses the alert dialog style. It will be launched from a notification. + */ +public class UsbStorageStopActivity extends AlertActivity implements DialogInterface.OnClickListener { + + private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1; + + /** Used to detect when the USB cable is unplugged, so we can call finish() */ + private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) { + handleBatteryChanged(intent); + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set up the "dialog" + final AlertController.AlertParams p = mAlertParams; + p.mIconId = com.android.internal.R.drawable.stat_sys_warning; + p.mTitle = getString(com.android.internal.R.string.usb_storage_stop_title); + p.mMessage = getString(com.android.internal.R.string.usb_storage_stop_message); + p.mPositiveButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_mount); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_unmount); + p.mNegativeButtonListener = this; + setupAlert(); + } + + @Override + protected void onResume() { + super.onResume(); + + registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + } + + @Override + protected void onPause() { + super.onPause(); + + unregisterReceiver(mBatteryReceiver); + } + + /** + * {@inheritDoc} + */ + public void onClick(DialogInterface dialog, int which) { + + if (which == POSITIVE_BUTTON) { + stopUsbStorage(); + } + + // No matter what, finish the activity + finish(); + } + + private void stopUsbStorage() { + IMountService mountService = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + if (mountService == null) { + showStoppingError(); + return; + } + + try { + mountService.setMassStorageEnabled(false); + } catch (RemoteException e) { + showStoppingError(); + return; + } + } + + private void handleBatteryChanged(Intent intent) { + int pluggedType = intent.getIntExtra("plugged", 0); + if (pluggedType == 0) { + // It was disconnected from the plug, so finish + finish(); + } + } + + private void showStoppingError() { + Toast.makeText(this, com.android.internal.R.string.usb_storage_stop_error_message, + Toast.LENGTH_LONG).show(); + } + +} diff --git a/location/java/com/android/internal/location/protocol/GPrefetchMode.java b/core/java/com/android/internal/gadget/IGadgetHost.aidl similarity index 65% rename from location/java/com/android/internal/location/protocol/GPrefetchMode.java rename to core/java/com/android/internal/gadget/IGadgetHost.aidl index 041b68610df25..a5b865496db72 100644 --- a/location/java/com/android/internal/location/protocol/GPrefetchMode.java +++ b/core/java/com/android/internal/gadget/IGadgetHost.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,14 @@ * limitations under the License. */ -package com.android.internal.location.protocol; +package com.android.internal.gadget; -public interface GPrefetchMode { - static final int PREFETCH_MODE_NO_PREFETCH = 0; - static final int PREFETCH_MODE_REQUESTED_NEIGHBORS_ONLY = 1; - static final int PREFETCH_MODE_MORE_NEIGHBORS = 2; +import android.content.ComponentName; +import android.gadget.GadgetInfo; +import android.widget.RemoteViews; +/** {@hide} */ +oneway interface IGadgetHost { + void updateGadget(int gadgetId, in RemoteViews views); } diff --git a/core/java/com/android/internal/gadget/IGadgetService.aidl b/core/java/com/android/internal/gadget/IGadgetService.aidl index 6f9af04531449..1b3946fce3a8d 100644 --- a/core/java/com/android/internal/gadget/IGadgetService.aidl +++ b/core/java/com/android/internal/gadget/IGadgetService.aidl @@ -18,12 +18,32 @@ package com.android.internal.gadget; import android.content.ComponentName; import android.gadget.GadgetInfo; +import com.android.internal.gadget.IGadgetHost; +import android.widget.RemoteViews; /** {@hide} */ interface IGadgetService { - int allocateGadgetId(String hostPackage); + + // + // for GadgetHost + // + int[] startListening(IGadgetHost host, String packageName, int hostId, + out List updatedViews); + void stopListening(int hostId); + int allocateGadgetId(String packageName, int hostId); void deleteGadgetId(int gadgetId); - void bindGadgetId(int gadgetId, in ComponentName provider); - GadgetInfo getGadgetInfo(int gadgetId); + void deleteHost(int hostId); + void deleteAllHosts(); + RemoteViews getGadgetViews(int gadgetId); + + // + // for GadgetManager + // + void updateGadgetIds(in int[] gadgetIds, in RemoteViews views); + void updateGadgetProvider(in ComponentName provider, in RemoteViews views); List getInstalledProviders(); + GadgetInfo getGadgetInfo(int gadgetId); + void bindGadgetId(int gadgetId, in ComponentName provider); + } + diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 8912960b0a479..cbb65dc43679a 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -17,6 +17,7 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.NetStat; import android.os.Parcel; import android.os.ParcelFormatException; import android.os.Parcelable; @@ -28,9 +29,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; /** @@ -39,12 +40,14 @@ import java.util.Map; * otherwise. */ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { + + private static final String TAG = "BatteryStatsImpl"; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 13; + private static final int VERSION = 15; private final File mFile; private final File mBackupFile; @@ -77,7 +80,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { long mRealtime; long mRealtimeStart; long mLastRealtime; - + + boolean mScreenOn; + long mLastScreenOnTimeMillis; + long mBatteryScreenOnTimeMillis; + long mPluggedScreenOnTimeMillis; /** * These provide time bases that discount the time the device is plugged * in to power. @@ -87,6 +94,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { long mTrackBatteryUptimeStart; long mTrackBatteryPastRealtime; long mTrackBatteryRealtimeStart; + + long mUnpluggedBatteryUptime; + long mUnpluggedBatteryRealtime; + + HashSet mGpsRequesters = new HashSet(); long mLastWriteTime = 0; // Milliseconds @@ -255,14 +267,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { long realtime = SystemClock.elapsedRealtime() * 1000; long heldTime = stats.getBatteryUptimeLocked(realtime) - mUpdateTime; if (heldTime > 0) { - mTotalTime += (heldTime * 1000) / mCount; + mTotalTime += heldTime / mCount; } mUpdateTime = stats.getBatteryUptimeLocked(realtime); } private long computeRunTimeLocked(long curBatteryUptime) { - return mTotalTime + - (mNesting > 0 ? ((curBatteryUptime * 1000) - mUpdateTime) / mCount : 0); + return mTotalTime + (mNesting > 0 ? (curBatteryUptime - mUpdateTime) / mCount : 0); } void writeSummaryFromParcelLocked(Parcel out, long curBatteryUptime) { @@ -284,6 +295,15 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } + public void unplugTcpCounters() { + final int NU = mUidStats.size(); + for (int iu = 0; iu < NU; iu++) { + Uid u = mUidStats.valueAt(iu); + u.mTcpBytesReceivedAtLastUnplug = u.getTcpBytesReceived(STATS_TOTAL); + u.mTcpBytesSentAtLastUnplug = u.getTcpBytesSent(STATS_TOTAL); + } + } + public void unplugTimers() { ArrayList timers; @@ -305,6 +325,69 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } + public void noteStartGps(int uid) { + mGpsRequesters.add(uid); + mUidStats.get(uid).noteStartGps(); + } + + public void noteStopGps(int uid) { + mGpsRequesters.remove(uid); + mUidStats.get(uid).noteStopGps(); + } + + public void noteRequestGpsOn(int uid) { + mGpsRequesters.add(uid); + mUidStats.get(uid).noteStartGps(); + } + + public void noteRequestGpsOff(int uid) { + mGpsRequesters.remove(uid); + mUidStats.get(uid).noteStopGps(); + } + + /** + * When the device screen or battery state changes, update the appropriate "screen on time" + * counter. + */ + private void updateScreenOnTime(boolean screenOn) { + if (!mScreenOn) { + Log.w(TAG, "updateScreenOnTime without mScreenOn, ignored"); + return; + } + long now = SystemClock.elapsedRealtime(); + long elapsed = now - mLastScreenOnTimeMillis; + if (mOnBattery) { + mBatteryScreenOnTimeMillis += elapsed; + } else { + mPluggedScreenOnTimeMillis += elapsed; + } + if (screenOn) { + mLastScreenOnTimeMillis = now; + } + } + + public void noteScreenOn() { + mScreenOn = true; + mLastScreenOnTimeMillis = SystemClock.elapsedRealtime(); + } + + public void noteScreenOff() { + if (!mScreenOn) { + Log.w(TAG, "noteScreenOff without mScreenOn, ignored"); + return; + } + updateScreenOnTime(false); + mScreenOn = false; + } + + @Override public long getBatteryScreenOnTime() { + return mBatteryScreenOnTimeMillis; + } + + @Override public long getPluggedScreenOnTime() { + return mPluggedScreenOnTimeMillis; + } + @Override public SparseArray getUidStats() { return mUidStats; @@ -314,6 +397,14 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { * The statistics associated with a particular uid. */ public final class Uid extends BatteryStats.Uid { + + final int mUid; + long mLoadedTcpBytesReceived; + long mLoadedTcpBytesSent; + long mTcpBytesReceivedAtLastUnplug; + long mTcpBytesSentAtLastUnplug; + + private final byte[] mBuf = new byte[16]; /** * The statistics we have collected for this uid's wake locks. @@ -334,6 +425,10 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { * The statistics we have collected for this uid's processes. */ final HashMap mPackageStats = new HashMap(); + + public Uid(int uid) { + mUid = uid; + } @Override public Map getWakelockStats() { @@ -354,6 +449,42 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public Map getPackageStats() { return mPackageStats; } + + public int getUid() { + return mUid; + } + + public long getTcpBytesReceived(int which) { + long current = NetStat.getUidRxBytes(mUid); + + if (which == STATS_CURRENT) { + return current; + } else if (which == STATS_LAST) { + return mLoadedTcpBytesReceived; + } else if (which == STATS_UNPLUGGED) { + return current - mTcpBytesReceivedAtLastUnplug; + } else if (which == STATS_TOTAL) { + return mLoadedTcpBytesReceived + current; + } else { + throw new IllegalArgumentException("which = " + which); + } + } + + public long getTcpBytesSent(int which) { + long current = NetStat.getUidTxBytes(mUid); + + if (which == STATS_CURRENT) { + return current; + } else if (which == STATS_LAST) { + return mLoadedTcpBytesSent; + } else if (which == STATS_UNPLUGGED) { + return current - mTcpBytesSentAtLastUnplug; + } else if (which == STATS_TOTAL) { + return mLoadedTcpBytesSent + current; + } else { + throw new IllegalArgumentException("which = " + which); + } + } void writeToParcelLocked(Parcel out) { out.writeInt(mWakelockStats.size()); @@ -366,6 +497,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { out.writeInt(mSensorStats.size()); for (Map.Entry sensorEntry : mSensorStats.entrySet()) { out.writeInt(sensorEntry.getKey()); + out.writeString(sensorEntry.getValue().getName()); Uid.Sensor sensor = sensorEntry.getValue(); sensor.writeToParcelLocked(out); } @@ -383,6 +515,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { Uid.Pkg pkg = pkgEntry.getValue(); pkg.writeToParcelLocked(out); } + + out.writeLong(mLoadedTcpBytesReceived); + out.writeLong(mLoadedTcpBytesSent); + out.writeLong(mTcpBytesReceivedAtLastUnplug); + out.writeLong(mTcpBytesSentAtLastUnplug); } void readFromParcelLocked(Parcel in) { @@ -399,7 +536,8 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mSensorStats.clear(); for (int k = 0; k < numSensors; k++) { int sensorNumber = in.readInt(); - Uid.Sensor sensor = new Sensor(); + String name = in.readString(); + Uid.Sensor sensor = new Sensor(name); sensor.readFromParcelLocked(in); mSensorStats.put(sensorNumber, sensor); } @@ -421,6 +559,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { pkg.readFromParcelLocked(in); mPackageStats.put(packageName, pkg); } + + mLoadedTcpBytesReceived = in.readLong(); + mLoadedTcpBytesSent = in.readLong(); + mTcpBytesReceivedAtLastUnplug = in.readLong(); + mTcpBytesSentAtLastUnplug = in.readLong(); } /** @@ -499,7 +642,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } public final class Sensor extends BatteryStats.Uid.Sensor { + static final int GPS = -10000; // Treat GPS as a sensor + final String mName; Timer sensorTime; + + public Sensor(String name) { + mName = name; + } private Timer readTimerFromParcel(Parcel in) { if (in.readInt() == 0) { @@ -530,6 +679,10 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public Timer getSensorTime() { return sensorTime; } + + public String getName() { + return mName; + } } /** @@ -1039,19 +1192,19 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } - public Timer getSensorTimerLocked(int sensor, boolean create) { + public Timer getSensorTimerLocked(String name, int sensor, boolean create) { Integer sId = Integer.valueOf(sensor); Sensor se = mSensorStats.get(sId); if (se == null) { if (!create) { return null; } - se = new Sensor(); + se = new Sensor(name); mSensorStats.put(sId, se); } Timer t = se.sensorTime; if (t == null) { - t = new Timer(0, mSensorTimers); + t = new Timer(BatteryStats.SENSOR, mSensorTimers); se.sensorTime = t; } return t; @@ -1071,20 +1224,34 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } - public void noteStartSensor(int sensor) { - Timer t = getSensorTimerLocked(sensor, true); + public void noteStartSensor(String name, int sensor) { + Timer t = getSensorTimerLocked(name, sensor, true); if (t != null) { t.startRunningLocked(BatteryStatsImpl.this); } } - public void noteStopSensor(int sensor) { + public void noteStopSensor(String name, int sensor) { // Don't create a timer if one doesn't already exist - Timer t = getSensorTimerLocked(sensor, false); + Timer t = getSensorTimerLocked(name, sensor, false); if (t != null) { t.stopRunningLocked(BatteryStatsImpl.this); } } + + public void noteStartGps() { + Timer t = getSensorTimerLocked("GPS", Sensor.GPS, true); + if (t != null) { + t.startRunningLocked(BatteryStatsImpl.this); + } + } + + public void noteStopGps() { + Timer t = getSensorTimerLocked("GPS", Sensor.GPS, false); + if (t != null) { + t.stopRunningLocked(BatteryStatsImpl.this); + } + } public BatteryStatsImpl getBatteryStats() { return BatteryStatsImpl.this; @@ -1100,6 +1267,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mTrackBatteryPastRealtime = 0; mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000; mRealtimeStart = mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime() * 1000; + + mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart); + mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart); } public BatteryStatsImpl(Parcel p) { @@ -1119,13 +1289,21 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public void setOnBattery(boolean onBattery) { synchronized(this) { if (mOnBattery != onBattery) { + if (mScreenOn) { + updateScreenOnTime(true); + } + long uptime = SystemClock.uptimeMillis() * 1000; long mSecRealtime = SystemClock.elapsedRealtime(); long realtime = mSecRealtime * 1000; if (onBattery) { - mTrackBatteryUptimeStart = uptime; - mTrackBatteryRealtimeStart = realtime; + mTrackBatteryUptimeStart = getBatteryUptime(uptime); + mTrackBatteryRealtimeStart = getBatteryRealtime(realtime); + unplugTcpCounters(); unplugTimers(); + + mUnpluggedBatteryUptime = getBatteryUptime(uptime); + mUnpluggedBatteryRealtime = getBatteryRealtime(realtime); } else { mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart; mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart; @@ -1172,14 +1350,16 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { @Override public long computeBatteryUptime(long curTime, int which) { + long uptime = getBatteryUptime(curTime); switch (which) { case STATS_TOTAL: - return mBatteryUptime + getBatteryUptime(curTime); + return mBatteryUptime + uptime; case STATS_LAST: return mBatteryLastUptime; case STATS_CURRENT: + return uptime; case STATS_UNPLUGGED: - return getBatteryUptime(curTime); + return uptime - mUnpluggedBatteryUptime; } return 0; } @@ -1192,8 +1372,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { case STATS_LAST: return mBatteryLastRealtime; case STATS_CURRENT: - case STATS_UNPLUGGED: return getBatteryRealtimeLocked(curTime); + case STATS_UNPLUGGED: + return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime; } return 0; } @@ -1234,7 +1415,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public Uid getUidStatsLocked(int uid) { Uid u = mUidStats.get(uid); if (u == null) { - u = new Uid(); + u = new Uid(uid); mUidStats.put(uid, u); } return u; @@ -1246,7 +1427,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public void removeUidStatsLocked(int uid) { mUidStats.remove(uid); } - + /** * Retrieve the statistics object for a particular process, creating * if needed. @@ -1388,15 +1569,21 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mRealtime = in.readLong(); mLastRealtime = in.readLong(); mStartCount++; + + if (version >= 14) { + mBatteryScreenOnTimeMillis = in.readLong(); + mPluggedScreenOnTimeMillis = in.readLong(); + mScreenOn = false; + } final int NU = in.readInt(); - for (int iu=0; iu= 12) { int NSE = in.readInt(); - for (int is=0; is= 14) { + seName = in.readString(); + } if (in.readInt() != 0) { - u.getSensorTimerLocked(seNumber, true).readSummaryFromParcelLocked(in); + u.getSensorTimerLocked(seName, seNumber, true) + .readSummaryFromParcelLocked(in); } } } int NP = in.readInt(); - for (int ip=0; ip= 14) { + u.mLoadedTcpBytesReceived = in.readLong(); + u.mLoadedTcpBytesSent = in.readLong(); + } } } @@ -1474,10 +1671,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { out.writeLong(computeUptime(NOW_SYS, STATS_CURRENT)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_TOTAL)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT)); + + out.writeLong(mBatteryScreenOnTimeMillis); + out.writeLong(mPluggedScreenOnTimeMillis); final int NU = mUidStats.size(); out.writeInt(NU); - for (int iu=0; iu - - - - +package com.android.internal.os; + +parcelable PkgUsageStats; diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java new file mode 100755 index 0000000000000..e847878dd5682 --- /dev/null +++ b/core/java/com/android/internal/os/PkgUsageStats.java @@ -0,0 +1,60 @@ +package com.android.internal.os; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * implementation of PkgUsageStats associated with an + * application package. + * @hide + */ +public class PkgUsageStats implements Parcelable { + public String packageName; + public int launchCount; + public long usageTime; + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public PkgUsageStats createFromParcel(Parcel in) { + return new PkgUsageStats(in); + } + + public PkgUsageStats[] newArray(int size) { + return new PkgUsageStats[size]; + } + }; + + public String toString() { + return "PkgUsageStats{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + packageName + "}"; + } + + public PkgUsageStats(String pkgName, int count, long time) { + packageName = pkgName; + launchCount = count; + usageTime = time; + } + + public PkgUsageStats(Parcel source) { + packageName = source.readString(); + launchCount = source.readInt(); + usageTime = source.readLong(); + } + + public PkgUsageStats(PkgUsageStats pStats) { + packageName = pStats.packageName; + launchCount = pStats.launchCount; + usageTime = pStats.usageTime; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int parcelableFlags){ + dest.writeString(packageName); + dest.writeInt(launchCount); + dest.writeLong(usageTime); + } +} diff --git a/core/java/com/android/internal/os/RecoverySystem.java b/core/java/com/android/internal/os/RecoverySystem.java new file mode 100644 index 0000000000000..c938610b612e5 --- /dev/null +++ b/core/java/com/android/internal/os/RecoverySystem.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.os.FileUtils; +import android.os.Power; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +/** + * Utility class for interacting with the Android recovery partition. + * The recovery partition is a small standalone system which can perform + * operations that are difficult while the main system is running, like + * upgrading system software or reformatting the data partition. + * Note that most of these operations must be run as root. + * + * @hide + */ +public class RecoverySystem { + private static final String TAG = "RecoverySystem"; // for logging + + // Used to communicate with recovery. See commands/recovery/recovery.c. + private static File RECOVERY_DIR = new File("/cache/recovery"); + private static File COMMAND_FILE = new File(RECOVERY_DIR, "command"); + private static File LOG_FILE = new File(RECOVERY_DIR, "log"); + + // Length limits for reading files. + private static int LOG_FILE_MAX_LENGTH = 8 * 1024; + + /** + * Reboot into the recovery system to install a system update. + * @param update package to install (must be in /cache or /data). + * @throws IOException if something goes wrong. + */ + public static void rebootAndUpdate(File update) throws IOException { + String path = update.getCanonicalPath(); + if (path.startsWith("/cache/")) { + path = "CACHE:" + path.substring(7); + } else if (path.startsWith("/data/")) { + path = "DATA:" + path.substring(6); + } else { + throw new IllegalArgumentException( + "Must start with /cache or /data: " + path); + } + bootCommand("--update_package=" + path); + } + + /** + * Reboot into the recovery system to wipe the /data partition. + * @param extras to add to the RECOVERY_COMPLETED intent after rebooting. + * @throws IOException if something goes wrong. + */ + public static void rebootAndWipe() throws IOException { + bootCommand("--wipe_data"); + } + + /** + * Reboot into the recovery system with the supplied argument. + * @param arg to pass to the recovery utility. + * @throws IOException if something goes wrong. + */ + private static void bootCommand(String arg) throws IOException { + RECOVERY_DIR.mkdirs(); // In case we need it + COMMAND_FILE.delete(); // In case it's not writable + LOG_FILE.delete(); + + FileWriter command = new FileWriter(COMMAND_FILE); + try { + command.write(arg); + command.write("\n"); + } finally { + command.close(); + } + + // Having written the command file, go ahead and reboot + Power.reboot("recovery"); + throw new IOException("Reboot failed (no permissions?)"); + } + + /** + * Called after booting to process and remove recovery-related files. + * @return the log file from recovery, or null if none was found. + */ + public static String handleAftermath() { + // Record the tail of the LOG_FILE + String log = null; + try { + log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n"); + } catch (FileNotFoundException e) { + Log.i(TAG, "No recovery log file"); + } catch (IOException e) { + Log.e(TAG, "Error reading recovery log", e); + } + + // Delete everything in RECOVERY_DIR + String[] names = RECOVERY_DIR.list(); + for (int i = 0; names != null && i < names.length; i++) { + File f = new File(RECOVERY_DIR, names[i]); + if (!f.delete()) { + Log.e(TAG, "Can't delete: " + f); + } else { + Log.i(TAG, "Deleted: " + f); + } + } + + return log; + } +} diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 011e9446985a3..b0b00b2d4c520 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -13,6 +13,8 @@ import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; +import java.lang.ref.WeakReference; + public class IInputConnectionWrapper extends IInputContext.Stub { static final String TAG = "IInputConnectionWrapper"; @@ -22,6 +24,8 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_GET_EXTRACTED_TEXT = 40; private static final int DO_COMMIT_TEXT = 50; private static final int DO_COMMIT_COMPLETION = 55; + private static final int DO_SET_SELECTION = 57; + private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 58; private static final int DO_SET_COMPOSING_TEXT = 60; private static final int DO_FINISH_COMPOSING_TEXT = 65; private static final int DO_SEND_KEY_EVENT = 70; @@ -33,7 +37,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_PERFORM_PRIVATE_COMMAND = 120; private static final int DO_CLEAR_META_KEY_STATES = 130; - private InputConnection mInputConnection; + private WeakReference mInputConnection; private Looper mMainLooper; private Handler mH; @@ -57,17 +61,21 @@ public class IInputConnectionWrapper extends IInputContext.Stub { } public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) { - mInputConnection = conn; + mInputConnection = new WeakReference(conn); mMainLooper = mainLooper; mH = new MyHandler(mMainLooper); } - public void getTextAfterCursor(int length, int seq, IInputContextCallback callback) { - dispatchMessage(obtainMessageISC(DO_GET_TEXT_AFTER_CURSOR, length, seq, callback)); + public boolean isActive() { + return true; } - public void getTextBeforeCursor(int length, int seq, IInputContextCallback callback) { - dispatchMessage(obtainMessageISC(DO_GET_TEXT_BEFORE_CURSOR, length, seq, callback)); + public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) { + dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback)); + } + + public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) { + dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback)); } public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) { @@ -88,6 +96,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text)); } + public void setSelection(int start, int end) { + dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end)); + } + + public void performContextMenuAction(int id) { + dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0)); + } + public void setComposingText(CharSequence text, int newCursorPosition) { dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text)); } @@ -147,8 +163,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_TEXT_AFTER_CURSOR: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setTextAfterCursor(mInputConnection.getTextAfterCursor(msg.arg1), - args.seq); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); + args.callback.setTextAfterCursor(null, args.seq); + return; + } + args.callback.setTextAfterCursor(ic.getTextAfterCursor( + msg.arg1, msg.arg2), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e); } @@ -157,8 +179,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_TEXT_BEFORE_CURSOR: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setTextBeforeCursor(mInputConnection.getTextBeforeCursor(msg.arg1), - args.seq); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); + args.callback.setTextBeforeCursor(null, args.seq); + return; + } + args.callback.setTextBeforeCursor(ic.getTextBeforeCursor( + msg.arg1, msg.arg2), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e); } @@ -167,7 +195,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_CURSOR_CAPS_MODE: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setCursorCapsMode(mInputConnection.getCursorCapsMode(msg.arg1), + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); + args.callback.setCursorCapsMode(0, args.seq); + return; + } + args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e); @@ -177,7 +211,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_EXTRACTED_TEXT: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setExtractedText(mInputConnection.getExtractedText( + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getExtractedText on inactive InputConnection"); + args.callback.setExtractedText(null, args.seq); + return; + } + args.callback.setExtractedText(ic.getExtractedText( (ExtractedTextRequest)args.arg1, msg.arg1), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setExtractedText", e); @@ -185,52 +225,130 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return; } case DO_COMMIT_TEXT: { - mInputConnection.commitText((CharSequence)msg.obj, msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitText on inactive InputConnection"); + return; + } + ic.commitText((CharSequence)msg.obj, msg.arg1); + return; + } + case DO_SET_SELECTION: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "setSelection on inactive InputConnection"); + return; + } + ic.setSelection(msg.arg1, msg.arg2); + return; + } + case DO_PERFORM_CONTEXT_MENU_ACTION: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "performContextMenuAction on inactive InputConnection"); + return; + } + ic.performContextMenuAction(msg.arg1); return; } case DO_COMMIT_COMPLETION: { - mInputConnection.commitCompletion((CompletionInfo)msg.obj); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCompletion on inactive InputConnection"); + return; + } + ic.commitCompletion((CompletionInfo)msg.obj); return; } case DO_SET_COMPOSING_TEXT: { - mInputConnection.setComposingText((CharSequence)msg.obj, msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingText on inactive InputConnection"); + return; + } + ic.setComposingText((CharSequence)msg.obj, msg.arg1); return; } case DO_FINISH_COMPOSING_TEXT: { - mInputConnection.finishComposingText(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "finishComposingText on inactive InputConnection"); + return; + } + ic.finishComposingText(); return; } case DO_SEND_KEY_EVENT: { - mInputConnection.sendKeyEvent((KeyEvent)msg.obj); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "sendKeyEvent on inactive InputConnection"); + return; + } + ic.sendKeyEvent((KeyEvent)msg.obj); return; } case DO_CLEAR_META_KEY_STATES: { - mInputConnection.clearMetaKeyStates(msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); + return; + } + ic.clearMetaKeyStates(msg.arg1); return; } case DO_DELETE_SURROUNDING_TEXT: { - mInputConnection.deleteSurroundingText(msg.arg1, msg.arg2); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); + return; + } + ic.deleteSurroundingText(msg.arg1, msg.arg2); return; } case DO_BEGIN_BATCH_EDIT: { - mInputConnection.beginBatchEdit(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "beginBatchEdit on inactive InputConnection"); + return; + } + ic.beginBatchEdit(); return; } case DO_END_BATCH_EDIT: { - mInputConnection.beginBatchEdit(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "endBatchEdit on inactive InputConnection"); + return; + } + ic.endBatchEdit(); return; } case DO_HIDE_STATUS_ICON: { - mInputConnection.hideStatusIcon(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "hideStatusIcon on inactive InputConnection"); + return; + } + ic.hideStatusIcon(); return; } case DO_SHOW_STATUS_ICON: { - mInputConnection.showStatusIcon((String)msg.obj, msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "showStatusIcon on inactive InputConnection"); + return; + } + ic.showStatusIcon((String)msg.obj, msg.arg1); return; } case DO_PERFORM_PRIVATE_COMMAND: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "performPrivateCommand on inactive InputConnection"); + return; + } SomeArgs args = (SomeArgs)msg.obj; - mInputConnection.performPrivateCommand((String)args.arg1, + ic.performPrivateCommand((String)args.arg1, (Bundle)args.arg2); return; } @@ -257,6 +375,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return mH.obtainMessage(what, arg1, 0, args); } + Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) { + SomeArgs args = new SomeArgs(); + args.callback = callback; + args.seq = seq; + return mH.obtainMessage(what, arg1, arg2, args); + } + Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq, IInputContextCallback callback) { SomeArgs args = new SomeArgs(); diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index b048ce292e2b8..7cc8ada6044ec 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -29,9 +29,9 @@ import com.android.internal.view.IInputContextCallback; * {@hide} */ oneway interface IInputContext { - void getTextBeforeCursor(int length, int seq, IInputContextCallback callback); + void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback); - void getTextAfterCursor(int length, int seq, IInputContextCallback callback); + void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback); void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback); @@ -48,6 +48,10 @@ import com.android.internal.view.IInputContextCallback; void commitCompletion(in CompletionInfo completion); + void setSelection(int start, int end); + + void performContextMenuAction(int id); + void beginBatchEdit(); void endBatchEdit(); diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index f650713fc0eff..9b004025ea9ae 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -38,9 +38,9 @@ oneway interface IInputMethod { void unbindInput(); - void startInput(in EditorInfo attribute); + void startInput(in IInputContext inputContext, in EditorInfo attribute); - void restartInput(in EditorInfo attribute); + void restartInput(in IInputContext inputContext, in EditorInfo attribute); void createSession(IInputMethodCallback callback); @@ -48,7 +48,7 @@ oneway interface IInputMethod { void revokeSession(IInputMethodSession session); - void showSoftInput(boolean explicit); + void showSoftInput(int flags); void hideSoftInput(); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 2a15bdb999716..2f5cd14ea7ac9 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -35,7 +35,8 @@ interface IInputMethodManager { void removeClient(in IInputMethodClient client); InputBindResult startInput(in IInputMethodClient client, - in EditorInfo attribute, boolean initial, boolean needResult); + IInputContext inputContext, in EditorInfo attribute, + boolean initial, boolean needResult); void finishInput(in IInputMethodClient client); void showSoftInput(in IInputMethodClient client, int flags); void hideSoftInput(in IInputMethodClient client, int flags); diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index a9ba5f6190ff1..af4ad25fc6f26 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -151,11 +151,11 @@ public class InputConnectionWrapper implements InputConnection { mIInputContext = inputContext; } - public CharSequence getTextAfterCursor(int length) { + public CharSequence getTextAfterCursor(int length, int flags) { CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); - mIInputContext.getTextAfterCursor(length, callback.mSeq, callback); + mIInputContext.getTextAfterCursor(length, flags, callback.mSeq, callback); synchronized (callback) { callback.waitForResultLocked(); if (callback.mHaveValue) { @@ -169,11 +169,11 @@ public class InputConnectionWrapper implements InputConnection { return value; } - public CharSequence getTextBeforeCursor(int length) { + public CharSequence getTextBeforeCursor(int length, int flags) { CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); - mIInputContext.getTextBeforeCursor(length, callback.mSeq, callback); + mIInputContext.getTextBeforeCursor(length, flags, callback.mSeq, callback); synchronized (callback) { callback.waitForResultLocked(); if (callback.mHaveValue) { @@ -241,6 +241,24 @@ public class InputConnectionWrapper implements InputConnection { } } + public boolean setSelection(int start, int end) { + try { + mIInputContext.setSelection(start, end); + return true; + } catch (RemoteException e) { + return false; + } + } + + public boolean performContextMenuAction(int id) { + try { + mIInputContext.performContextMenuAction(id); + return true; + } catch (RemoteException e) { + return false; + } + } + public boolean setComposingText(CharSequence text, int newCursorPosition) { try { mIInputContext.setComposingText(text, newCursorPosition); diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java index 558a4c30bca11..9e1f2aedca475 100644 --- a/core/java/com/android/internal/view/menu/IconMenuItemView.java +++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java @@ -142,9 +142,6 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie } void setCaptionMode(boolean shortcut) { - - mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut()); - /* * If there is no item model, don't do any of the below (for example, * the 'More' item doesn't have a model) @@ -153,6 +150,8 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie return; } + mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut()); + CharSequence text = mItemData.getTitleForItemView(this); if (mShortcutCaptionMode) { diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index 32513cd5d2ded..e15587520625b 100644 --- a/core/java/com/android/internal/view/menu/ListMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java @@ -220,7 +220,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView mRadioButton = (RadioButton) inflater.inflate(com.android.internal.R.layout.list_menu_item_radio, this, false); - addView(mRadioButton, 0); + addView(mRadioButton); } private void insertCheckBox() { @@ -228,7 +228,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView mCheckBox = (CheckBox) inflater.inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, false); - addView(mCheckBox, 0); + addView(mCheckBox); } public boolean prefersCondensedTitle() { diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index a2673a56b9491..648d944cc7b86 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -16,241 +16,42 @@ package com.android.internal.widget; -import android.content.res.TypedArray; import android.os.Bundle; -import android.os.Handler; import android.text.Editable; -import android.text.Selection; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.TextUtils; import android.text.method.KeyListener; import android.util.Log; -import android.util.LogPrinter; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.widget.TextView; -class ComposingText { -} - public class EditableInputConnection extends BaseInputConnection { private static final boolean DEBUG = false; private static final String TAG = "EditableInputConnection"; - public static final Object COMPOSING = new ComposingText(); - private final TextView mTextView; - private Object[] mDefaultComposingSpans; - public EditableInputConnection(TextView textview) { - super(textview); + super(textview, false); mTextView = textview; } - public static final void removeComposingSpans(Spannable text) { - text.removeSpan(COMPOSING); - Object[] sps = text.getSpans(0, text.length(), Object.class); - if (sps != null) { - for (int i=sps.length-1; i>=0; i--) { - Object o = sps[i]; - if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) { - text.removeSpan(o); - } - } - } - } - - public static void setComposingSpans(Spannable text) { - final Object[] sps = text.getSpans(0, text.length(), Object.class); - if (sps != null) { - for (int i=sps.length-1; i>=0; i--) { - final Object o = sps[i]; - if (o == COMPOSING) { - text.removeSpan(o); - continue; - } - final int fl = text.getSpanFlags(o); - if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) - != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) { - text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o), - (fl&Spanned.SPAN_POINT_MARK_MASK) - | Spanned.SPAN_COMPOSING - | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - } - - text.setSpan(COMPOSING, 0, text.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); - } - - public static int getComposingSpanStart(Spannable text) { - return text.getSpanStart(COMPOSING); - } - - public static int getComposingSpanEnd(Spannable text) { - return text.getSpanEnd(COMPOSING); - } - - public boolean setComposingText(CharSequence text, int newCursorPosition) { - if (DEBUG) Log.v(TAG, "setComposingText " + text); - replaceText(text, newCursorPosition, true); - return true; - } - - public boolean finishComposingText() { - if (DEBUG) Log.v(TAG, "finishComposingText"); - final Editable content = getEditable(); - if (content != null) { - removeComposingSpans(content); - } - return true; - } - - public boolean commitText(CharSequence text, int newCursorPosition) { - if (DEBUG) Log.v(TAG, "commitText " + text); - replaceText(text, newCursorPosition, false); - return true; - } - - public boolean commitCompletion(CompletionInfo text) { - if (DEBUG) Log.v(TAG, "commitCompletion " + text); - mTextView.onCommitCompletion(text); - return true; - } - - public CharSequence getTextBeforeCursor(int length) { - final Editable content = getEditable(); - if (content == null) return null; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - if (length > a) { - length = a; - } - - return content.subSequence(a - length, a); - } - - public CharSequence getTextAfterCursor(int length) { - final Editable content = getEditable(); - if (content == null) return null; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - if (b + length > content.length()) { - length = content.length() - b; - } - - return content.subSequence(b, b + length); - } - - public int getCursorCapsMode(int reqModes) { - final Editable content = getEditable(); - if (content == null) return 0; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - return TextUtils.getCapsMode(content, a, reqModes); - } - - public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { - if (mTextView != null) { - ExtractedText et = new ExtractedText(); - if (mTextView.extractText(request, et)) { - if ((flags&EXTRACTED_TEXT_MONITOR) != 0) { - mTextView.setExtracting(request); - } - return et; - } + public Editable getEditable() { + TextView tv = mTextView; + if (tv != null) { + return tv.getEditableText(); } return null; } - public boolean deleteSurroundingText(int leftLength, int rightLength) { - if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength - + " / " + rightLength); - final Editable content = getEditable(); - if (content == null) return false; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - // ignore the composing text. - int ca = content.getSpanStart(COMPOSING); - int cb = content.getSpanEnd(COMPOSING); - if (cb < ca) { - int tmp = ca; - ca = cb; - cb = tmp; - } - if (ca != -1 && cb != -1) { - if (ca < a) a = ca; - if (cb > b) b = cb; - } - - int deleted = 0; - - if (leftLength > 0) { - int start = a - leftLength; - if (start < 0) start = 0; - content.delete(start, a); - deleted = a - start; - } - - if (rightLength > 0) { - b = b - deleted; - - int end = b + rightLength; - if (end > content.length()) end = content.length(); - - content.delete(b, end); - } - - return true; - } - public boolean beginBatchEdit() { - if (mTextView == null) return false; - mTextView.onBeginBatchEdit(); + mTextView.beginBatchEdit(); return true; } public boolean endBatchEdit() { - if (mTextView == null) return false; - mTextView.onEndBatchEdit(); + mTextView.endBatchEdit(); return true; } @@ -262,104 +63,37 @@ public class EditableInputConnection extends BaseInputConnection { return true; } - public boolean performPrivateCommand(String action, Bundle data) { - if (mTextView == null) return false; - mTextView.onPrivateIMECommand(action, data); + public boolean commitCompletion(CompletionInfo text) { + if (DEBUG) Log.v(TAG, "commitCompletion " + text); + mTextView.beginBatchEdit(); + mTextView.onCommitCompletion(text); + mTextView.endBatchEdit(); + return true; + } + + public boolean performContextMenuAction(int id) { + if (DEBUG) Log.v(TAG, "performContextMenuAction " + id); + mTextView.beginBatchEdit(); + mTextView.onTextContextMenuItem(id); + mTextView.endBatchEdit(); return true; } - private Editable getEditable() { - TextView tv = mTextView; - if (tv != null) { - return tv.getEditableText(); + public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { + if (mTextView != null) { + ExtractedText et = new ExtractedText(); + if (mTextView.extractText(request, et)) { + if ((flags&GET_EXTRACTED_TEXT_MONITOR) != 0) { + mTextView.setExtracting(request); + } + return et; + } } return null; } - private void replaceText(CharSequence text, int newCursorPosition, - boolean composing) { - final Editable content = getEditable(); - - // delete composing text set previously. - int a = content.getSpanStart(COMPOSING); - int b = content.getSpanEnd(COMPOSING); - - if (DEBUG) Log.v(TAG, "Composing span: " + a + " to " + b); - - if (b < a) { - int tmp = a; - a = b; - b = tmp; - } - - if (a != -1 && b != -1) { - removeComposingSpans(content); - } else { - a = Selection.getSelectionStart(content); - b = Selection.getSelectionEnd(content); - if (a >=0 && b>= 0 && a != b) { - if (b < a) { - int tmp = a; - a = b; - b = tmp; - } - } - } - - if (composing) { - Spannable sp = null; - if (!(text instanceof Spannable)) { - sp = new SpannableStringBuilder(text); - text = sp; - if (mDefaultComposingSpans == null) { - TypedArray ta = mTextView.getContext().getTheme() - .obtainStyledAttributes(new int[] { - com.android.internal.R.attr.candidatesTextStyleSpans - }); - CharSequence style = ta.getText(0); - ta.recycle(); - if (style != null && style instanceof Spanned) { - mDefaultComposingSpans = ((Spanned)style).getSpans( - 0, style.length(), Object.class); - } - } - if (mDefaultComposingSpans != null) { - for (int i = 0; i < mDefaultComposingSpans.length; ++i) { - sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - } else { - sp = (Spannable)text; - } - setComposingSpans(sp); - } - - // Adjust newCursorPosition to be relative the start of the text. - newCursorPosition += a; - - if (DEBUG) Log.v(TAG, "Replacing from " + a + " to " + b + " with \"" - + text + "\", composing=" + composing - + ", type=" + text.getClass().getCanonicalName()); - - if (DEBUG) { - LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); - lp.println("Current text:"); - TextUtils.dumpSpans(content, lp, " "); - lp.println("Composing text:"); - TextUtils.dumpSpans(text, lp, " "); - } - - content.replace(a, b, text); - if (newCursorPosition < 0) newCursorPosition = 0; - if (newCursorPosition > content.length()) - newCursorPosition = content.length(); - Selection.setSelection(content, newCursorPosition); - - if (DEBUG) { - LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); - lp.println("Final text:"); - TextUtils.dumpSpans(content, lp, " "); - } + public boolean performPrivateCommand(String action, Bundle data) { + mTextView.onPrivateIMECommand(action, data); + return true; } } diff --git a/core/java/com/google/android/net/GoogleHttpClient.java b/core/java/com/google/android/net/GoogleHttpClient.java index 4656aff15bc13..2fcb0c30081bc 100644 --- a/core/java/com/google/android/net/GoogleHttpClient.java +++ b/core/java/com/google/android/net/GoogleHttpClient.java @@ -16,18 +16,28 @@ package com.google.android.net; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.net.http.AndroidHttpClient; +import android.os.Build; +import android.os.NetStat; +import android.os.SystemClock; +import android.provider.Checkin; +import android.util.Config; +import android.util.Log; +import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.ProtocolException; -import org.apache.http.impl.client.RequestWrapper; -import org.apache.http.impl.client.EntityEnclosingRequestWrapper; -import org.apache.http.client.HttpClient; import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.impl.client.EntityEnclosingRequestWrapper; +import org.apache.http.impl.client.RequestWrapper; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; @@ -35,25 +45,13 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.os.SystemClock; -import android.os.Build; -import android.net.http.AndroidHttpClient; -import android.provider.Checkin; -import android.util.Config; -import android.util.Log; - /** * {@link AndroidHttpClient} wrapper that uses {@link UrlRules} to rewrite URLs * and otherwise tweak HTTP requests. */ public class GoogleHttpClient implements HttpClient { - private static final String TAG = "GoogleHttpClient"; - private final AndroidHttpClient mClient; - private final ContentResolver mResolver; - private final String mUserAgent; + private static final String TAG = "GoogleHttpClient"; /** Exception thrown when a request is blocked by the URL rules. */ public static class BlockedRequestException extends IOException { @@ -63,6 +61,10 @@ public class GoogleHttpClient implements HttpClient { mRule = rule; } } + + private final AndroidHttpClient mClient; + private final ContentResolver mResolver; + private final String mUserAgent; /** * Create an HTTP client. Normally one client is shared throughout an app. @@ -120,8 +122,37 @@ public class GoogleHttpClient implements HttpClient { String code = "Error"; long start = SystemClock.elapsedRealtime(); try { - HttpResponse response = mClient.execute(request, context); - code = Integer.toString(response.getStatusLine().getStatusCode()); + HttpResponse response; + // TODO: if we're logging network stats, and if the apache library is configured + // to follow redirects, count each redirect as an additional round trip. + + // see if we're logging network stats. + boolean logNetworkStats = NetworkStatsEntity.shouldLogNetworkStats(); + + if (logNetworkStats) { + int uid = android.os.Process.myUid(); + long startTx = NetStat.getUidTxBytes(uid); + long startRx = NetStat.getUidRxBytes(uid); + + response = mClient.execute(request, context); + code = Integer.toString(response.getStatusLine().getStatusCode()); + + HttpEntity origEntity = response == null ? null : response.getEntity(); + if (origEntity != null) { + // yeah, we compute the same thing below. we do need to compute this here + // so we can wrap the HttpEntity in the response. + long now = SystemClock.elapsedRealtime(); + long elapsed = now - start; + NetworkStatsEntity entity = new NetworkStatsEntity(origEntity, + mUserAgent, uid, startTx, startRx, + elapsed /* response latency */, now /* processing start time */); + response.setEntity(entity); + } + } else { + response = mClient.execute(request, context); + code = Integer.toString(response.getStatusLine().getStatusCode()); + } + return response; } catch (IOException e) { code = "IOException"; diff --git a/core/java/com/google/android/net/NetStats.java b/core/java/com/google/android/net/NetStats.java deleted file mode 100644 index fee821989a56f..0000000000000 --- a/core/java/com/google/android/net/NetStats.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.google.android.net; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.NoSuchElementException; -import java.util.StringTokenizer; - -/** - * Gets network send/receive statistics for this process. - * The statistics come from /proc/pid/stat, using the ATOP kernel modification. - */ -public class NetStats { - private static String statsFile = "/proc/" + android.os.Process.myPid() + "/stat"; - - private static String TAG = "netstat"; - - /** - * Returns network stats for this process. - * Returns stats of 0 if problem encountered. - * - * @return [bytes sent, bytes received] - */ - public static long[] getStats() { - long result[] = new long[2]; - - try { - BufferedReader br = new BufferedReader(new FileReader(statsFile), 512); - String line = br.readLine(); // Skip first line - line = br.readLine(); - StringTokenizer st = new StringTokenizer(line); - st.nextToken(); // disk read - st.nextToken(); // disk sectors - st.nextToken(); // disk write - st.nextToken(); // disk sectors - st.nextToken(); // tcp send - result[0] = Long.parseLong(st.nextToken()); // tcp bytes sent - st.nextToken(); //tcp recv - result[1] = Long.parseLong(st.nextToken()); // tcp bytes recv - } catch (IOException e) { - // Probably wrong kernel; no point logging exception - } catch (NoSuchElementException e) { - } catch (NullPointerException e) { - } - return result; - } -} diff --git a/core/java/com/google/android/net/NetworkStatsEntity.java b/core/java/com/google/android/net/NetworkStatsEntity.java new file mode 100644 index 0000000000000..f5d2349ed417c --- /dev/null +++ b/core/java/com/google/android/net/NetworkStatsEntity.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.net; + +import android.os.NetStat; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.EventLog; + +import org.apache.http.HttpEntity; +import org.apache.http.entity.HttpEntityWrapper; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + + +public class NetworkStatsEntity extends HttpEntityWrapper { + + private static final int HTTP_STATS_EVENT = 52001; + + private class NetworkStatsInputStream extends FilterInputStream { + + public NetworkStatsInputStream(InputStream wrapped) { + super(wrapped); + } + + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + long processingTime = SystemClock.elapsedRealtime() - mProcessingStartTime; + long tx = NetStat.getUidTxBytes(mUid); + long rx = NetStat.getUidRxBytes(mUid); + + EventLog.writeEvent(HTTP_STATS_EVENT, mUa, mResponseLatency, processingTime, + tx - mStartTx, rx - mStartRx); + } + } + } + + private final String mUa; + private final int mUid; + private final long mStartTx; + private final long mStartRx; + private final long mResponseLatency; + private final long mProcessingStartTime; + + public NetworkStatsEntity(HttpEntity orig, String ua, + int uid, long startTx, long startRx, long responseLatency, + long processingStartTime) { + super(orig); + this.mUa = ua; + this.mUid = uid; + this.mStartTx = startTx; + this.mStartRx = startRx; + this.mResponseLatency = responseLatency; + this.mProcessingStartTime = processingStartTime; + } + + public static boolean shouldLogNetworkStats() { + return "1".equals(SystemProperties.get("googlehttpclient.logstats")); + } + + @Override + public InputStream getContent() throws IOException { + InputStream orig = super.getContent(); + return new NetworkStatsInputStream(orig); + } +} diff --git a/core/java/com/google/android/util/SimplePullParser.java b/core/java/com/google/android/util/SimplePullParser.java index 95f2ddb7f3450..031790b9cf2f5 100644 --- a/core/java/com/google/android/util/SimplePullParser.java +++ b/core/java/com/google/android/util/SimplePullParser.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.Reader; +import java.io.Closeable; import android.util.Xml; import android.util.Log; @@ -41,6 +42,7 @@ public class SimplePullParser { private String mLogTag = null; private final XmlPullParser mParser; + private Closeable source; private String mCurrentStartTag; /** @@ -56,6 +58,7 @@ public class SimplePullParser { moveToStartDocument(parser); mParser = parser; mCurrentStartTag = null; + source = stream; } catch (XmlPullParserException e) { throw new ParseException(e); } @@ -68,6 +71,7 @@ public class SimplePullParser { public SimplePullParser(XmlPullParser parser) { mParser = parser; mCurrentStartTag = null; + source = null; } /** @@ -89,6 +93,7 @@ public class SimplePullParser { moveToStartDocument(parser); mParser = parser; mCurrentStartTag = null; + source = reader; } catch (XmlPullParserException e) { throw new ParseException(e); } @@ -171,6 +176,12 @@ public class SimplePullParser { } if (eventType == XmlPullParser.END_DOCUMENT && parentDepth == 0) { + // we could just rely on the caller calling close(), which it should, but try + // to auto-close for clients that might have missed doing so. + if (source != null) { + source.close(); + source = null; + } return null; } @@ -332,6 +343,20 @@ public class SimplePullParser { } } + /** + * Close this SimplePullParser and any underlying resources (e.g., its InputStream or + * Reader source) used by this SimplePullParser. + */ + public void close() { + if (source != null) { + try { + source.close(); + } catch (IOException ioe) { + // ignore + } + } + } + /** * Returns the string value of the named attribute. An exception will * be thrown if the attribute is not present or is not a valid long. diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 32c3a54bd7906..6e5c4e07e2fe7 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -42,7 +42,6 @@ LOCAL_SRC_FILES:= \ android_os_SystemClock.cpp \ android_os_SystemProperties.cpp \ android_os_UEventObserver.cpp \ - android_os_NetStat.cpp \ android_os_Hardware.cpp \ android_net_LocalSocketImpl.cpp \ android_net_NetUtils.cpp \ @@ -146,7 +145,8 @@ LOCAL_SHARED_LIBRARIES := \ libcorecg \ libsqlite \ libdvm \ - libGLES_CM \ + libEGL \ + libGLESv1_CM \ libhardware \ libhardware_legacy \ libsonivox \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 097ffac2cf371..40dc2a1dc4d13 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -129,7 +129,6 @@ extern int register_android_os_SystemClock(JNIEnv* env); extern int register_android_os_FileObserver(JNIEnv *env); extern int register_android_os_FileUtils(JNIEnv *env); extern int register_android_os_UEventObserver(JNIEnv* env); -extern int register_android_os_NetStat(JNIEnv* env); extern int register_android_os_MemoryFile(JNIEnv* env); extern int register_android_net_LocalSocketImpl(JNIEnv* env); extern int register_android_net_NetworkUtils(JNIEnv* env); @@ -502,6 +501,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) JavaVMOption opt; char propBuf[PROPERTY_VALUE_MAX]; char stackTraceFileBuf[PROPERTY_VALUE_MAX]; + char dexoptFlagsBuf[PROPERTY_VALUE_MAX]; char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX]; char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX]; char* stackTraceFile = NULL; @@ -509,7 +509,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) char* cp; bool checkJni = false; bool logStdio = false; - bool verifyJava = true; enum { kEMDefault, kEMIntPortable, kEMIntFast } executionMode = kEMDefault; blockSigpipe(); @@ -536,15 +535,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) } } - property_get("dalvik.vm.verify-bytecode", propBuf, ""); - if (strcmp(propBuf, "true") == 0) { - verifyJava = true; - } else if (strcmp(propBuf, "false") == 0) { - verifyJava = false; - } else { - /* bad value or not defined; use default */ - } - property_get("dalvik.vm.execution-mode", propBuf, ""); if (strcmp(propBuf, "int:portable") == 0) { executionMode = kEMIntPortable; @@ -609,21 +599,49 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) mOptions.add(opt); /* - * Enable or disable bytecode verification. - * - * We don't optimize classes that haven't been verified, but that only - * matters if we do "just-in-time" DEX optimization. + * Enable or disable dexopt features, such as bytecode verification and + * calculation of register maps for precise GC. */ - if (verifyJava) { - opt.optionString = "-Xverify:all"; - mOptions.add(opt); - opt.optionString = "-Xdexopt:verified"; - mOptions.add(opt); - } else { - opt.optionString = "-Xverify:none"; - mOptions.add(opt); - opt.optionString = "-Xdexopt:verified"; - mOptions.add(opt); + property_get("dalvik.vm.dexopt-flags", dexoptFlagsBuf, ""); + if (dexoptFlagsBuf[0] != '\0') { + const char* opc; + const char* val; + + opc = strstr(dexoptFlagsBuf, "v="); /* verification */ + if (opc != NULL) { + switch (*(opc+2)) { + case 'n': val = "-Xverify:none"; break; + case 'r': val = "-Xverify:remote"; break; + case 'a': val = "-Xverify:all"; break; + default: val = NULL; break; + } + + if (val != NULL) { + opt.optionString = val; + mOptions.add(opt); + } + } + + opc = strstr(dexoptFlagsBuf, "o="); /* optimization */ + if (opc != NULL) { + switch (*(opc+2)) { + case 'n': val = "-Xdexopt:none"; break; + case 'v': val = "-Xdexopt:verified"; break; + case 'a': val = "-Xdexopt:all"; break; + default: val = NULL; break; + } + + if (val != NULL) { + opt.optionString = val; + mOptions.add(opt); + } + } + + opc = strstr(dexoptFlagsBuf, "m=y"); /* register map */ + if (opc != NULL) { + opt.optionString = "-Xgenregmap"; + mOptions.add(opt); + } } /* enable debugging; set suspend=y to pause during VM init */ @@ -1066,7 +1084,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_net_LocalSocketImpl), REG_JNI(register_android_net_NetworkUtils), REG_JNI(register_android_net_wifi_WifiManager), - REG_JNI(register_android_os_NetStat), REG_JNI(register_android_os_MemoryFile), REG_JNI(register_com_android_internal_os_ZygoteInit), REG_JNI(register_android_hardware_Camera), diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index be8526d0c67a6..332b01c7ce705 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -1,3 +1,5 @@ +#define LOG_TAG "BitmapFactory" + #include "SkImageDecoder.h" #include "SkPixelRef.h" #include "SkStream.h" @@ -481,6 +483,48 @@ static void nativeRequestCancel(JNIEnv*, jobject joptions) { (void)AutoDecoderCancel::RequestCancel(joptions); } +static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, + jobject padding) { + + jbyte* array = env->GetByteArrayElements(chunkObject, 0); + if (array != NULL) { + size_t chunkSize = env->GetArrayLength(chunkObject); + void* storage = alloca(chunkSize); + android::Res_png_9patch* chunk = static_cast(storage); + memcpy(chunk, array, chunkSize); + android::Res_png_9patch::deserialize(chunk); + + chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); + chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); + chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); + chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); + + for (int i = 0; i < chunk->numXDivs; i++) { + chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); + if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { + chunk->xDivs[i]++; + } + } + + for (int i = 0; i < chunk->numYDivs; i++) { + chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); + if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { + chunk->yDivs[i]++; + } + } + + memcpy(array, chunk, chunkSize); + + if (padding) { + GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, + chunk->paddingRight, chunk->paddingBottom); + } + + env->ReleaseByteArrayElements(chunkObject, array, 0); + } + return chunkObject; +} + /////////////////////////////////////////////////////////////////////////////// static JNINativeMethod gMethods[] = { @@ -502,7 +546,13 @@ static JNINativeMethod gMethods[] = { { "nativeDecodeByteArray", "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", (void*)nativeDecodeByteArray + }, + + { "nativeScaleNinePatch", + "([BFLandroid/graphics/Rect;)[B", + (void*)nativeScaleNinePatch } + }; static JNINativeMethod gOptionsMethods[] = { diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index b9e5f67067dc3..605e4b8af76df 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -93,7 +93,7 @@ public: SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); return canvas->getDevice()->accessBitmap(false).height(); } - + static void setViewport(JNIEnv* env, jobject, SkCanvas* canvas, int width, int height) { canvas->setViewport(width, height); @@ -454,13 +454,32 @@ public: #endif } - static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject, + static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas, SkCanvas* canvas, SkBitmap* bitmap, jfloat left, jfloat top, - SkPaint* paint) { + SkPaint* paint, + jboolean autoScale, jfloat densityScale) { SkScalar left_ = SkFloatToScalar(left); SkScalar top_ = SkFloatToScalar(top); - canvas->drawBitmap(*bitmap, left_, top_, paint); + + if (!autoScale || densityScale <= 0.0f) { + canvas->drawBitmap(*bitmap, left_, top_, paint); + } else { + canvas->save(); + SkScalar canvasScale = GraphicsJNI::getCanvasDensityScale(env, jcanvas); + SkScalar scale = canvasScale / SkFloatToScalar(densityScale); + canvas->scale(scale, scale); + + SkPaint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterBitmap(true); + + canvas->drawBitmap(*bitmap, left_, top_, &filteredPaint); + + canvas->restore(); + } } static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap, @@ -492,7 +511,7 @@ public: static void drawBitmapArray(JNIEnv* env, jobject, SkCanvas* canvas, jintArray jcolors, int offset, int stride, - int x, int y, int width, int height, + jfloat x, jfloat y, int width, int height, jboolean hasAlpha, SkPaint* paint) { SkBitmap bitmap; @@ -508,7 +527,8 @@ public: return; } - canvas->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint); + canvas->drawBitmap(bitmap, SkFloatToScalar(x), SkFloatToScalar(y), + paint); } static void drawBitmapMatrix(JNIEnv* env, jobject, SkCanvas* canvas, @@ -882,13 +902,13 @@ static JNINativeMethod gCanvasMethods[] = { {"native_drawRoundRect","(ILandroid/graphics/RectF;FFI)V", (void*) SkCanvasGlue::drawRoundRect}, {"native_drawPath","(III)V", (void*) SkCanvasGlue::drawPath}, - {"native_drawBitmap","(IIFFI)V", + {"native_drawBitmap","(IIFFIZF)V", (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint}, {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/RectF;I)V", (void*) SkCanvasGlue::drawBitmapRF}, {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/Rect;I)V", (void*) SkCanvasGlue::drawBitmapRR}, - {"native_drawBitmap", "(I[IIIIIIIZI)V", + {"native_drawBitmap", "(I[IIIFFIIZI)V", (void*)SkCanvasGlue::drawBitmapArray}, {"nativeDrawBitmapMatrix", "(IIII)V", diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index 65c2326a6cab6..a285def804a62 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -56,11 +56,14 @@ public: break; // eof } - const jbyte* array = env->GetByteArrayElements(fJavaByteArray, - NULL); - memcpy(buffer, array, n); - env->ReleaseByteArrayElements(fJavaByteArray, - const_cast(array), JNI_ABORT); + env->GetByteArrayRegion(fJavaByteArray, 0, n, + reinterpret_cast(buffer)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); + return 0; + } buffer = (void*)((char*)buffer + n); bytesRead += n; @@ -189,10 +192,15 @@ public: requested = fCapacity; } - jbyte* array = env->GetByteArrayElements(storage, NULL); - memcpy(array, buffer, requested); - env->ReleaseByteArrayElements(storage, array, 0); - + env->SetByteArrayRegion(storage, 0, requested, + reinterpret_cast(buffer)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("--- write:SetByteArrayElements threw an exception\n"); + return false; + } + fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, storage, 0, requested); if (env->ExceptionCheck()) { diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 44113e5b46bf1..6eebbdcfb5636 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -162,6 +162,7 @@ static jfieldID gBitmapConfig_nativeInstanceID; static jclass gCanvas_class; static jfieldID gCanvas_nativeInstanceID; +static jfieldID gCanvas_densityScaleID; static jclass gPaint_class; static jfieldID gPaint_nativeInstanceID; @@ -318,6 +319,13 @@ SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) { return c; } +SkScalar GraphicsJNI::getCanvasDensityScale(JNIEnv* env, jobject canvas) { + SkASSERT(env); + SkASSERT(canvas); + SkASSERT(env->IsInstanceOf(canvas, gCanvas_class)); + return SkFloatToScalar(env->GetFloatField(canvas, gCanvas_densityScaleID)); +} + SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) { SkASSERT(env); SkASSERT(paint); @@ -543,7 +551,8 @@ int register_android_graphics_Graphics(JNIEnv* env) gCanvas_class = make_globalref(env, "android/graphics/Canvas"); gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I"); - + gCanvas_densityScaleID = getFieldIDCheck(env, gCanvas_class, "mDensityScale", "F"); + gPaint_class = make_globalref(env, "android/graphics/Paint"); gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index e67b20b190cb7..e2dc9acf1beb3 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -38,6 +38,7 @@ public: static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap); static SkPicture* getNativePicture(JNIEnv*, jobject picture); static SkRegion* getNativeRegion(JNIEnv*, jobject region); + static SkScalar getCanvasDensityScale(JNIEnv*, jobject canvas); /** Return the corresponding native config from the java Config enum, or kNo_Config if the java object is null. diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 9e943f3e0db21..b11edfc07436e 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -47,19 +47,17 @@ public: static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint) { - const jbyte* array = env->GetByteArrayElements(chunkObj, 0); - if (array != NULL) { - size_t chunkSize = env->GetArrayLength(chunkObj); + size_t chunkSize = env->GetArrayLength(chunkObj); + void* storage = alloca(chunkSize); + env->GetByteArrayRegion(chunkObj, 0, chunkSize, + reinterpret_cast(storage)); + if (!env->ExceptionCheck()) { // need to deserialize the chunk - void* storage = alloca(chunkSize); Res_png_9patch* chunk = static_cast(storage); - memcpy(chunk, array, chunkSize); assert(chunkSize == chunk->serializedSize()); // this relies on deserialization being done in place Res_png_9patch::deserialize(chunk); NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); - env->ReleaseByteArrayElements(chunkObj, const_cast(array), - JNI_ABORT); } } @@ -102,23 +100,20 @@ public: SkRect bounds; GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); - const jbyte* array = (jbyte*)env->GetByteArrayElements(chunkObj, 0); - if (array != NULL) { - size_t chunkSize = env->GetArrayLength(chunkObj); + size_t chunkSize = env->GetArrayLength(chunkObj); + void* storage = alloca(chunkSize); + env->GetByteArrayRegion(chunkObj, 0, chunkSize, + reinterpret_cast(storage)); + if (!env->ExceptionCheck()) { // need to deserialize the chunk - void* storage = alloca(chunkSize); Res_png_9patch* chunk = static_cast(storage); - memcpy(chunk, array, chunkSize); assert(chunkSize == chunk->serializedSize()); // this relies on deserialization being done in place Res_png_9patch::deserialize(chunk); SkRegion* region = NULL; NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion); - env->ReleaseByteArrayElements(chunkObj, const_cast(array), - JNI_ABORT); return (jint)region; } - return 0; } diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index a81f2520bcd44..79965b94313c8 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -259,7 +259,7 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t } else { callback_flag = FRAME_CALLBACK_FLAG_NOOP; } - c->setFrameCallback(installed ? preview_callback : NULL, cookie, callback_flag); + c->setPreviewCallback(installed ? preview_callback : NULL, cookie, callback_flag); } static void autofocus_callback_impl(bool success, void *cookie) diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index e4586d9238e7a..307c6fdd89c93 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -63,11 +63,11 @@ struct audiorecord_callback_cookie { #define AUDIORECORD_ERROR -1 #define AUDIORECORD_ERROR_BAD_VALUE -2 #define AUDIORECORD_ERROR_INVALID_OPERATION -3 -#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -4 -#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT -5 -#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -6 -#define AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE -7 -#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -8 +#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16 +#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT -17 +#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18 +#define AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE -19 +#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20 jint android_media_translateRecorderErrorCode(int code) { switch(code) { diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 99785a28bdb9e..6bd365519567d 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -55,6 +55,8 @@ android_media_AudioSystem_setVolume(JNIEnv *env, jobject clazz, jint type, jint LOGV("setVolume(%d)", int(volume)); if (int(type) == AudioTrack::VOICE_CALL) { return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, float(volume) / 100.0)); + } else if (int(type) == AudioTrack::BLUETOOTH_SCO) { + return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, float(1.0))); } else { return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, AudioSystem::linearToLog(volume))); } diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 6bbcaeefca976..bbecc1b6a0cf7 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -53,6 +53,7 @@ struct fields_t { int STREAM_MUSIC; //... stream type constants int STREAM_ALARM; //... stream type constants int STREAM_NOTIFICATION; //... stream type constants + int STREAM_BLUETOOTH_SCO; //... stream type constants int MODE_STREAM; //... memory mode int MODE_STATIC; //... memory mode jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object @@ -95,13 +96,13 @@ class AudioTrackJniStorage { #define AUDIOTRACK_SUCCESS 0 #define AUDIOTRACK_ERROR -1 -#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -2 -#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT -3 -#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -4 -#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -5 -#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -6 -#define AUDIOTRACK_ERROR_BAD_VALUE -7 -#define AUDIOTRACK_ERROR_INVALID_OPERATION -8 +#define AUDIOTRACK_ERROR_BAD_VALUE -2 +#define AUDIOTRACK_ERROR_INVALID_OPERATION -3 +#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -16 +#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT -17 +#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -18 +#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -19 +#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -20 jint android_media_translateErrorCode(int code) { @@ -195,10 +196,12 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th atStreamType = AudioTrack::ALARM; } else if (streamType == javaAudioTrackFields.STREAM_NOTIFICATION) { atStreamType = AudioTrack::NOTIFICATION; + } else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) { + atStreamType = AudioTrack::BLUETOOTH_SCO; } else { LOGE("Error creating AudioTrack: unknown stream type."); return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE; - } + } // check the format. // This function was called from Java, so we compare the format against the Java constants @@ -662,6 +665,35 @@ static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobjec } +// ---------------------------------------------------------------------------- +// returns the minimum required size for the successful creation of a streaming AudioTrack +static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, + jint sampleRateInHertz, jint nbChannels, jint audioFormat) { + int afSamplingRate; + int afFrameCount; + uint32_t afLatency; + + if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { + return -1; + } + if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + return -1; + } + + if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) { + return -1; + } + + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate); + uint32_t minFrameCount = (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate; + int minBuffSize = minFrameCount + * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1) + * nbChannels; + return minBuffSize; +} + + // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -695,24 +727,27 @@ static JNINativeMethod gMethods[] = { {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, {"native_get_output_sample_rate", "()I", (void *)android_media_AudioTrack_get_output_sample_rate}, + {"native_get_min_buff_size", + "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, }; // field names found in android/media/AudioTrack.java -#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" -#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" -#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" -#define JAVA_CONST_BUFFER_COUNT_NAME "BUFFER_COUNT" -#define JAVA_CONST_STREAM_VOICE_CALL_NAME "STREAM_VOICE_CALL" -#define JAVA_CONST_STREAM_SYSTEM_NAME "STREAM_SYSTEM" -#define JAVA_CONST_STREAM_RING_NAME "STREAM_RING" -#define JAVA_CONST_STREAM_MUSIC_NAME "STREAM_MUSIC" -#define JAVA_CONST_STREAM_ALARM_NAME "STREAM_ALARM" -#define JAVA_CONST_STREAM_NOTIFICATION_NAME "STREAM_NOTIFICATION" -#define JAVA_CONST_MODE_STREAM_NAME "MODE_STREAM" -#define JAVA_CONST_MODE_STATIC_NAME "MODE_STATIC" -#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" -#define JAVA_JNIDATA_FIELD_NAME "mJniData" +#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" +#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" +#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" +#define JAVA_CONST_BUFFER_COUNT_NAME "BUFFER_COUNT" +#define JAVA_CONST_STREAM_VOICE_CALL_NAME "STREAM_VOICE_CALL" +#define JAVA_CONST_STREAM_SYSTEM_NAME "STREAM_SYSTEM" +#define JAVA_CONST_STREAM_RING_NAME "STREAM_RING" +#define JAVA_CONST_STREAM_MUSIC_NAME "STREAM_MUSIC" +#define JAVA_CONST_STREAM_ALARM_NAME "STREAM_ALARM" +#define JAVA_CONST_STREAM_NOTIFICATION_NAME "STREAM_NOTIFICATION" +#define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME "STREAM_BLUETOOTH_SCO" +#define JAVA_CONST_MODE_STREAM_NAME "MODE_STREAM" +#define JAVA_CONST_MODE_STATIC_NAME "MODE_STATIC" +#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" +#define JAVA_JNIDATA_FIELD_NAME "mJniData" #define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat" #define JAVA_AUDIOMANAGER_CLASS_NAME "android/media/AudioManager" @@ -810,28 +845,32 @@ int register_android_media_AudioTrack(JNIEnv *env) LOGE("Can't find %s", JAVA_AUDIOMANAGER_CLASS_NAME); return -1; } - if ( !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + if ( !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_VOICE_CALL_NAME, &(javaAudioTrackFields.STREAM_VOICE_CALL)) - || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_MUSIC_NAME, &(javaAudioTrackFields.STREAM_MUSIC)) - || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_SYSTEM_NAME, &(javaAudioTrackFields.STREAM_SYSTEM)) - || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_RING_NAME, &(javaAudioTrackFields.STREAM_RING)) || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_AUDIOMANAGER_CLASS_NAME, JAVA_CONST_STREAM_ALARM_NAME, &(javaAudioTrackFields.STREAM_ALARM)) || !android_media_getIntConstantFromClass(env, audioManagerClass, - JAVA_AUDIOMANAGER_CLASS_NAME, - JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION)) ) { + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION)) + || !android_media_getIntConstantFromClass(env, audioManagerClass, + JAVA_AUDIOMANAGER_CLASS_NAME, + JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME, + &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))) { // error log performed in android_media_getIntConstantFromClass() return -1; } - + return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_media_JetPlayer.cpp b/core/jni/android_media_JetPlayer.cpp index 994f16184c8c2..fe6094341886d 100644 --- a/core/jni/android_media_JetPlayer.cpp +++ b/core/jni/android_media_JetPlayer.cpp @@ -127,7 +127,7 @@ android_media_JetPlayer_release(JNIEnv *env, jobject thiz) // ---------------------------------------------------------------------------- static jboolean -android_media_JetPlayer_openFile(JNIEnv *env, jobject thiz, jstring path) +android_media_JetPlayer_loadFromFile(JNIEnv *env, jobject thiz, jstring path) { JetPlayer *lpJet = (JetPlayer *)env->GetIntField( thiz, javaJetPlayerFields.nativePlayerInJavaObj); @@ -139,7 +139,6 @@ android_media_JetPlayer_openFile(JNIEnv *env, jobject thiz, jstring path) // set up event callback function lpJet->setEventCallback(jetPlayerEventCallback); - const char *pathStr = env->GetStringUTFChars(path, NULL); if (pathStr == NULL) { // Out of memory LOGE("android_media_JetPlayer_openFile(): aborting, out of memory"); @@ -148,7 +147,7 @@ android_media_JetPlayer_openFile(JNIEnv *env, jobject thiz, jstring path) } LOGV("android_media_JetPlayer_openFile(): trying to open %s", pathStr ); - EAS_RESULT result = lpJet->openFile(pathStr); + EAS_RESULT result = lpJet->loadFromFile(pathStr); env->ReleaseStringUTFChars(path, pathStr); if(result==EAS_SUCCESS) { @@ -164,7 +163,8 @@ android_media_JetPlayer_openFile(JNIEnv *env, jobject thiz, jstring path) // ---------------------------------------------------------------------------- static jboolean -android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz) +android_media_JetPlayer_loadFromFileD(JNIEnv *env, jobject thiz, + jobject fileDescriptor, jlong offset, jlong length) { JetPlayer *lpJet = (JetPlayer *)env->GetIntField( thiz, javaJetPlayerFields.nativePlayerInJavaObj); @@ -173,6 +173,35 @@ android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz) "Unable to retrieve JetPlayer pointer for openFile()"); } + // set up event callback function + lpJet->setEventCallback(jetPlayerEventCallback); + + LOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" ); + EAS_RESULT result = lpJet->loadFromFD(getParcelFileDescriptorFD(env, fileDescriptor), + (long long)offset, (long long)length); // cast params to types used by EAS_FILE + + if(result==EAS_SUCCESS) { + LOGV("android_media_JetPlayer_openFileDescr(): file successfully opened"); + return JNI_TRUE; + } else { + LOGE("android_media_JetPlayer_openFileDescr(): failed to open file with EAS error %d", + (int)result); + return JNI_FALSE; + } +} + + +// ---------------------------------------------------------------------------- +static jboolean +android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz) +{ + JetPlayer *lpJet = (JetPlayer *)env->GetIntField( + thiz, javaJetPlayerFields.nativePlayerInJavaObj); + if (lpJet == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve JetPlayer pointer for closeFile()"); + } + if( lpJet->closeFile()==EAS_SUCCESS) { //LOGV("android_media_JetPlayer_closeFile(): file successfully closed"); return JNI_TRUE; @@ -191,7 +220,7 @@ android_media_JetPlayer_play(JNIEnv *env, jobject thiz) thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for play()"); } EAS_RESULT result = lpJet->play(); @@ -214,7 +243,7 @@ android_media_JetPlayer_pause(JNIEnv *env, jobject thiz) thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for pause()"); } EAS_RESULT result = lpJet->pause(); @@ -243,7 +272,7 @@ android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for queueSegment()"); } EAS_RESULT result @@ -269,7 +298,7 @@ android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for queueSegmentMuteArray()"); } EAS_RESULT result=EAS_FAILURE; @@ -314,7 +343,7 @@ android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for setMuteFlags()"); } EAS_RESULT result; @@ -338,7 +367,7 @@ android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for setMuteArray()"); } EAS_RESULT result=EAS_FAILURE; @@ -367,7 +396,8 @@ android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz, //LOGV("android_media_JetPlayer_setMuteArray(): mute flags successfully updated"); return JNI_TRUE; } else { - LOGE("android_media_JetPlayer_setMuteArray(): failed to update mute flags with EAS error code %ld", result); + LOGE("android_media_JetPlayer_setMuteArray(): \ + failed to update mute flags with EAS error code %ld", result); return JNI_FALSE; } } @@ -382,7 +412,7 @@ android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz, thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for setMuteFlag()"); } EAS_RESULT result; @@ -407,7 +437,7 @@ android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId) thiz, javaJetPlayerFields.nativePlayerInJavaObj); if (lpJet == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", - "Unable to retrieve JetPlayer pointer for openFile()"); + "Unable to retrieve JetPlayer pointer for triggerClip()"); } EAS_RESULT result; @@ -423,6 +453,29 @@ android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId) } +// ---------------------------------------------------------------------------- +static jboolean +android_media_JetPlayer_clearQueue(JNIEnv *env, jobject thiz) +{ + JetPlayer *lpJet = (JetPlayer *)env->GetIntField( + thiz, javaJetPlayerFields.nativePlayerInJavaObj); + if (lpJet == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve JetPlayer pointer for clearQueue()"); + } + + EAS_RESULT result = lpJet->clearQueue(); + if(result==EAS_SUCCESS) { + //LOGV("android_media_JetPlayer_clearQueue(): clearQueue successful"); + return JNI_TRUE; + } else { + LOGE("android_media_JetPlayer_clearQueue(): clearQueue failed with EAS error code %ld", + result); + return JNI_FALSE; + } +} + + // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -430,7 +483,10 @@ static JNINativeMethod gMethods[] = { {"native_setup", "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup}, {"native_finalize", "()V", (void *)android_media_JetPlayer_finalize}, {"native_release", "()V", (void *)android_media_JetPlayer_release}, - {"native_openJetFile", "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_openFile}, + {"native_loadJetFromFile", + "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_loadFromFile}, + {"native_loadJetFromFileD", "(Ljava/io/FileDescriptor;JJ)Z", + (void *)android_media_JetPlayer_loadFromFileD}, {"native_closeJetFile","()Z", (void *)android_media_JetPlayer_closeFile}, {"native_playJet", "()Z", (void *)android_media_JetPlayer_play}, {"native_pauseJet", "()Z", (void *)android_media_JetPlayer_pause}, @@ -442,6 +498,7 @@ static JNINativeMethod gMethods[] = { {"native_setMuteArray","([ZZ)Z", (void *)android_media_JetPlayer_setMuteArray}, {"native_setMuteFlag", "(IZZ)Z", (void *)android_media_JetPlayer_setMuteFlag}, {"native_triggerClip", "(I)Z", (void *)android_media_JetPlayer_triggerClip}, + {"native_clearQueue", "()Z", (void *)android_media_JetPlayer_clearQueue}, }; #define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj" diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index 722b5b8165ab3..c98207a58b57f 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -287,6 +287,24 @@ static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject clazz) return doBooleanCommand("DRIVER STOP", "OK"); } +static jboolean android_net_wifi_startPacketFiltering(JNIEnv* env, jobject clazz) +{ + return doBooleanCommand("DRIVER RXFILTER-ADD 0", "OK") + && doBooleanCommand("DRIVER RXFILTER-ADD 1", "OK") + && doBooleanCommand("DRIVER RXFILTER-START", "OK"); +} + +static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject clazz) +{ + jboolean result = doBooleanCommand("DRIVER RXFILTER-STOP", "OK"); + if (result) { + (void)doBooleanCommand("DRIVER RXFILTER-REMOVE 1", "OK"); + (void)doBooleanCommand("DRIVER RXFILTER-REMOVE 0", "OK"); + } + + return result; +} + static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) { char reply[256]; @@ -478,6 +496,8 @@ static JNINativeMethod gWifiMethods[] = { { "setScanModeCommand", "(Z)Z", (void*) android_net_wifi_setScanModeCommand }, { "startDriverCommand", "()Z", (void*) android_net_wifi_startDriverCommand }, { "stopDriverCommand", "()Z", (void*) android_net_wifi_stopDriverCommand }, + { "startPacketFiltering", "()Z", (void*) android_net_wifi_startPacketFiltering }, + { "stopPacketFiltering", "()Z", (void*) android_net_wifi_stopPacketFiltering }, { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand }, { "setNumAllowedChannelsCommand", "(I)Z", (void*) android_net_wifi_setNumAllowedChannelsCommand }, { "getNumAllowedChannelsCommand", "()I", (void*) android_net_wifi_getNumAllowedChannelsCommand }, diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 6ba949c421975..a7a0428a4f6f1 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -88,93 +88,98 @@ static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz) #endif } -static int read_mapinfo(FILE *fp, stats_t* stats) +static void read_mapinfo(FILE *fp, stats_t* stats) { char line[1024]; int len; - int skip; + bool skip, done = false; unsigned start = 0, size = 0, resident = 0, pss = 0; unsigned shared_clean = 0, shared_dirty = 0; unsigned private_clean = 0, private_dirty = 0; unsigned referenced = 0; + unsigned temp; int isNativeHeap; int isDalvikHeap; int isSqliteHeap; -again: - isNativeHeap = 0; - isDalvikHeap = 0; - isSqliteHeap = 0; - skip = 0; - - if(fgets(line, 1024, fp) == 0) return 0; + if(fgets(line, 1024, fp) == 0) return; - len = strlen(line); - if (len < 1) return 0; - line[--len] = 0; + while (!done) { + isNativeHeap = 0; + isDalvikHeap = 0; + isSqliteHeap = 0; + skip = false; - /* ignore guard pages */ - if (line[18] == '-') skip = 1; + len = strlen(line); + if (len < 1) return; + line[--len] = 0; - start = strtoul(line, 0, 16); + /* ignore guard pages */ + if (len > 18 && line[18] == '-') skip = true; - if (len >= 50) { - if (!strcmp(line + 49, "[heap]")) { + start = strtoul(line, 0, 16); + + if (strstr("[heap]", line)) { isNativeHeap = 1; - } else if (!strncmp(line + 49, "/dalvik-LinearAlloc", strlen("/dalvik-LinearAlloc"))) { + } else if (strstr("/dalvik-LinearAlloc", line)) { isDalvikHeap = 1; - } else if (!strncmp(line + 49, "/mspace/dalvik-heap", strlen("/mspace/dalvik-heap"))) { + } else if (strstr("/mspace/dalvik-heap", line)) { isDalvikHeap = 1; - } else if (!strncmp(line + 49, "/dalvik-heap-bitmap/", strlen("/dalvik-heap-bitmap/"))) { + } else if (strstr("/dalvik-heap-bitmap/", line)) { isDalvikHeap = 1; - } else if (!strncmp(line + 49, "/tmp/sqlite-heap", strlen("/tmp/sqlite-heap"))) { + } else if (strstr("/tmp/sqlite-heap", line)) { isSqliteHeap = 1; } - } - // TODO: This needs to be fixed to be less fragile. If the order of this file changes or a new - // line is add, this method will return without filling out any of the information. + while (true) { + if (fgets(line, 1024, fp) == 0) { + done = true; + break; + } - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Size: %d kB", &size) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Rss: %d kB", &resident) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Pss: %d kB", &pss) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Shared_Clean: %d kB", &shared_clean) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Shared_Dirty: %d kB", &shared_dirty) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Private_Clean: %d kB", &private_clean) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Private_Dirty: %d kB", &private_dirty) != 1) return 0; - if (fgets(line, 1024, fp) == 0) return 0; - if (sscanf(line, "Referenced: %d kB", &referenced) != 1) return 0; - - if (skip) { - goto again; - } + if (sscanf(line, "Size: %d kB", &temp) == 1) { + size = temp; + } else if (sscanf(line, "Rss: %d kB", &temp) == 1) { + resident = temp; + } else if (sscanf(line, "Pss: %d kB", &temp) == 1) { + pss = temp; + } else if (sscanf(line, "Shared_Clean: %d kB", &temp) == 1) { + shared_clean = temp; + } else if (sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) { + shared_dirty = temp; + } else if (sscanf(line, "Private_Clean: %d kB", &temp) == 1) { + private_clean = temp; + } else if (sscanf(line, "Private_Dirty: %d kB", &temp) == 1) { + private_dirty = temp; + } else if (sscanf(line, "Referenced: %d kB", &temp) == 1) { + referenced = temp; + } else if (strlen(line) > 40 && line[8] == '-' && line[17] == ' ') { + // looks like a new mapping + // example: "0000a000-00232000 rwxp 0000a000 00:00 0 [heap]" + break; + } + } - if (isNativeHeap) { - stats->nativePss += pss; - stats->nativePrivateDirty += private_dirty; - stats->nativeSharedDirty += shared_dirty; - } else if (isDalvikHeap) { - stats->dalvikPss += pss; - stats->dalvikPrivateDirty += private_dirty; - stats->dalvikSharedDirty += shared_dirty; - } else if (isSqliteHeap) { - // ignore - } else { - stats->otherPss += pss; - stats->otherPrivateDirty += shared_dirty; - stats->otherSharedDirty += private_dirty; + if (!skip) { + if (isNativeHeap) { + stats->nativePss += pss; + stats->nativePrivateDirty += private_dirty; + stats->nativeSharedDirty += shared_dirty; + } else if (isDalvikHeap) { + stats->dalvikPss += pss; + stats->dalvikPrivateDirty += private_dirty; + stats->dalvikSharedDirty += shared_dirty; + } else if ( isSqliteHeap) { + // ignore + } else { + stats->otherPss += pss; + stats->otherPrivateDirty += shared_dirty; + stats->otherSharedDirty += private_dirty; + } + } } - - return 1; } static void load_maps(int pid, stats_t* stats) @@ -185,10 +190,8 @@ static void load_maps(int pid, stats_t* stats) sprintf(tmp, "/proc/%d/smaps", pid); fp = fopen(tmp, "r"); if (fp == 0) return; - - while (read_mapinfo(fp, stats) != 0) { - // Do nothing - } + + read_mapinfo(fp, stats); fclose(fp); } diff --git a/core/jni/android_os_NetStat.cpp b/core/jni/android_os_NetStat.cpp deleted file mode 100644 index 983f719b0255a..0000000000000 --- a/core/jni/android_os_NetStat.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* //device/libs/android_runtime/android_os_Wifi.cpp -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "NetStat" - -#include "jni.h" -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#if HAVE_ANDROID_OS -#include -#endif - -namespace android { - -static jint android_os_netStatGetTxPkts(JNIEnv* env, jobject clazz) -{ - int ret = 0; - int fd = -1; - char input[50]; - - fd = open("/sys/class/net/rmnet0/statistics/tx_packets", O_RDONLY); - if (fd <= 0) { - fd = open("/sys/class/net/ppp0/statistics/tx_packets", O_RDONLY); - } - - if (fd > 0) { - int size = read(fd, input, 50); - if (size > 0) { - ret = atoi(input); - } - close(fd); - } - - return (jint)ret; -} - -static jint android_os_netStatGetRxPkts(JNIEnv* env, jobject clazz) -{ - int ret = 0; - int fd = -1; - char input[50]; - - fd = open("/sys/class/net/rmnet0/statistics/rx_packets", O_RDONLY); - if (fd <= 0) { - fd = open("/sys/class/net/ppp0/statistics/rx_packets", O_RDONLY); - } - - if (fd > 0) { - int size = read(fd, input, 50); - if (size > 0) { - ret = atoi(input); - } - close(fd); - } - - return (jint)ret; -} - -static jint android_os_netStatGetRxBytes(JNIEnv* env, jobject clazz) -{ - int ret = 0; - int fd = -1; - char input[50]; - - fd = open("/sys/class/net/rmnet0/statistics/rx_bytes", O_RDONLY); - if (fd <= 0) { - fd = open("/sys/class/net/ppp0/statistics/rx_bytes", O_RDONLY); - } - - if (fd > 0) { - int size = read(fd, input, 50); - if (size > 0) { - ret = atoi(input); - } - close(fd); - } - - return (jint)ret; -} - - -static jint android_os_netStatGetTxBytes(JNIEnv* env, jobject clazz) -{ - int ret = 0; - int fd = -1; - char input[50]; - - fd = open("/sys/class/net/rmnet0/statistics/tx_bytes", O_RDONLY); - if (fd <= 0) { - fd = open("/sys/class/net/ppp0/statistics/tx_bytes", O_RDONLY); - } - - if (fd > 0) { - int size = read(fd, input, 50); - if (size > 0) { - ret = atoi(input); - } - close(fd); - } - - return (jint)ret; -} - -// ---------------------------------------------------------------------------- - -/* - * JNI registration. - */ -static JNINativeMethod gMethods[] = { - /* name, signature, funcPtr */ - - { "netStatGetTxPkts", "()I", - (void*) android_os_netStatGetTxPkts }, - - { "netStatGetRxPkts", "()I", - (void*) android_os_netStatGetRxPkts }, - - { "netStatGetTxBytes", "()I", - (void*) android_os_netStatGetTxBytes }, - - { "netStatGetRxBytes", "()I", - (void*) android_os_netStatGetRxBytes }, - -}; - -int register_android_os_NetStat(JNIEnv* env) -{ - jclass netStat = env->FindClass("android/os/NetStat"); - LOG_FATAL_IF(netStat == NULL, "Unable to find class android/os/NetStat"); - - return AndroidRuntime::registerNativeMethods(env, - "android/os/NetStat", gMethods, NELEM(gMethods)); -} - -}; // namespace android - diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp index 61a4a26aad0ec..0936310ba06df 100644 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -428,36 +428,6 @@ static void disconnectRemoteDeviceNative(JNIEnv *env, jobject object, jstring ad #endif } -static jboolean isConnectableNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "IsConnectable", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static jboolean isDiscoverableNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "IsDiscoverable", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - static jstring getModeNative(JNIEnv *env, jobject object) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); @@ -495,26 +465,6 @@ static jboolean setModeNative(JNIEnv *env, jobject object, jstring mode) { return JNI_FALSE; } -static void common_Bonding(JNIEnv *env, jobject object, int timeout_ms, - const char *func, jstring address) { -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s", c_address); - DBusMessage *reply = - dbus_func_args_timeout(env, nat->conn, timeout_ms, nat->adapter, - DBUS_CLASS_NAME, func, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) { - dbus_message_unref(reply); - } - } -#endif -} - static jboolean createBondingNative(JNIEnv *env, jobject object, jstring address, jint timeout_ms) { LOGV(__FUNCTION__); @@ -540,15 +490,49 @@ static jboolean createBondingNative(JNIEnv *env, jobject object, return JNI_FALSE; } -static void cancelBondingProcessNative(JNIEnv *env, jobject object, +static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object, jstring address) { LOGV(__FUNCTION__); - common_Bonding(env, object, -1, "CancelBondingProcess", address); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, -1, nat->adapter, + DBUS_CLASS_NAME, "CancelBondingProcess", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + if (reply) { + dbus_message_unref(reply); + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; } -static void removeBondingNative(JNIEnv *env, jobject object, jstring address) { +static jboolean removeBondingNative(JNIEnv *env, jobject object, jstring address) { LOGV(__FUNCTION__); - common_Bonding(env, object, -1, "RemoveBonding", address); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + LOGV("... address = %s", c_address); + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, -1, nat->adapter, + DBUS_CLASS_NAME, "RemoveBonding", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(address, c_address); + if (reply) { + dbus_message_unref(reply); + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; } static jobjectArray listBondingsNative(JNIEnv *env, jobject object) { @@ -660,14 +644,6 @@ static jboolean setNameNative(JNIEnv *env, jobject obj, jstring name) { return JNI_FALSE; } -static jstring getMajorClassNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetMajorClass"); -} - -static jstring getMinorClassNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetMinorClass"); -} - static jstring common_getRemote(JNIEnv *env, jobject object, const char *func, jstring address) { LOGV("%s:%s", __FUNCTION__, func); @@ -704,66 +680,6 @@ static jstring common_getRemote(JNIEnv *env, jobject object, const char *func, return NULL; } -static jstring getRemoteAliasNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteAlias", address); -} - -static jboolean setRemoteAliasNative(JNIEnv *env, jobject obj, - jstring address, jstring alias) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, obj); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - const char *c_alias = env->GetStringUTFChars(alias, NULL); - - LOGV("... address = %s alias = %s", c_address, c_alias); - - DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "SetRemoteAlias", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_STRING, &c_alias, - DBUS_TYPE_INVALID); - - env->ReleaseStringUTFChars(address, c_address); - env->ReleaseStringUTFChars(alias, c_alias); - if (reply) - { - dbus_message_unref(reply); - return JNI_TRUE; - } - return JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static jboolean clearRemoteAliasNative(JNIEnv *env, jobject obj, jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, obj); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "ClearRemoteAlias", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - - env->ReleaseStringUTFChars(address, c_address); - if (reply) - { - dbus_message_unref(reply); - return JNI_TRUE; - } - return JNI_FALSE; - } -#endif - return JNI_FALSE; -} - static jstring getRemoteVersionNative(JNIEnv *env, jobject obj, jstring address) { return common_getRemote(env, obj, "GetRemoteVersion", address); } @@ -780,14 +696,6 @@ static jstring getRemoteCompanyNative(JNIEnv *env, jobject obj, jstring address) return common_getRemote(env, obj, "GetRemoteCompany", address); } -static jstring getRemoteMajorClassNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteMajorClass", address); -} - -static jstring getRemoteMinorClassNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteMinorClass", address); -} - static jstring getRemoteNameNative(JNIEnv *env, jobject obj, jstring address) { return common_getRemote(env, obj, "GetRemoteName", address); } @@ -800,28 +708,6 @@ static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) { return common_getRemote(env, obj, "LastUsed", address); } -static jobjectArray getRemoteServiceClassesNative(JNIEnv *env, jobject object, - jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteServiceClasses", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} - static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); @@ -1070,8 +956,6 @@ static JNINativeMethod sMethods[] = { {"getAddressNative", "()Ljava/lang/String;", (void *)getAddressNative}, {"getNameNative", "()Ljava/lang/String;", (void*)getNameNative}, {"setNameNative", "(Ljava/lang/String;)Z", (void *)setNameNative}, - {"getMajorClassNative", "()Ljava/lang/String;", (void *)getMajorClassNative}, - {"getMinorClassNative", "()Ljava/lang/String;", (void *)getMinorClassNative}, {"getVersionNative", "()Ljava/lang/String;", (void *)getVersionNative}, {"getRevisionNative", "()Ljava/lang/String;", (void *)getRevisionNative}, {"getManufacturerNative", "()Ljava/lang/String;", (void *)getManufacturerNative}, @@ -1100,17 +984,11 @@ static JNINativeMethod sMethods[] = { {"removeBondingNative", "(Ljava/lang/String;)Z", (void *)removeBondingNative}, {"getRemoteNameNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteNameNative}, - {"getRemoteAliasNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteAliasNative}, - {"setRemoteAliasNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)setRemoteAliasNative}, - {"clearRemoteAliasNative", "(Ljava/lang/String;)Z", (void *)clearRemoteAliasNative}, {"getRemoteVersionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteVersionNative}, {"getRemoteRevisionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteRevisionNative}, {"getRemoteClassNative", "(Ljava/lang/String;)I", (void *)getRemoteClassNative}, {"getRemoteManufacturerNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteManufacturerNative}, {"getRemoteCompanyNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteCompanyNative}, - {"getRemoteMajorClassNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteMajorClassNative}, - {"getRemoteMinorClassNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteMinorClassNative}, - {"getRemoteServiceClassesNative", "(Ljava/lang/String;)[Ljava/lang/String;", (void *)getRemoteServiceClassesNative}, {"getRemoteServiceChannelNative", "(Ljava/lang/String;S)Z", (void *)getRemoteServiceChannelNative}, {"getRemoteFeaturesNative", "(Ljava/lang/String;)[B", (void *)getRemoteFeaturesNative}, {"lastSeenNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastSeenNative}, diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 3468265bd8bf6..75a0fbee23ebc 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -47,8 +47,6 @@ static jmethodID method_onRemoteDeviceDisappeared; static jmethodID method_onRemoteClassUpdated; static jmethodID method_onRemoteNameUpdated; static jmethodID method_onRemoteNameFailed; -static jmethodID method_onRemoteAliasChanged; -static jmethodID method_onRemoteAliasCleared; static jmethodID method_onRemoteDeviceConnected; static jmethodID method_onRemoteDeviceDisconnectRequested; static jmethodID method_onRemoteDeviceDisconnected; @@ -60,6 +58,8 @@ static jmethodID method_onGetRemoteServiceChannelResult; static jmethodID method_onPasskeyAgentRequest; static jmethodID method_onPasskeyAgentCancel; +static jmethodID method_onAuthAgentAuthorize; +static jmethodID method_onAuthAgentCancel; typedef event_loop_native_data_t native_data_t; @@ -85,7 +85,6 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V"); method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V"); method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V"); - method_onRemoteAliasChanged = env->GetMethodID(clazz, "onRemoteAliasChanged", "(Ljava/lang/String;Ljava/lang/String;)V"); method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V"); method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V"); method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V"); @@ -96,6 +95,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V"); method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V"); + method_onAuthAgentAuthorize = env->GetMethodID(clazz, "onAuthAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); + method_onAuthAgentCancel = env->GetMethodID(clazz, "onAuthAgentCancel", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); @@ -139,12 +140,12 @@ static void cleanupNativeDataNative(JNIEnv* env, jobject object) { #ifdef HAVE_BLUETOOTH static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, void *data); -static DBusHandlerResult passkey_agent_event_filter(DBusConnection *conn, - DBusMessage *msg, - void *data); +static DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, + void *data); -static const DBusObjectPathVTable passkey_agent_vtable = { - NULL, passkey_agent_event_filter, NULL, NULL, NULL, NULL +static const DBusObjectPathVTable agent_vtable = { + NULL, agent_event_filter, NULL, NULL, NULL, NULL }; #endif @@ -193,9 +194,9 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { } // Add an object handler for passkey agent method calls - const char *path = "/android/bluetooth/PasskeyAgent"; + const char *path = "/android/bluetooth/Agent"; if (!dbus_connection_register_object_path(nat->conn, path, - &passkey_agent_vtable, NULL)) { + &agent_vtable, NULL)) { LOGE("%s: Can't register object path %s for agent!", __FUNCTION__, path); return JNI_FALSE; @@ -204,7 +205,7 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep // trying for 10 seconds. int attempt; - for (attempt = 1000; attempt > 0; attempt--) { + for (attempt = 0; attempt < 1000; attempt++) { DBusMessage *reply = dbus_func_args_error(env, nat->conn, &err, BLUEZ_DBUS_BASE_PATH, "org.bluez.Security", "RegisterDefaultPasskeyAgent", @@ -213,7 +214,8 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { if (reply) { // Success dbus_message_unref(reply); - return JNI_TRUE; + LOGV("Registered agent on attempt %d of 1000\n", attempt); + break; } else if (dbus_error_has_name(&err, "org.freedesktop.DBus.Error.ServiceUnknown")) { // hcid is still down, retry @@ -225,9 +227,25 @@ static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { return JNI_FALSE; } } - LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), " - "is hcid running?"); - return JNI_FALSE; + if (attempt == 1000) { + LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), " + "is hcid running?"); + return JNI_FALSE; + } + + // Now register the Auth agent + DBusMessage *reply = dbus_func_args_error(env, nat->conn, &err, + BLUEZ_DBUS_BASE_PATH, + "org.bluez.Security", "RegisterDefaultAuthorizationAgent", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + if (!reply) { + LOG_AND_FREE_DBUS_ERROR(&err); + return JNI_FALSE; + } + + dbus_message_unref(reply); + return JNI_TRUE; } #endif @@ -243,12 +261,19 @@ static void tearDownEventLoopNative(JNIEnv *env, jobject object) { DBusError err; dbus_error_init(&err); - const char *path = "/android/bluetooth/PasskeyAgent"; + const char *path = "/android/bluetooth/Agent"; DBusMessage *reply = dbus_func_args(env, nat->conn, BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "UnregisterDefaultPasskeyAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + "org.bluez.Security", "UnregisterDefaultPasskeyAgent", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + if (reply) dbus_message_unref(reply); + + reply = + dbus_func_args(env, nat->conn, BLUEZ_DBUS_BASE_PATH, + "org.bluez.Security", "UnregisterDefaultAuthorizationAgent", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); if (reply) dbus_message_unref(reply); dbus_connection_unregister_object_path(nat->conn, path); @@ -393,34 +418,6 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteAliasChanged")) { - char *c_address, *c_alias; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_STRING, &c_alias, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s, alias = %s", c_address, c_alias); - env->CallVoidMethod(nat->me, - method_onRemoteAliasChanged, - env->NewStringUTF(c_address), - env->NewStringUTF(c_alias)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteAliasCleared")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteAliasCleared, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "RemoteDeviceConnected")) { @@ -518,9 +515,8 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, } // Called by dbus during WaitForAndDispatchEventNative() -static DBusHandlerResult passkey_agent_event_filter(DBusConnection *conn, - DBusMessage *msg, - void *data) { +static DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, void *data) { native_data_t *nat = event_loop_nat; JNIEnv *env; @@ -573,11 +569,120 @@ static DBusHandlerResult passkey_agent_event_filter(DBusConnection *conn, env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel, env->NewStringUTF(address)); + // reply + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Release")) { - LOGE("We are no longer the passkey agent!"); + LOGW("We are no longer the passkey agent!"); + + // reply + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_method_call(msg, + "org.bluez.AuthorizationAgent", "Authorize")) { + const char *adapter; + const char *address; + const char *service; + const char *uuid; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &adapter, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &service, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + LOGV("... address = %s", address); + LOGV("... service = %s", service); + LOGV("... uuid = %s", uuid); + + bool auth_granted = env->CallBooleanMethod(nat->me, + method_onAuthAgentAuthorize, env->NewStringUTF(address), + env->NewStringUTF(service), env->NewStringUTF(uuid)); + + // reply + if (auth_granted) { + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + } else { + DBusMessage *reply = dbus_message_new_error(msg, + "org.bluez.Error.Rejected", "Authorization rejected"); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_method_call(msg, + "org.bluez.AuthorizationAgent", "Cancel")) { + const char *adapter; + const char *address; + const char *service; + const char *uuid; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &adapter, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &service, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + LOGV("... address = %s", address); + LOGV("... service = %s", service); + LOGV("... uuid = %s", uuid); + + env->CallVoidMethod(nat->me, + method_onAuthAgentCancel, env->NewStringUTF(address), + env->NewStringUTF(service), env->NewStringUTF(uuid)); + + // reply + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + + } else if (dbus_message_is_method_call(msg, + "org.bluez.AuthorizationAgent", "Release")) { + LOGW("We are no longer the auth agent!"); + + // reply + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) { + LOGE("%s: Cannot create message reply\n", __FUNCTION__); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_connection_send(nat->conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; } else { LOGV("... ignored"); } @@ -614,7 +719,8 @@ static jboolean waitForAndDispatchEventNative(JNIEnv *env, jobject object, #define BOND_RESULT_SUCCESS 0 #define BOND_RESULT_AUTH_FAILED 1 #define BOND_RESULT_AUTH_REJECTED 2 -#define BOND_RESULT_REMOTE_DEVICE_DOWN 3 +#define BOND_RESULT_AUTH_CANCELED 3 +#define BOND_RESULT_REMOTE_DEVICE_DOWN 4 void onCreateBondingResult(DBusMessage *msg, void *user) { LOGV(__FUNCTION__); @@ -637,7 +743,11 @@ void onCreateBondingResult(DBusMessage *msg, void *user) { // happens if either side presses 'cancel' at the pairing dialog. LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_AUTH_REJECTED; - } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".ConnectionAttemptFailed")) { + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationCanceled")) { + // Not sure if this happens + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_AUTH_CANCELED; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.ConnectionAttemptFailed")) { // Other device is not responding at all LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_REMOTE_DEVICE_DOWN; diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index add108053f87b..d147bcc883c28 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -44,6 +44,7 @@ static struct typedvalue_offsets_t jfieldID mAssetCookie; jfieldID mResourceId; jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; static struct assetfiledescriptor_offsets_t @@ -83,7 +84,11 @@ enum { static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags) + uint32_t typeSpecFlags, ResTable_config* config = NULL); + +jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config) { env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, @@ -93,6 +98,9 @@ static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, typeSpecFlags); + if (config != NULL) { + env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); + } return block; } @@ -703,13 +711,14 @@ static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject const ResTable& res(am->getResources()); Res_value value; + ResTable_config config; uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, &typeSpecFlags); + ssize_t block = res.getResource(ident, &value, false, &typeSpecFlags, &config); uint32_t ref = ident; if (resolve) { block = res.resolveReference(&value, block, &ref); } - return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; + return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config) : block; } static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, @@ -1648,6 +1657,8 @@ int register_android_content_AssetManager(JNIEnv* env) gTypedValueOffsets.mChangingConfigurations = env->GetFieldID(typedValue, "changingConfigurations", "I"); LOG_FATAL_IF(gTypedValueOffsets.mChangingConfigurations == NULL, "Unable to find TypedValue.changingConfigurations"); + gTypedValueOffsets.mDensity = env->GetFieldID(typedValue, "density", "I"); + LOG_FATAL_IF(gTypedValueOffsets.mDensity == NULL, "Unable to find TypedValue.density"); jclass assetFd = env->FindClass("android/content/res/AssetFileDescriptor"); LOG_FATAL_IF(assetFd == NULL, "Unable to find class android/content/res/AssetFileDescriptor"); diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index 8bacc74dc645b..fbbd85216608a 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/core/jni/server/com_android_server_HardwareService.cpp b/core/jni/server/com_android_server_HardwareService.cpp index 224ab186d5e49..ac3634858829f 100644 --- a/core/jni/server/com_android_server_HardwareService.cpp +++ b/core/jni/server/com_android_server_HardwareService.cpp @@ -28,21 +28,21 @@ namespace android { -static void on(JNIEnv *env, jobject clazz) +static void vibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms) { - // LOGI("on\n"); - vibrator_on(); + // LOGI("vibratorOn\n"); + vibrator_on(timeout_ms); } -static void off(JNIEnv *env, jobject clazz) +static void vibratorOff(JNIEnv *env, jobject clazz) { - // LOGI("off\n"); + // LOGI("vibratorOff\n"); vibrator_off(); } static JNINativeMethod method_table[] = { - { "on", "()V", (void*)on }, - { "off", "()V", (void*)off } + { "vibratorOn", "(J)V", (void*)vibratorOn }, + { "vibratorOff", "()V", (void*)vibratorOff } }; int register_android_os_Vibrator(JNIEnv *env) diff --git a/core/jni/server/com_android_server_KeyInputQueue.cpp b/core/jni/server/com_android_server_KeyInputQueue.cpp index 4e9ffb1e314de..63830d59c24ab 100644 --- a/core/jni/server/com_android_server_KeyInputQueue.cpp +++ b/core/jni/server/com_android_server_KeyInputQueue.cpp @@ -205,6 +205,25 @@ android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz, return st; } +static jboolean +android_server_KeyInputQueue_hasKeys(JNIEnv* env, jobject clazz, + jintArray keyCodes, jbooleanArray outFlags) +{ + jboolean ret = JNI_FALSE; + + int32_t* codes = env->GetIntArrayElements(keyCodes, NULL); + uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL); + size_t numCodes = env->GetArrayLength(keyCodes); + if (numCodes == env->GetArrayLength(outFlags)) { + gLock.lock(); + if (gHub != NULL) ret = gHub->hasKeys(numCodes, codes, flags); + gLock.unlock(); + } + + env->ReleaseBooleanArrayElements(outFlags, flags, 0); + env->ReleaseIntArrayElements(keyCodes, codes, 0); + return ret; +} // ---------------------------------------------------------------------------- @@ -233,6 +252,8 @@ static JNINativeMethod gInputMethods[] = { (void*) android_server_KeyInputQueue_getKeycodeState }, { "getKeycodeState", "(II)I", (void*) android_server_KeyInputQueue_getKeycodeStateDevice }, + { "hasKeys", "([I[Z)Z", + (void*) android_server_KeyInputQueue_hasKeys }, }; int register_android_server_KeyInputQueue(JNIEnv* env) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ded909f0c3ca6..0e8d3fd8a03fa 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -549,6 +549,13 @@ android:label="@string/permlab_mount_unmount_filesystems" android:description="@string/permdesc_mount_unmount_filesystems" /> + + + - - + @@ -902,6 +910,39 @@ android:description="@string/permdesc_checkinProperties" android:protectionLevel="signature" /> + + + + + + + + + + + + - + + + + + - - - - - - - - - - - - - - - - + diff --git a/core/res/res/anim/dialog_enter.xml b/core/res/res/anim/dialog_enter.xml index fd08cd03eae38..f48dd3797a493 100644 --- a/core/res/res/anim/dialog_enter.xml +++ b/core/res/res/anim/dialog_enter.xml @@ -20,10 +20,10 @@ - + android:duration="75" /> + android:duration="75" /> diff --git a/core/res/res/anim/dialog_exit.xml b/core/res/res/anim/dialog_exit.xml index e1b7a775978fa..24de6e7533685 100644 --- a/core/res/res/anim/dialog_exit.xml +++ b/core/res/res/anim/dialog_exit.xml @@ -19,10 +19,10 @@ --> - + android:duration="75" /> + android:duration="75"/> diff --git a/core/res/res/anim/input_method_enter.xml b/core/res/res/anim/input_method_enter.xml index 572a266f53fd3..6263c37649db3 100644 --- a/core/res/res/anim/input_method_enter.xml +++ b/core/res/res/anim/input_method_enter.xml @@ -20,12 +20,6 @@ - diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml index d28313af0bd4d..af9382c1fb361 100644 --- a/core/res/res/anim/input_method_exit.xml +++ b/core/res/res/anim/input_method_exit.xml @@ -19,12 +19,6 @@ --> - diff --git a/core/res/res/drawable/checkbox.xml b/core/res/res/anim/input_method_fancy_enter.xml similarity index 52% rename from core/res/res/drawable/checkbox.xml rename to core/res/res/anim/input_method_fancy_enter.xml index 7a221856226bf..15f5ad7d07ca7 100644 --- a/core/res/res/drawable/checkbox.xml +++ b/core/res/res/anim/input_method_fancy_enter.xml @@ -1,6 +1,6 @@ - - - - - - - + + + + diff --git a/core/res/res/drawable/radiobutton.xml b/core/res/res/anim/input_method_fancy_exit.xml similarity index 51% rename from core/res/res/drawable/radiobutton.xml rename to core/res/res/anim/input_method_fancy_exit.xml index a72c82598ac29..ecc5de3762edc 100644 --- a/core/res/res/drawable/radiobutton.xml +++ b/core/res/res/anim/input_method_fancy_exit.xml @@ -1,6 +1,6 @@ - - - - - - - + + + + diff --git a/core/res/res/drawable/radiobutton_background.xml b/core/res/res/anim/lock_screen_exit.xml similarity index 72% rename from core/res/res/drawable/radiobutton_background.xml rename to core/res/res/anim/lock_screen_exit.xml index 948a141dac25f..54d8e93c2acd4 100644 --- a/core/res/res/drawable/radiobutton_background.xml +++ b/core/res/res/anim/lock_screen_exit.xml @@ -1,6 +1,6 @@ - - - + + + diff --git a/core/res/res/anim/status_bar_enter.xml b/core/res/res/anim/status_bar_enter.xml index 2df1af4ccbcfa..b57d8e769d942 100644 --- a/core/res/res/anim/status_bar_enter.xml +++ b/core/res/res/anim/status_bar_enter.xml @@ -19,6 +19,8 @@ --> - - + + diff --git a/core/res/res/anim/status_bar_exit.xml b/core/res/res/anim/status_bar_exit.xml index c72d014421c57..8c6dc1c04bad4 100644 --- a/core/res/res/anim/status_bar_exit.xml +++ b/core/res/res/anim/status_bar_exit.xml @@ -19,6 +19,8 @@ --> - - + + diff --git a/core/res/res/drawable-land/statusbar_background.png b/core/res/res/drawable-land/statusbar_background.png index 9e82f752dd25a..8ecc24c59add0 100644 Binary files a/core/res/res/drawable-land/statusbar_background.png and b/core/res/res/drawable-land/statusbar_background.png differ diff --git a/core/res/res/drawable-land/title_bar.9.png b/core/res/res/drawable-land/title_bar.9.png new file mode 100644 index 0000000000000..63ad9169a5de9 Binary files /dev/null and b/core/res/res/drawable-land/title_bar.9.png differ diff --git a/core/res/res/drawable-land/title_bar_tall.png b/core/res/res/drawable-land/title_bar_tall.png new file mode 100644 index 0000000000000..670decc589c7f Binary files /dev/null and b/core/res/res/drawable-land/title_bar_tall.png differ diff --git a/core/res/res/drawable/btn_check_off.png b/core/res/res/drawable/btn_check_off.png index 56d3861542ea4..3a137d94a1fae 100644 Binary files a/core/res/res/drawable/btn_check_off.png and b/core/res/res/drawable/btn_check_off.png differ diff --git a/core/res/res/drawable/btn_check_off_disable.png b/core/res/res/drawable/btn_check_off_disable.png index 6c065bde1cf88..e012afd28ddd5 100644 Binary files a/core/res/res/drawable/btn_check_off_disable.png and b/core/res/res/drawable/btn_check_off_disable.png differ diff --git a/core/res/res/drawable/btn_check_off_disable_focused.png b/core/res/res/drawable/btn_check_off_disable_focused.png index cf23690371b73..47d225d671fb5 100644 Binary files a/core/res/res/drawable/btn_check_off_disable_focused.png and b/core/res/res/drawable/btn_check_off_disable_focused.png differ diff --git a/core/res/res/drawable/btn_check_off_pressed.png b/core/res/res/drawable/btn_check_off_pressed.png index 47c1a460f110f..984dfd750d306 100644 Binary files a/core/res/res/drawable/btn_check_off_pressed.png and b/core/res/res/drawable/btn_check_off_pressed.png differ diff --git a/core/res/res/drawable/btn_check_off_selected.png b/core/res/res/drawable/btn_check_off_selected.png index cf53075c0aad8..6854550393125 100644 Binary files a/core/res/res/drawable/btn_check_off_selected.png and b/core/res/res/drawable/btn_check_off_selected.png differ diff --git a/core/res/res/drawable/btn_check_on_disable.png b/core/res/res/drawable/btn_check_on_disable.png index 8e9f633e13ac0..6cb02f3e4674b 100644 Binary files a/core/res/res/drawable/btn_check_on_disable.png and b/core/res/res/drawable/btn_check_on_disable.png differ diff --git a/core/res/res/drawable/btn_check_on_disable_focused.png b/core/res/res/drawable/btn_check_on_disable_focused.png index 0abb0519f7a1d..4b6064dcabec1 100644 Binary files a/core/res/res/drawable/btn_check_on_disable_focused.png and b/core/res/res/drawable/btn_check_on_disable_focused.png differ diff --git a/core/res/res/drawable/btn_check_on_pressed.png b/core/res/res/drawable/btn_check_on_pressed.png index ee2175d047116..300d64afecb92 100644 Binary files a/core/res/res/drawable/btn_check_on_pressed.png and b/core/res/res/drawable/btn_check_on_pressed.png differ diff --git a/core/res/res/drawable/btn_check_on_selected.png b/core/res/res/drawable/btn_check_on_selected.png index 9356456577b43..0b36adbe27125 100644 Binary files a/core/res/res/drawable/btn_check_on_selected.png and b/core/res/res/drawable/btn_check_on_selected.png differ diff --git a/core/res/res/drawable/btn_default_normal.9.png b/core/res/res/drawable/btn_default_normal.9.png index ad1634ad273e0..a337eb5f546d6 100644 Binary files a/core/res/res/drawable/btn_default_normal.9.png and b/core/res/res/drawable/btn_default_normal.9.png differ diff --git a/core/res/res/drawable/btn_default_normal_disable.9.png b/core/res/res/drawable/btn_default_normal_disable.9.png index a89f37d82d9a2..e414d762dd64a 100644 Binary files a/core/res/res/drawable/btn_default_normal_disable.9.png and b/core/res/res/drawable/btn_default_normal_disable.9.png differ diff --git a/core/res/res/drawable/btn_default_normal_disable_focused.9.png b/core/res/res/drawable/btn_default_normal_disable_focused.9.png index b71dae9250fbe..b2d45e57a2ec3 100644 Binary files a/core/res/res/drawable/btn_default_normal_disable_focused.9.png and b/core/res/res/drawable/btn_default_normal_disable_focused.9.png differ diff --git a/core/res/res/drawable/btn_default_pressed.9.png b/core/res/res/drawable/btn_default_pressed.9.png index f496a86b86558..92d5ab546b90a 100644 Binary files a/core/res/res/drawable/btn_default_pressed.9.png and b/core/res/res/drawable/btn_default_pressed.9.png differ diff --git a/core/res/res/drawable/btn_default_selected.9.png b/core/res/res/drawable/btn_default_selected.9.png index c9e7e647a3e4f..5405fa02e4661 100644 Binary files a/core/res/res/drawable/btn_default_selected.9.png and b/core/res/res/drawable/btn_default_selected.9.png differ diff --git a/core/res/res/drawable/btn_default_small_normal.9.png b/core/res/res/drawable/btn_default_small_normal.9.png index faac205a9966f..963ecb3a6c165 100644 Binary files a/core/res/res/drawable/btn_default_small_normal.9.png and b/core/res/res/drawable/btn_default_small_normal.9.png differ diff --git a/core/res/res/drawable/btn_default_small_normal_disable.9.png b/core/res/res/drawable/btn_default_small_normal_disable.9.png index ce4fd287e680a..15bb123ecf9a5 100644 Binary files a/core/res/res/drawable/btn_default_small_normal_disable.9.png and b/core/res/res/drawable/btn_default_small_normal_disable.9.png differ diff --git a/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png b/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png index 1f04c18bd5431..a343515309086 100644 Binary files a/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png and b/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png differ diff --git a/core/res/res/drawable/btn_default_small_pressed.9.png b/core/res/res/drawable/btn_default_small_pressed.9.png index dab005bb452ff..8476e1227cd6c 100644 Binary files a/core/res/res/drawable/btn_default_small_pressed.9.png and b/core/res/res/drawable/btn_default_small_pressed.9.png differ diff --git a/core/res/res/drawable/btn_default_small_selected.9.png b/core/res/res/drawable/btn_default_small_selected.9.png index 5dec50465bc53..8ab8d63595e44 100644 Binary files a/core/res/res/drawable/btn_default_small_selected.9.png and b/core/res/res/drawable/btn_default_small_selected.9.png differ diff --git a/core/res/res/drawable/btn_keyboard_key_normal.9.png b/core/res/res/drawable/btn_keyboard_key_normal.9.png index 5c8a94514d9bc..7ba18dd25ac8f 100644 Binary files a/core/res/res/drawable/btn_keyboard_key_normal.9.png and b/core/res/res/drawable/btn_keyboard_key_normal.9.png differ diff --git a/core/res/res/drawable/btn_keyboard_key_normal_off.9.png b/core/res/res/drawable/btn_keyboard_key_normal_off.9.png index e6e640a54e9ec..bda9b83941ff2 100644 Binary files a/core/res/res/drawable/btn_keyboard_key_normal_off.9.png and b/core/res/res/drawable/btn_keyboard_key_normal_off.9.png differ diff --git a/core/res/res/drawable/btn_keyboard_key_normal_on.9.png b/core/res/res/drawable/btn_keyboard_key_normal_on.9.png index ce97a44140d51..0c16ed5093dfc 100644 Binary files a/core/res/res/drawable/btn_keyboard_key_normal_on.9.png and b/core/res/res/drawable/btn_keyboard_key_normal_on.9.png differ diff --git a/core/res/res/drawable/btn_keyboard_key_pressed.9.png b/core/res/res/drawable/btn_keyboard_key_pressed.9.png index f2e9f04511cb6..39b9314a1a699 100755 Binary files a/core/res/res/drawable/btn_keyboard_key_pressed.9.png and b/core/res/res/drawable/btn_keyboard_key_pressed.9.png differ diff --git a/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png b/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png index d541f95c9ecc8..bdcf06e1b8986 100644 Binary files a/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png and b/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png differ diff --git a/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png b/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png index 1cd6d459600e2..79621a9e6300c 100644 Binary files a/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png and b/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png differ diff --git a/core/res/res/drawable/btn_radio_off_pressed.png b/core/res/res/drawable/btn_radio_off_pressed.png index 41120ae82e21e..d6d8a9d4b6853 100644 Binary files a/core/res/res/drawable/btn_radio_off_pressed.png and b/core/res/res/drawable/btn_radio_off_pressed.png differ diff --git a/core/res/res/drawable/btn_radio_off_selected.png b/core/res/res/drawable/btn_radio_off_selected.png index 8b5535d0520e3..53f3e870e77da 100644 Binary files a/core/res/res/drawable/btn_radio_off_selected.png and b/core/res/res/drawable/btn_radio_off_selected.png differ diff --git a/core/res/res/drawable/btn_radio_on.png b/core/res/res/drawable/btn_radio_on.png index 7286b312aeea0..7bf696d8d075b 100644 Binary files a/core/res/res/drawable/btn_radio_on.png and b/core/res/res/drawable/btn_radio_on.png differ diff --git a/core/res/res/drawable/btn_radio_on_pressed.png b/core/res/res/drawable/btn_radio_on_pressed.png index 20ce0ec0055c4..c904a35c742bc 100644 Binary files a/core/res/res/drawable/btn_radio_on_pressed.png and b/core/res/res/drawable/btn_radio_on_pressed.png differ diff --git a/core/res/res/drawable/btn_radio_on_selected.png b/core/res/res/drawable/btn_radio_on_selected.png index ed53dc7f81ded..78e1fc0227ca3 100644 Binary files a/core/res/res/drawable/btn_radio_on_selected.png and b/core/res/res/drawable/btn_radio_on_selected.png differ diff --git a/core/res/res/drawable/checkbox_label_background.9.png b/core/res/res/drawable/checkbox_label_background.9.png deleted file mode 100644 index e6af4b073fe38..0000000000000 Binary files a/core/res/res/drawable/checkbox_label_background.9.png and /dev/null differ diff --git a/core/res/res/drawable/checkbox_off_background_focus_yellow.png b/core/res/res/drawable/checkbox_off_background_focus_yellow.png deleted file mode 100644 index ffde6f828b78f..0000000000000 Binary files a/core/res/res/drawable/checkbox_off_background_focus_yellow.png and /dev/null differ diff --git a/core/res/res/drawable/checkbox_on_background_focus_yellow.png b/core/res/res/drawable/checkbox_on_background_focus_yellow.png deleted file mode 100644 index 301800969c544..0000000000000 Binary files a/core/res/res/drawable/checkbox_on_background_focus_yellow.png and /dev/null differ diff --git a/core/res/res/drawable/dark_header.9.png b/core/res/res/drawable/dark_header.9.png new file mode 100644 index 0000000000000..8fa5f09152136 Binary files /dev/null and b/core/res/res/drawable/dark_header.9.png differ diff --git a/core/res/res/drawable/divider_horizontal_bright.9.png b/core/res/res/drawable/divider_horizontal_bright.9.png index a1ba2d33d9c0e..144fc224e9269 100644 Binary files a/core/res/res/drawable/divider_horizontal_bright.9.png and b/core/res/res/drawable/divider_horizontal_bright.9.png differ diff --git a/core/res/res/drawable/divider_horizontal_dark.9.png b/core/res/res/drawable/divider_horizontal_dark.9.png index 7b32381b108cf..08838cafa8153 100644 Binary files a/core/res/res/drawable/divider_horizontal_dark.9.png and b/core/res/res/drawable/divider_horizontal_dark.9.png differ diff --git a/core/res/res/drawable/divider_horizontal_dim_dark.9.png b/core/res/res/drawable/divider_horizontal_dim_dark.9.png index 20bc4dcf47b98..08838cafa8153 100644 Binary files a/core/res/res/drawable/divider_horizontal_dim_dark.9.png and b/core/res/res/drawable/divider_horizontal_dim_dark.9.png differ diff --git a/core/res/res/drawable/extract_edit_text.xml b/core/res/res/drawable/extract_edit_text.xml new file mode 100644 index 0000000000000..9c6c4ba860cdf --- /dev/null +++ b/core/res/res/drawable/extract_edit_text.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/core/res/res/drawable/ic_btn_speak_now.png b/core/res/res/drawable/ic_btn_speak_now.png new file mode 100644 index 0000000000000..83ee68b5d1f5c Binary files /dev/null and b/core/res/res/drawable/ic_btn_speak_now.png differ diff --git a/core/res/res/drawable/keyboard_background.9.png b/core/res/res/drawable/keyboard_background.9.png index 595acc587efe6..1d3ce05b86df8 100644 Binary files a/core/res/res/drawable/keyboard_background.9.png and b/core/res/res/drawable/keyboard_background.9.png differ diff --git a/core/res/res/drawable/keyboard_suggest_strip_shadow.9.png b/core/res/res/drawable/keyboard_suggest_strip_shadow.9.png new file mode 100644 index 0000000000000..d231ae686ec82 Binary files /dev/null and b/core/res/res/drawable/keyboard_suggest_strip_shadow.9.png differ diff --git a/core/res/res/drawable/keyboard_textfield_pressed.9.png b/core/res/res/drawable/keyboard_textfield_pressed.9.png new file mode 100644 index 0000000000000..f4e3f10fa8f84 Binary files /dev/null and b/core/res/res/drawable/keyboard_textfield_pressed.9.png differ diff --git a/core/res/res/drawable/keyboard_textfield_selected.9.png b/core/res/res/drawable/keyboard_textfield_selected.9.png new file mode 100644 index 0000000000000..6e703af76f49f Binary files /dev/null and b/core/res/res/drawable/keyboard_textfield_selected.9.png differ diff --git a/core/res/res/drawable/menu_background.9.png b/core/res/res/drawable/menu_background.9.png index e7266b2c0011e..ee99583594a24 100644 Binary files a/core/res/res/drawable/menu_background.9.png and b/core/res/res/drawable/menu_background.9.png differ diff --git a/core/res/res/drawable/radiobutton_label_background.9.png b/core/res/res/drawable/radiobutton_label_background.9.png deleted file mode 100644 index e6af4b073fe38..0000000000000 Binary files a/core/res/res/drawable/radiobutton_label_background.9.png and /dev/null differ diff --git a/core/res/res/drawable/radiobutton_off_background_focus_yellow.png b/core/res/res/drawable/radiobutton_off_background_focus_yellow.png deleted file mode 100644 index 1a092e363df13..0000000000000 Binary files a/core/res/res/drawable/radiobutton_off_background_focus_yellow.png and /dev/null differ diff --git a/core/res/res/drawable/radiobutton_on_background_focus_yellow.png b/core/res/res/drawable/radiobutton_on_background_focus_yellow.png deleted file mode 100644 index aa59771b38e35..0000000000000 Binary files a/core/res/res/drawable/radiobutton_on_background_focus_yellow.png and /dev/null differ diff --git a/core/res/res/drawable/scrollbar_handle_horizontal.9.png b/core/res/res/drawable/scrollbar_handle_horizontal.9.png index f333733f85c98..324b4bd152868 100755 Binary files a/core/res/res/drawable/scrollbar_handle_horizontal.9.png and b/core/res/res/drawable/scrollbar_handle_horizontal.9.png differ diff --git a/core/res/res/drawable/scrollbar_handle_vertical.9.png b/core/res/res/drawable/scrollbar_handle_vertical.9.png index ff0829503c417..3519d68bafb78 100755 Binary files a/core/res/res/drawable/scrollbar_handle_vertical.9.png and b/core/res/res/drawable/scrollbar_handle_vertical.9.png differ diff --git a/core/res/res/drawable/statusbar_background.png b/core/res/res/drawable/statusbar_background.png index 6af7329fab945..945ad92ad209b 100644 Binary files a/core/res/res/drawable/statusbar_background.png and b/core/res/res/drawable/statusbar_background.png differ diff --git a/core/res/res/drawable/textfield_default.9.png b/core/res/res/drawable/textfield_default.9.png index ab99aebc18f11..12cdd4c981d20 100644 Binary files a/core/res/res/drawable/textfield_default.9.png and b/core/res/res/drawable/textfield_default.9.png differ diff --git a/core/res/res/drawable/textfield_disabled.9.png b/core/res/res/drawable/textfield_disabled.9.png index 00701587a8a56..ad8ba70735c6a 100644 Binary files a/core/res/res/drawable/textfield_disabled.9.png and b/core/res/res/drawable/textfield_disabled.9.png differ diff --git a/core/res/res/drawable/textfield_disabled_selected.9.png b/core/res/res/drawable/textfield_disabled_selected.9.png old mode 100755 new mode 100644 index 139d606ba9226..04b6b6bf0fd4d Binary files a/core/res/res/drawable/textfield_disabled_selected.9.png and b/core/res/res/drawable/textfield_disabled_selected.9.png differ diff --git a/core/res/res/drawable/textfield_pressed.9.png b/core/res/res/drawable/textfield_pressed.9.png index 7b1350f0885d4..bcc0152e0ccf3 100644 Binary files a/core/res/res/drawable/textfield_pressed.9.png and b/core/res/res/drawable/textfield_pressed.9.png differ diff --git a/core/res/res/drawable/textfield_selected.9.png b/core/res/res/drawable/textfield_selected.9.png index 7286ba599c196..47115b608303d 100644 Binary files a/core/res/res/drawable/textfield_selected.9.png and b/core/res/res/drawable/textfield_selected.9.png differ diff --git a/core/res/res/drawable/title_bar.9.png b/core/res/res/drawable/title_bar.9.png index 8d1833944f9b1..44c3179a5aa1e 100644 Binary files a/core/res/res/drawable/title_bar.9.png and b/core/res/res/drawable/title_bar.9.png differ diff --git a/core/res/res/drawable/title_bar_shadow.9.png b/core/res/res/drawable/title_bar_shadow.9.png new file mode 100644 index 0000000000000..0872366579748 Binary files /dev/null and b/core/res/res/drawable/title_bar_shadow.9.png differ diff --git a/core/res/res/drawable/title_bar_shadow.png b/core/res/res/drawable/title_bar_shadow.png deleted file mode 100644 index a7178144287fc..0000000000000 Binary files a/core/res/res/drawable/title_bar_shadow.png and /dev/null differ diff --git a/core/res/res/drawable/title_bar_tall.png b/core/res/res/drawable/title_bar_tall.png new file mode 100644 index 0000000000000..09f5447ef276f Binary files /dev/null and b/core/res/res/drawable/title_bar_tall.png differ diff --git a/core/res/res/drawable/zoom_ring_arrows.png b/core/res/res/drawable/zoom_ring_arrows.png new file mode 100644 index 0000000000000..e443de7bfe0b5 Binary files /dev/null and b/core/res/res/drawable/zoom_ring_arrows.png differ diff --git a/core/res/res/drawable/zoom_ring_thumb.png b/core/res/res/drawable/zoom_ring_thumb.png new file mode 100644 index 0000000000000..4cc5f6f32d64f Binary files /dev/null and b/core/res/res/drawable/zoom_ring_thumb.png differ diff --git a/core/res/res/drawable/zoom_ring_track.png b/core/res/res/drawable/zoom_ring_track.png new file mode 100644 index 0000000000000..91d91f47061fc Binary files /dev/null and b/core/res/res/drawable/zoom_ring_track.png differ diff --git a/core/res/res/layout/activity_list.xml b/core/res/res/layout/activity_list.xml new file mode 100644 index 0000000000000..2967f0f269f8d --- /dev/null +++ b/core/res/res/layout/activity_list.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/core/res/res/layout/activity_list_item_2.xml b/core/res/res/layout/activity_list_item_2.xml new file mode 100644 index 0000000000000..78eca026c2413 --- /dev/null +++ b/core/res/res/layout/activity_list_item_2.xml @@ -0,0 +1,25 @@ + + + + diff --git a/core/res/res/layout/expanded_menu_layout.xml b/core/res/res/layout/expanded_menu_layout.xml index cd4ea12e442c2..5d9877364d8be 100644 --- a/core/res/res/layout/expanded_menu_layout.xml +++ b/core/res/res/layout/expanded_menu_layout.xml @@ -16,5 +16,5 @@ diff --git a/core/res/res/layout/input_method.xml b/core/res/res/layout/input_method.xml index ec75cf1f9184b..a21bbe899c574 100644 --- a/core/res/res/layout/input_method.xml +++ b/core/res/res/layout/input_method.xml @@ -27,7 +27,7 @@ diff --git a/core/res/res/layout/input_method_extract_view.xml b/core/res/res/layout/input_method_extract_view.xml index f8a4bdee04799..3f68bd3d27f54 100644 --- a/core/res/res/layout/input_method_extract_view.xml +++ b/core/res/res/layout/input_method_extract_view.xml @@ -21,8 +21,10 @@ diff --git a/core/res/res/layout/js_prompt.xml b/core/res/res/layout/js_prompt.xml index 9ab9d09fee1e6..86974ba860df0 100644 --- a/core/res/res/layout/js_prompt.xml +++ b/core/res/res/layout/js_prompt.xml @@ -20,16 +20,17 @@ android:layout_height="wrap_content" android:gravity="center_horizontal" > - + - + - - - - - - - - - - - - - - -

+

Setting Up Application Signing

+ +

As you begin developing Android applications, you should understand that all +Android applications must be digitally signed before the system will install +them on the emulator or an actual device.

+ +

The Android build tools help you get started quickly by signing your .apk +files with a debug key, prior to installing them on the emulator. This means +that you can compile your application and install it on the emulator without +having to generate your own private key. However, please note that if you intend +to publish your application, you must sign the application with your +own private key, rather than the debug key generated by the SDK tools.

+ +

To sign your applications, the ADT plugin requires the Keytool utility +included in the JDK. To set up your development environment for +signing, all you need to do is make sure that Keytool is available on your +machine that the build tools know how to find it.

+ +

In most cases, you can tell the SDK build tools how to find Keytool by making +sure that +your JAVA_HOME environment variable is set and that it references a suitable +JDK. Alternatively, +you can add the JDK version of Keytool to your PATH variable.

+ +

If you are developing on a version of Linux that originally came with Gnu +Compiler for Java, +make sure that the system is using the JDK version of Keytool, rather than the +gcj version. +If keytool is already in your PATH, it might be pointing to a symlink at +/usr/bin/keytool. +In this case, check the symlink target to make sure that it points to the +keytool in the JDK.

+ +

In all cases, please read and understand Signing Your +Applications, which provides an overview of application signing on Android +and what it means to you as an Android application developer.

+ +

Running an Android Application

To run a compiled application, you will upload the .apk file to the /data/app/ directory @@ -122,7 +161,7 @@ activity_name: ActivityName can be used without DDMS, such as displaying CPU usage or screen refresh rate on the emulator.

  • Configure your IDE to attach to port 8700 for debugging. We - include information on how to set up Eclipse to debug - your project.
  • + include information on + how to set up Eclipse to debug your project. diff --git a/docs/html/guide/developing/tools/adb.jd b/docs/html/guide/developing/tools/adb.jd index 50fb0248bb333..b111047e60bf4 100644 --- a/docs/html/guide/developing/tools/adb.jd +++ b/docs/html/guide/developing/tools/adb.jd @@ -104,7 +104,7 @@ Emulator 2, adb: 5557 ...
    adb [-d|-e|-s <serialNumber>] <command> 
    -

    When you issue a command, the program invokes an adb client. The client is not specifically associated with any emulator instance, so if multiple emulators/devices are running, you need to use the -d option to specify the target instance to which the command should be directed. For more information about using this option, see Directing Commands to a Specific Emulator/Device Instance.

    +

    When you issue a command, the program invokes an adb client. The client is not specifically associated with any emulator instance, so if multiple emulators/devices are running, you need to use the -d option to specify the target instance to which the command should be directed. For more information about using this option, see Directing Commands to a Specific Emulator/Device Instance.

    diff --git a/docs/html/guide/developing/tools/adt.jd b/docs/html/guide/developing/tools/adt.jd index c3bd25505a519..f28b24c2af013 100644 --- a/docs/html/guide/developing/tools/adt.jd +++ b/docs/html/guide/developing/tools/adt.jd @@ -90,7 +90,7 @@ If you are having trouble downloading the ADT plugin after following the steps a If you are still unable to use Eclipse to download the ADT plugin, follow these steps to download and install the plugin from your computer:

      -
    1. Download the ADT zip file (do not unpack it). +
    2. Download the ADT zip file (do not unpack it).
    3. Follow steps 1 and 2 in the default install instructions (above).
    4. In Eclipse 3.3, click New Archive Site....
      In Eclipse 3.4, click Add Site..., then Archive... diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 3e01172842d0e..ee56ebb4e9f99 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -3,9 +3,9 @@
    5. Android Basics

    6. @@ -15,15 +15,16 @@
      • @@ -51,22 +76,22 @@
    7. Audio and Video
    8. -
    9. - +
    10. Location
    11. -
    12. - +
    13. @@ -86,7 +111,7 @@
    14. aapt
    15. adb
    16. activitycreator
    17. -
    18. ADT Plugin
    19. +
    20. aidl
    21. ddms
    22. dx
    23. @@ -98,8 +123,8 @@
    24. sqlite3
    25. Traceview
    26. -
    27. Instrumentation
    28. -
    29. JUnit
    30. + @@ -118,19 +143,10 @@
    31. Designing for Performance
    32. Designing for Responsiveness
    33. Designing for Seamlessness
    34. -
    35. User Interface Guidelines
    36. + - -
    37. Tutorials

      • Hello World
      • @@ -155,4 +171,4 @@ - \ No newline at end of file + diff --git a/docs/html/guide/index.jd b/docs/html/guide/index.jd index e7232356ff292..ecbf97bd40d68 100644 --- a/docs/html/guide/index.jd +++ b/docs/html/guide/index.jd @@ -1,62 +1,84 @@ -page.title=Get Started +page.title=The Developer's Guide @jd:body -

        Welcome to the Android Developer Guide! The Dev Guide is your conceptual and practical -introduction to developing applications for Android. With this guide, you're free to explore -which ever topics interest you, based on your goals and experience.

        +

        +Welcome to the Android Dev Guide! The Dev Guide is +a practical introduction to developing applications for Android. +It explores the concepts behind Android, the framework for +constructing an application, and the tools for developing, +testing, and publishing software for the platform. +

        -

        If you're new to Android, you're probably wondering what it takes to -write a "Hello, World" application. So here it is:

        +

        +The Dev Guide holds most of the documentation for the Android +platform, except for reference material on the framework API. +For API specifications, go to the +Reference tab above. +

        -
        -package com.example.hello;
        +

        +As you can see in the panel on the left, the Dev Guide is +divided into a handful of sections. They are: +

        -import android.app.Activity; -import android.os.Bundle; -import android.widget.TextView; +

        +
        Android Basics
        +
        An initial orientation to Android — what it is, +what it offers, and how your application fits in.
        -public class HelloWorld extends Activity { +
        Framework Topics
        +
        Discussions of particular parts of the Android framework +and API. For an overview of the framework, begin with +Application +Fundamentals. Then explore other topics — from +designing a user interface and setting up resources to storing +data and using permissions — as needed.
        - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - TextView tv = new TextView(this); - tv.setText("Hello, World"); - setContentView(tv); - } +
        Developing
        +
        Directions for using Android's development and debugging tools, +and for testing the results.
        -} -
        +
        Publishing
        +
        Instructions on how to prepare your application for deployment +and how to publish it when it's ready.
        -

        That's the only code you need to write!

        +
        Best Practices
        +
        Recommendations on preferred techniques for writing +applications that perform efficiently and work well for the +user.
        -

        Not convinced?

        -

        If you're still shouting "Show me the code!" then take a look at -more Android code in the Hello World Samples.

        +
        Tutorials and Samples
        +
        Step-by-step tutorials and sample code demonstrating how +an Android application is constructed.
        -

        Ready to start?

        -

        If you're already convinced you want to develop on Android, -then we'll teach you how to build and run this "Hello, World" application in the -Hello World Introduction.

        +
        Appendix
        +
        Reference information and specifications, as well as FAQs, +a glossary of terms, and other information.
        + -

        Ready for more?

        -

        Once you've got your feet wet (or just want to skip to the heart of Android), the following -sections of the Dev Guide will educate you on the Android ins, outs, idioms and techniques:

        -
          -
        • Android Basics: - Learn more about what Android is, what it offers, and how your application fits in.
        • -
        • Framework Topics: - Become well versed in the practical matters of developing on Android — from drawing a UI, to storing data, - to drawing 3D graphics, and much more.
        • -
        • Developing: - Learn more about developing with IDEs, using Android develop/debug tools, and testing.
        • -
        • Publishing: - Learn how to get you application out there, for the world to enjoy!
        • -
        • Best Practices: - Get some recommendations on preferred techniques to write the best applications.
        • -
        • Tutorials: - Get help doing some of the basics, step by step.
        • -
        • Appendix: - Flotsam and jetsam. Find some of those spare nuggets of information.
        • -
        +

        +The first step in programming for Android is downloading the SDK +(software development kit). For instructions and information about +the kit, go to the SDK tab above. +

        -

        For more help, you should consider joining one or more of the Android discussion groups. See the Community for more information.

        +

        +After you have the SDK, begin by looking over the Dev Guide. +If you want to start by getting a quick look at the code, the short +Hello World +tutorial walks you through a standard "Hello, World" application as +it would be written for the Android platform. The +Application +Fundamentals document is a good place to start for an +understanding of the application framework. +

        + + +

        +For additional help, consider joining one or more of the Android +discussion groups. Go to the +Community tab above +for more information. +

        + +

        To return to this page later, just click the "Dev Guide" tab while any Dev Guide page is loaded.

        \ No newline at end of file diff --git a/docs/html/guide/practices/design/seamlessness.jd b/docs/html/guide/practices/design/seamlessness.jd index caf0d6a0b6218..a6c164192b33d 100644 --- a/docs/html/guide/practices/design/seamlessness.jd +++ b/docs/html/guide/practices/design/seamlessness.jd @@ -164,7 +164,7 @@ interface they've come to expect. When designing your UIs, you should try and avoid rolling your own as much as possible. Instead, use a Theme. You can override or extend those parts of the theme that you need to, but at least you're starting from the same UI base as all the other applications. For all -the details, read Applying Styles and Themes.

        +the details, read Applying Styles and Themes.

        Design Your UI to Work with Multiple Screen Resolutions

        @@ -208,7 +208,6 @@ option when starting the emulator.

        Don't Assume Touchscreen or Keyboard

        -Keyboad Different Keystrokes for Different Folks

        Android will support a variety of handset form-factors. That's a fancy way of saying that some Android devices will have full "QWERTY" keyboards, while @@ -220,8 +219,6 @@ about specific keyboard layouts -- unless, of course, you're really interested in restricting your application so that it can only be used on those devices.

        -

        Assume the Network is Slow

        -

        Don't Assume Touchscreen or Keyboard

        Do Conserve the Device Battery

        A mobile device isn't very mobile if it's constantly plugged into the diff --git a/docs/html/guide/publishing/preparing.jd b/docs/html/guide/publishing/preparing.jd index 1f3c6243aaa2a..267cba285ac6d 100644 --- a/docs/html/guide/publishing/preparing.jd +++ b/docs/html/guide/publishing/preparing.jd @@ -175,7 +175,7 @@ MapView elements

        If your application uses one or more diff --git a/docs/html/guide/publishing/publishing.jd b/docs/html/guide/publishing/publishing.jd index 1a022ddf66303..aed244ed92f12 100644 --- a/docs/html/guide/publishing/publishing.jd +++ b/docs/html/guide/publishing/publishing.jd @@ -21,10 +21,10 @@ page.title=Publishing Your Applications

      • Publishing Upgrades on Android Market
      • Using Intents to Launch the Market Application
    - +

    See also

    diff --git a/docs/html/guide/publishing/versioning.jd b/docs/html/guide/publishing/versioning.jd index 1f4df0b0c7a39..d10626698a69e 100644 --- a/docs/html/guide/publishing/versioning.jd +++ b/docs/html/guide/publishing/versioning.jd @@ -13,12 +13,20 @@ page.title=Versioning Your Applications
  • Determine your versioning strategy early in the development process, including considerations for future releases.
  • +

    In this document

    + +
      +
    1. Setting Application Version
    2. +
    3. Specifying Minimum System API Version +
    + +

    See also

    1. Preparing to Publish Your Application
    2. Publishing On Android Market
    3. -
    4. The Manifest File
    5. +
    6. The AndroidManifest.xml File
    @@ -40,11 +48,17 @@ users. A publishing service may also need to check the application version to determine compatibility and establish upgrade/downgrade relationships. -

    The Android system itself does not ever check the version +

    The Android system itself does not ever check the application version information for an application, such as to enforce restrictions on upgrades, compatibility, and so on. Instead, only users or applications themselves are -responsible for enforcing any version restrictions.

    +responsible for enforcing any version restrictions for applications themselves.

    +

    The Android system does check any system version compatibility expressed +by an application in its manifest, in the minSdkVersion attribute. This +allows an application to specify the minimum system API with which is compatible. +For more information see Specifying Minimum System API Version. + +

    Setting Application Version

    To define the version information for your application, you set attributes in the application's manifest file. Two attributes are available, and you should always define values for both of them:

    @@ -111,8 +125,15 @@ applications use the {@link android.content.pm.PackageManager#getPackageInfo(java.lang.String, int)} method of {@link android.content.pm.PackageManager PackageManager}.

    +

    Specifying Minimum System API Version

    . +

    If your application requires a specific minimum version of the Android -platform, you can also specify that in the manifest file:

    +platform, you can specify that version as an API Level identifier +in the application's manifest file. Doing so ensures that your +application can only be installed on devices that +are running a compatible version of the Android system.

    + +

    To specify the minimum system version in the manifest, use this attribute:

    • android:minSdkVersion — An integer value corresponding to @@ -129,7 +150,6 @@ that your application is compatible with all platform versions.

    • To specify a minimum platform version for your application, add a <uses-sdk> element as a child of <manifest>, then define the -android:minSdkVersion as an attribute. Currently, only one platform -version has been released for mobile devices — that version is "1". For -this reason, you do not need to define this attribute in your application and, -at this point, defining it is not recommended.

      \ No newline at end of file +android:minSdkVersion as an attribute.

      + +

      For more information, also see the Android System Image 1.1 Version Notes.

      \ No newline at end of file diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd index 7118ceb01da3a..3e1501cb79fa5 100644 --- a/docs/html/guide/topics/fundamentals.jd +++ b/docs/html/guide/topics/fundamentals.jd @@ -17,6 +17,7 @@ page.title=Application Fundamentals
    • Application Components
      1. Activating components: intents
      2. +
      3. Shutting down components
      4. The manifest file
      5. Intent filters
    • @@ -27,8 +28,14 @@ page.title=Application Fundamentals
    • Clearing the stack
    • Starting tasks
    • -
    • Processes and Threads
    • -
    • Lifecycles +
    • Processes and Threads +
        +
      1. Processes
      2. +
      3. Threads
      4. +
      5. Remote procedure calls
      6. +
      7. Thread-safe methods
      8. +
    • +
    • Component Lifecycles
      1. Activity lifecycle
      2. Service lifecycle
      3. @@ -40,13 +47,14 @@ page.title=Application Fundamentals

        Android applications are written in the Java programming language. -The compiled Java code — along with data and -resource files required by the application and a manifest describing the -application — is bundled by the aapt tool into an Android package, -an archive file marked by an {@code .apk} suffix. This file is the vehicle -for distributing the application and installing it on mobile devices; it's -the file users download to their devices. All the code in a single -{@code .apk} file is considered to be one application. +The compiled Java code — along with any data and resource +files required by the application — is bundled by the +aapt +tool into an Android package, an archive file +marked by an {@code .apk} suffix. This file is the vehicle +for distributing the application and installing it on mobile devices; +it's the file users download to their devices. All the code in a +single {@code .apk} file is considered to be one application.

        @@ -76,7 +84,7 @@ in the same Linux process, sharing the same VM.

        -

        Application Components

        +

        Application Components

        A central feature of Android is that one application can make use of elements @@ -125,24 +133,31 @@ current activity start the next one. Each activity is given a default window to draw in. Typically, the window fills the screen, but it might be smaller than the screen and float on top of other windows. An activity can also make use of additional windows — -for example, a window that presents users with vital information when they -select a particular item on-screen, or a pop-up dialog that calls for a user -response in the midst of the activity. +for example, a pop-up dialog that calls for a user response in the midst of +the activity, or a window that presents users with vital information when they +select a particular item on-screen.

        The visual content of the window is provided by a hierarchy of views — objects derived from the base {@link android.view.View} class. Each view -draws in a particular rectangular space within the window and responds to user -actions directed at that space. Android has a number of ready-made views that -you can use — including buttons, text fields, scroll bars, menu items, -check boxes, and more. A view hierarchy is placed within the activity's -window by the {@link android.app.Activity#setContentView -Activity.setContentView()} method. The content view -is the View object at the root of the hierarchy. -(See Views and Layout -for more information on views and the heirarchy.) -

        +controls a particular rectangular space within the window. Parent views +contain and organize the layout of their children. Leaf views (those at the +bottom of the hierarchy) draw in the rectangles they control and respond to +user actions directed at that space. Thus, views are where the activity's +interaction with the user takes place. For example, a view might display +a small image and initiate an action when the user taps that image. Android +has a number of ready-made views that you can use — including buttons, +text fields, scroll bars, menu items, check boxes, and more. +

        + +

        +A view hierarchy is placed within an activity's window by the +{@link android.app.Activity#setContentView Activity.setContentView()} +method. The content view is the View object at the root of the hierarchy. +(See the separate User Interface +document for more information on views and the hierarchy.) +

        Services
        A service doesn't have a visual user interface, but rather runs in @@ -152,13 +167,13 @@ data over the network or calculate something and provide the result to activitie that need it. Each service extends the {@link android.app.Service} base class.

        -A good example is a media player playing songs from a play list. The player +A prime example is a media player playing songs from a play list. The player application would probably have one or more activities that allow the user to choose songs and start playing them. However, the music playback itself would not be handled by an activity because users will expect the music to keep playing even after they leave the player and begin something different. To keep the music going, the media player activity could start a service to run -in the background. The system will then keep the music playback service running +in the background. The system would then keep the music playback service running even after the activity that started it leaves the screen.

        @@ -180,8 +195,10 @@ user interface, they often spawn another thread for time-consuming tasks
        A broadcast receiver is a component that does nothing but receive and react to broadcast announcements. Many broadcasts originate in system code — for example, announcements that the timezone has changed, -that the battery is low, that the keyboard has been exposed, or that the user -changed a language preference. +that the battery is low, that a picture has been taken, or that the user +changed a language preference. Applications can also initiate broadcasts +— for example, to let other applications know that some data has been +downloaded to the device and is available for them to use.

        An application can have any number of broadcast receivers to respond to any @@ -227,19 +244,19 @@ is available, creating the instance if necessary.

        -

        Activating components: intents

        +

        Activating components: intents

        Content providers are activated when they're targeted by a request from a ContentResolver. The other three components — activities, services, and broadcast receivers — are activated by asynchronous messages called intents. An intent is an {@link android.content.Intent} -object that holds the content of the message. Among other things, it names -the action an activity or service is being requested to take and specifies -the URI of the data to act on. For broadcast receivers, it names the -action being announced. For example, it might convey a request for +object that holds the content of the message. For activities and services, +it names the action being requested and specifies the URI of the data to +act on, among other things. For example, it might convey a request for an activity to present an image to the user or let the user edit some -text. Or it might announce to interested broadcast receivers that the +text. For broadcast receivers, the Intent object names the action being +announced. For example, it might announce to interested parties that the camera button has been pressed.

        @@ -281,13 +298,19 @@ onStart()} method and passes it the Intent object.

        Similarly, an intent can be passed to {@link android.content.Context#bindService Context.bindService()} to establish an ongoing connection between the calling component and a -target service. It initiates the service if it's not already running. -The service receives the Intent object in +target service. The service receives the Intent object in an {@link android.app.Service#onBind onBind()} call. -For example, an activity might establish a connection with the music -playback service mentioned earlier so that it could provide the user -with an interface for controlling the playback. The activity would -call {@code bindService()} to set up that connection. +(If the service is not already running, {@code bindService()} can +optionally start it.) For example, an activity might establish a connection +with the music playback service mentioned earlier so that it can provide +the user with the means (a user interface) for controlling the playback. +The activity would call {@code bindService()} to set up that connection, +and then call methods defined by the service to affect the playback. +

        + +

        +A later section, Remote procedure calls, has more details +about binding to a service.

        @@ -304,12 +327,49 @@ android.content.BroadcastReceiver#onReceive onReceive()} methods.

        -For more on intent messages, see the separate article, Intents +For more on intent messages, see the separate article, +Intents and Intent Filters. +

        -

        The manifest file

        +

        Shutting down components

        + +

        +A content provider is active only while it's responding to a request from +a ContentResolver. And a broadcast receiver is active only while it's +responding to a broadcast message. So there's no need to explicitly shut +down these components. +

        + +

        +Activities, on the other hand, provide the user interface. They're +in a long-running conversation with the user and may remain active, +even when idle, as long as the conversation continues. Similarly, services +may also remain running for a long time. So Android has methods to shut +down activities and services in an orderly way: +

        + +
          +
        • An activity can be shut down by calling its +{@link android.app.Activity#finish finish()} method. One activity can +shut down another activity (one it started with {@code startActivityForResult()}) by +calling {@link android.app.Activity#finishActivity finishActivity()}.
        • + +
        • A service can be stopped by calling its +{@link android.app.Service#stopSelf stopSelf()} method, or by calling +{@link android.content.Context#stopService Context.stopService()}.
        • +
        + +

        +Components might also be shut down by the system when they are no longer being +used or when Android must reclaim memory for more active components. A later +section, Component Lifecycles, discusses this +possibility and its ramifications in more detail. +

        + + +

        The manifest file

        Before Android can start an application component, it must learn that @@ -344,8 +404,9 @@ components. For example, an activity might be declared as follows: </manifest>

        -The {@code name} attribute of the {@code <activity>} element -names the {@link android.app.Activity} subclass that implements the +The {@code name} attribute of the +<activity> +element names the {@link android.app.Activity} subclass that implements the activity. The {@code icon} and {@code label} attributes point to resource files containing an icon and label that can be displayed to users to represent the activity. @@ -353,24 +414,28 @@ to users to represent the activity.

        The other components are declared in a similar way — -{@code <service>} elements for services, {@code <receiver>} -elements for broadcast receivers, and {@code <provider>} elements -for content providers. Activities, services, and content providers that -are not declared in the manifest are not visible to the system and are -consequently never run. Broadcast receivers can be declared in the -manifest, or they can be created dynamically in code (as -{@link android.content.BroadcastReceiver} objects) -and registered with the system by calling {@link -android.content.Context#registerReceiver Context.registerReceiver()}. +<service> +elements for services, +<receiver> +elements for broadcast receivers, and +<provider> +elements for content providers. Activities, services, and content providers +that are not declared in the manifest are not visible to the system and are +consequently never run. However, broadcast receivers can either be +declared in the manifest, or they can be created dynamically in code +(as {@link android.content.BroadcastReceiver} objects) +and registered with the system by calling +{@link android.content.Context#registerReceiver Context.registerReceiver()}.

        For more on how to structure a manifest file for your application, see -The Manifest File. +The +AndroidManifest.xml File.

        -

        Intent filters

        +

        Intent filters

        An Intent object can explicitly name a target component. If it does, @@ -442,7 +507,7 @@ and Intent Filters.

        -

        Activities and Tasks

        +

        Activities and Tasks

        As noted earlier, one activity can start another, including one defined @@ -467,14 +532,14 @@ the one that is the focus for user actions. When one activity starts another, the new activity is pushed on the stack; it becomes the running activity. The previous activity remains in the stack. When the user presses the BACK key, the current activity is popped from the stack, and the previous one resumes as -the running activity. Activities in the stack are never rearranged, only -pushed and popped. +the running activity.

        The stack contains objects, so if a task has more than one instance of the same Activity subclass open — multiple map viewers, for example — the -stack has a separate entry for each instance. +stack has a separate entry for each instance. Activities in the stack are never +rearranged, only pushed and popped.

        @@ -505,7 +570,8 @@ The behavior just described is the default behavior for activities and tasks. But there are ways to modify almost all aspects of it. The association of activities with tasks, and the behavior of an activity within a task, is controlled by the interaction between flags set in the Intent object that -started the activity and attributes set in the activity's {@code <activity>} +started the activity and attributes set in the activity's +<activity> element in the manifest. Both requester and respondent have a say in what happens.

        @@ -529,19 +595,19 @@ The principal {@code <activity>} attributes are:

        The following sections describe what some of these flags and attributes do, -and how they interact. +how they interact, and what considerations should govern their use.

        -

        Affinities and new tasks

        +

        Affinities and new tasks

        By default, all the activities in an application have an affinity for each other — that is, there's a preference for them all to belong to the same task. However, an individual affinity can be set for each activity -with the {@code taskAffinity} attribute. Activities defined in different -applications can share an affinity, or activities defined in the same -application can be assigned different affinities. +with the {@code taskAffinity} attribute of the {@code <activity>} element. +Activities defined in different applications can share an affinity, or activities +defined in the same application can be assigned different affinities. The affinity comes into play in two circumstances: When the Intent object that launches an activity contains the {@code FLAG_ACTIVITY_NEW_TASK} flag, and when an activity has its {@code allowTaskReparenting} attribute set @@ -550,26 +616,28 @@ to "{@code true}".

        The {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag
        -
        As mentioned earlier, a new activity is, by default, launched into +
        As described earlier, a new activity is, by default, launched into the task of the activity that called {@code startActivity()}. It's pushed onto the same stack as the caller. However, if the Intent object passed to {@code startActivity()} contains the {@code FLAG_ACTIVITY_NEW_TASK} flag, the system looks for a different task to house the new activity. Often, as the name of the flag implies, it's a new task. However, it -doesn't have to be. If there's an existing task with the same affinity -as the new activity, the activity is launched into that task. If not, -it begins a new task.
        +doesn't have to be. If there's already an existing task with the same +affinity as the new activity, the activity is launched into that task. If +not, it begins a new task.
        -
        The {@code allowTaskReparenting} attribute
        -
        If an activity has its {@code allowTaskReparenting} attribute is -set to "{@code true}", it can move from the task it starts in to the task +
        The allowTaskReparenting +attribute
        +
        If an activity has its {@code allowTaskReparenting} attribute set +to "{@code true}", it can move from the task it starts in to the task it has an affinity for when that task comes to the fore. For example, suppose that an activity that reports weather conditions in selected cities is defined as part of a travel application. It has the same affinity as other activities in the same application (the default affinity) and it allows reparenting. One of your activities starts the weather reporter, so it initially belongs to the same task as -your activity. However, when the travel application, next comes forward, +your activity. However, when the travel application next comes forward, the weather reporter will be reassigned to and displayed with that task.
        @@ -580,65 +648,116 @@ affinities to the activities associated with each of them.

        -

        Launch modes

        +

        Launch modes

        There are four different launch modes that can be assigned to an {@code -<activity>} element's {@code launchMode} attribute: +<activity>} element's +launchMode +attribute:

        -

        "{@code standard}" (the default value) +

        "{@code standard}" (the default mode)
        "{@code singleTop}"
        "{@code singleTask}"
        "{@code singleInstance}"

        -The launch mode determines three things: +The modes differ from each other on these four points:

          -
        • Whether the activity can belong to a task that includes other -activities. The answer is yes for all the modes except -"{@code singleInstance}". A "{@code singleInstance}" activity is always -the only activity in its task. If it tries to launch another activity, -that activity is assigned to a different task — as if {@code -FLAG_ACTIVITY_NEW_TASK} was in the intent.
        • -
        • Whether the activity always begins a task. For "{@code singleTask}" -and "{@code singleInstance}" the answer is yes. They mark activities that -can only be the root activities of a task; they define a task. In contrast, -"{@code standard}" and "{@code singleTop}" activities can belong to any task. +

        • Which task will hold the activity that responds to the intent. +For the "{@code standard}" and "{@code singleTop}" modes, it's the task that +originated the intent (and called +{@link android.content.Context#startActivity startActivity()}) +— unless the Intent object contains the +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag. +In that case, a different task is chosen as described in the previous +section, Affinities and new tasks. + +

          +In contrast, the "{@code singleTask}" and "{@code singleInstance}" modes mark +activities that are always at the root of a task. They define a task; they're +never launched into another task. +

          + +
        • Whether there can be multiple instances of the activity. +A "{@code standard}" or "{@code singleTop}" activity can be instantiated +many times. They can belong to multiple tasks, and a given task can have +multiple instances of the same activity. +

          + +

          +In contrast, "{@code singleTask}" and "{@code singleInstance}" activities +are limited to just one instance. Since these activities are at the root +of a task, this limitation means that there is never more than a single +instance of the task on the device at one time. +

          + +
        • Whether the instance can have other activities in its task. +A "{@code singleInstance}" activity stands alone as the only activity in its +task. If it starts another activity, that activity will be launched into a +different task regardless of its launch mode — as if {@code +FLAG_ACTIVITY_NEW_TASK} was in the intent. In all other respects, the +"{@code singleInstance}" mode is identical to "{@code singleTask}".

          + +

          +The other three modes permit multiple activities to belong to the task. +A "{@code singleTask}" activity will always be the root activity of the task, +but it can start other activities that will be assigned to its +task. Instances of "{@code standard}" and "{@code singleTop}" +activities can appear anywhere in a stack.

        • -
        • Whether an existing instance of the activity can handle new -intents. The answer is yes for all the modes except "{@code standard}". -Existing "{@code singleTask}" and "{@code singleInstance}" activities -handle all new intents that come their way; a new instance is never -created. In the case of "{@code singleTask}", all other activities in -the task are popped from the stack, so that the root "{@code singleTask}" -activity is at the top and in position to respond to the intent. +

        • Whether a new instance of the class will be launched +to handle a new intent. For the default "{@code standard}" mode, a +new instance is created to respond to every new intent. Each instance +handles just one intent. For the "{@code singleTop}" mode, an existing +instance of the class is re-used to handle a new intent if it resides +at the top of the activity stack of the target task. If it does not +reside at the top, it is not re-used. Instead, a new instance +is created for the new intent and pushed on the stack. + +

          +For example, suppose a task's activity stack consists of root activity A with +activities B, C, and D on top in that order, so the stack is A-B-C-D. An intent +arrives for an activity of type D. If D has the default "{@code standard}" launch +mode, a new instance of the class is launched and the stack becomes A-B-C-D-D. +However, if D's launch mode is "{@code singleTop}", the existing instance is +expected to handle the new intent (since it's at the top of the stack) and the +stack remains A-B-C-D.

          -If a "{@code singleTop}" activity is at the -top of its stack, that object is expected to handle any new intents. -However, if it's farther down the stack, a new instance is created for -the intent and pushed on the stack. +If, on the other hand, the arriving intent is for an activity of type B, a new +instance of B would be launched no matter whether B's mode is "{@code standard}" +or "{@code singleTop}" (since B is not at the top of the stack), so the resulting +stack would be A-B-C-D-B.

          -In contrast, a new instance of a "{@code standard}" activity is always -created for each new intent. +As noted above, there's never more than one instance of a "{@code singleTask}" +or "{@code singleInstance}" activity, so that instance is expected to handle +all new intents. A "{@code singleInstance}" activity is always at the top of +the stack (since it is the only activity in the task), so it is always in +position to handle the intent. However, a "{@code singleTask}" activity may +or may not have other activities above it in the stack. If it does, it is not +in position to handle the intent, and the intent is dropped. (Even though the +intent is dropped, its arrival would have caused the task to come to the +foreground, where it would remain.)

        • +

        When an existing activity is asked to handle a new intent, the Intent -object is passed to the activity in an {@link android.app.Activity#onNewIntent -onNewIntent()} call. (The intent object that originally started the -activity can be retrieved by calling -{@link android.app.Activity#getIntent getIntent()}.) +object is passed to the activity in an +{@link android.app.Activity#onNewIntent onNewIntent()} call. +(The intent object that originally started the activity can be retrieved by +calling {@link android.app.Activity#getIntent getIntent()}.)

        @@ -651,12 +770,12 @@ return to what that instance was doing before the new intent arrived.

        For more on launch modes, see -The +The AndroidManifest.xml File

        -

        Clearing the stack

        +

        Clearing the stack

        If the user leaves a task for a long time, the system clears the task of all @@ -676,7 +795,7 @@ control this behavior and modify it:

        The {@code alwaysRetainTaskState} attribute
        If this attribute is set to "{@code true}" in the root activity of a task, the default behavior just described does not happen. -Activities are retained in the stack even after a long period.
        +The task retains all activities in its stack even after a long period.
        The {@code clearTaskOnLaunch} attribute
        If this attribute is set to "{@code true}" in the root activity of a task, @@ -690,7 +809,7 @@ initial state, even after a momentary absence.
        single activity, not an entire task. And it can cause any activity to go away, including the root activity. When it's set to "{@code true}", the activity remains part of the task only for the current session. If the user -leaves and then relaunches the task, it no longer is present. +leaves and then returns to the task, it no longer is present.

        @@ -700,7 +819,11 @@ android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP FLAG_ACTIVITY_CLEAR_TOP} flag, and the target task already has an instance of the type of activity that should handle the intent in its stack, all activities above that instance are cleared away so that it stands at the top of the stack and can respond -to the intent. +to the intent. +If the launch mode of the designated activity is "{@code standard}", it too +will be removed from the stack, and a new instance will be launched to handle +the incoming intent. That's because a new instance is always created for +a new intent when the launch mode is "{@code standard}".

        @@ -711,7 +834,7 @@ a position where it can respond to the intent.

        -

        Starting tasks

        +

        Starting tasks

        An activity is set up as the entry point for a task by giving it @@ -731,7 +854,7 @@ and then come back to it later. For this reason, the two launch modes that mark activities as always initiating a task, "{@code singleTask}" and "{@code singleInstance}", should be used only when the activity has a {@code MAIN} and {@code LAUNCHER} filter. -Imagine, for example, what could happen if the filter is missing. +Imagine, for example, what could happen if the filter is missing: An intent launches a "{@code singleTask}" activity, initiating a new task, and the user spends some time working in that task. The user then presses the HOME key. The task is now ordered behind and obscured by the home @@ -760,7 +883,7 @@ See Clearing the stack, earlier.

        -

        Processes and Threads

        +

        Processes and Threads

        When the first of an application's components needs to be run, Android @@ -769,12 +892,15 @@ all components of the application run in that process and thread.

        -However, you can arrange for components to run in other processes as well as -spawn additional threads: +However, you can arrange for components to run in other processes, and you +can spawn additional threads for any process.

        -
          -
        • The process where a component runs is controlled by the manifest file. + +

          Processes

          + +

          +The process where a component runs is controlled by the manifest file. The component elements — {@code <activity>}, {@code <service>}, {@code <receiver>}, and {@code <provider>} — each have a {@code process} attribute that can specify a process @@ -784,26 +910,27 @@ while others do not. They can also be set so that components of different applications run in the same process — provided that the applications share the same Linux user ID and are signed by the same authorities. The {@code <application>} element also has a {@code process} attribute, -for setting a default value that applies to all components.

        • - -
        • Threads are created in code using standard Java {@link java.lang.Thread} -objects. Android provides a number of convenience classes for managing threads -— {@link android.os.Looper} for running a message loop within a thread, -{@link android.os.Handler} for processing messages, and -{@link android.os.HandlerThread} for setting up a thread with a message loop.

          +for setting a default value that applies to all components. +

          -Even though you may confine your application to a single process, there may be -times when you will need to spawn a thread to do some background work. Since the -user interface must always be quick to respond to user actions, the -thread that hosts an activity should not also host time-consuming operations like -network downloads, or anything else that may not be completed quickly. -

        • -
        +All components are instantiated in the main thread of the specified +process, and system calls to the component are dispatched from that +thread. Separate threads are not created for each instance. Consequently, +methods that respond to those calls — methods like +{@link android.view.View#onKeyDown View.onKeyDown()} that report +user actions and the lifecycle notifications discussed later in the +Component Lifecycles section — always run in the +main thread of the process. This means +that no component should perform long or blocking operations (such as networking +operations or computation loops) when called by the system, since this will block +any other components also in the process. You can spawn separate threads for +long operations, as discussed under Threads, next. +

        Android may decide to shut down a process at some point, when memory is -low and required by other applications that are more immediately serving +low and required by other processes that are more immediately serving the user. Application components running in the process are consequently destroyed. A process is restarted for those components when there's again work for them to do. @@ -816,17 +943,176 @@ with activities that are no longer visible on screen than a process with visible activities. The decision whether to terminate a process, therefore, depends on the state of the components running in that process. Those states are the subject of -the next section, Lifecycles. +a later section, Component Lifecycles.

        -

        Lifecycles

        +

        Threads

        + +

        +Even though you may confine your application to a single process, there will +likely be times when you will need to spawn a thread to do some background +work. Since the user interface must always be quick to respond to user actions, +the thread that hosts an activity should not also host time-consuming operations +like network downloads. Anything that may not be completed quickly should be +assigned to a different thread. +

        + +

        +Threads are created in code using standard Java {@link java.lang.Thread} +objects. Android provides a number of convenience classes for managing +threads — {@link android.os.Looper} for running a message loop within +a thread, {@link android.os.Handler} for processing messages, and +{@link android.os.HandlerThread} for setting up a thread with a message loop. +

        + + +

        Remote procedure calls

        + +

        +Android has a lightweight mechanism for remote procedure calls (RPCs) +— where a method is called locally, but executed remotely (in another +process), with any result returned back to the caller. +This entails decomposing the method call and all its attendant data to a +level the operating system can understand, transmitting it from the local +process and address space to the remote process and address space, and +reassembling and reenacting the call there. Return values have to be +transmitted in the opposite direction. Android provides all the code +to do that work, so that you can concentrate on defining and implementing +the RPC interface itself. +

        + +

        +An RPC interface can include only methods. +All methods are executed synchronously (the local method blocks until the +remote method finishes), even if there is no return value. +

        + +

        +In brief, the mechanism works as follows: You'd begin by declaring the +RPC interface you want to implement using a simple IDL (interface definition +language). From that declaration, the +aidl +tool generates a Java interface definition that must be made available to +both the local and the remote process. It contains two inner class, as shown +in the following diagram: +

        + +

        +RPC mechanism. +

        + +

        +The inner classes have all the code needed to administer remote procedure +calls for the interface you declared with the IDL. +Both inner classes implement the {@link android.os.IBinder} +interface. One of them is used locally and internally by the system; +the code you write can ignore it. +The other, called Stub, extends the {@link android.os.Binder} +class. In addition to internal code for effectuating the IPC calls, it +contains declarations for the methods in the RPC interface you declared. +You would subclass Stub to implement those methods, as indicated in the +diagram. +

        + +

        +Typically, the remote process would be managed by a service (because a +service can inform the system about the process and its connections to +other processes). It would have both the interface file generated by +the {@code aidl} tool and the Stub subclass implementing the +RPC methods. Clients of the service would have only the interface file +generated by the {@code aidl} tool. +

        + +

        +Here's how a connection between a service and its clients is set up: +

        + +
          +
        • Clients of the service (on the local side) would implement +{@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} and +{@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()} methods so they can be notified +when a successful connection to the remote service is established, and +when it goes away. They would then call +{@link android.content.Context#bindService bindService()} +to set up the connection. +
        • + +
        • +The service's {@link android.app.Service#onBind onBind()} +method would be implemented to either accept or reject the connection, +depending on the intent it receives (the intent passed to +{@code bindService()}). If the connection is accepted, it returns +an instance of the Stub subclass. +
        • + +
        • If the service accepts the connection, Android calls the +client's {@code onServiceConnected()} method and passes it an IBinder +object, a proxy for the Stub subclass managed by the service. Through +the proxy, the client can make calls on the remote service. +
        • +
        + +

        +This brief description omits some details of the RPC mechanism. For more +information, see +Designing a Remote +Interface Using AIDL and the {@link android.os.IBinder IBinder} class +description. +

        + + +

        Thread-safe methods

        + +

        +In a few contexts, the methods you implement may be called from more +than one thread, and therefore must be written to be thread-safe. +

        + +

        +This is primarily true for methods that can be called remotely — +as in the RPC mechanism discussed in the previous section. +When a call on a method implemented in an IBinder object originates +in the same process as the IBinder, the method is executed in the +caller's thread. However, when the call originates in another process, +the method is executed in a thread chosen from a pool of threads that +Android maintains in the same process as the IBinder; it's not executed +in the main thread of the process. For example, whereas a service's +{@code onBind()} method would be called from the main thread of the +service's process, methods implemented in the object that {@code onBind()} +returns (for example, a Stub subclass that implements RPC methods) would +be called from threads in the pool. +Since services can have more than one client, more than one pool thread +can engage the same IBinder method at the same time. IBinder methods +must, therefore, be implemented to be thread-safe. +

        + +

        +Similarly, a content provider can receive data requests that originate in +other processes. Although the ContentResolver and ContentProvider classes +hide the details of how the interprocess communication is managed, +ContentProvider methods that respond to those requests — the methods +{@link android.content.ContentProvider#query query()}, +{@link android.content.ContentProvider#insert insert()}, +{@link android.content.ContentProvider#delete delete()}, +{@link android.content.ContentProvider#update update()}, and +{@link android.content.ContentProvider#getType getType()} +— are called from a pool of threads in the content provider's +process, not the main thread of the process. Since these methods +may be called from any number of threads at the same time, they too must +be implemented to be thread-safe. +

        + + +

        Component Lifecycles

        Application components have a lifecycle — a beginning when Android instantiates them to respond to intents through to an end when the instances are destroyed. In between, they may sometimes be active -or inactive, or, in the case of activities, visible to the user or +or inactive,or, in the case of activities, visible to the user or invisible. This section discusses the lifecycles of activities, services, and broadcast receivers — including the states that they can be in during their lifetimes, the methods that notify you of transitions @@ -835,7 +1121,7 @@ the process hosting them might be terminated and the instances destroyed.

        -

        Activity lifecycle

        +

        Activity lifecycle

        An activity has essentially three states:

        @@ -864,12 +1150,10 @@ method), or simply killing its process. When it is displayed again to the user, it must be completely restarted and restored to its previous state.

        - -

        Lifecycle methods

        -

        As an activity transitions from state to state, it is notified of the change by calls to the following protected methods: +

        {@code void onCreate(Bundle savedInstanceState)}
        {@code void onStart()} @@ -881,9 +1165,9 @@ by calls to the following protected methods:

        All of these methods are hooks that you can override to do appropriate work -when the state changes. -All activities must implement {@link android.app.Activity#onCreate -onCreate()} to do initial setup when the activity is first instantiated. +when the state changes. All activities must implement +{@link android.app.Activity#onCreate onCreate()} to do the +initial setup when the object is first instantiated. Many will also implement {@link android.app.Activity#onPause onPause()} to commit data changes and otherwise prepare to stop interacting with the user.

        @@ -950,7 +1234,7 @@ can be in. The square rectangles represent the callback methods you can impleme to perform operations when the activity transitions between states.

        -

        State diagram for an Android activity lifecycle.

        @@ -1084,7 +1368,7 @@ extreme and dire circumstances when there is no other recourse.

        -

        Saving activity state

        +

        Saving activity state

        When the system, rather than the user, shuts down an activity to conserve @@ -1118,14 +1402,38 @@ return to the activity, so there's no reason to save its state.

        -Because {@code onSaveInstanceState()} is not always called, you -should use it only to record the transient state of the activity, -not to store persistent data. Use {@code onPause()} for that purpose -instead. +Because {@code onSaveInstanceState()} is not always called, you should +use it only to record the transient state of the activity, not to store +persistent data. Use {@code onPause()} for that purpose instead.

        -

        Service lifecycle

        +

        Coordinating activities

        + +

        +When one activity starts another, they both experience lifecycle +transitions. One pauses and may stop, while the other starts up. +On occasion, you may need to coordinate these activities, one with +the other. +

        + +

        +The order of lifecycle callbacks is well defined, +particularly when the two activities are in the same process: +

        + +
          +
        1. The current activity's {@code onPause()} method is called.
        2. + +
        3. Next, the starting activity's {@code onCreate()}, {@code onStart()}, +and {@code onResume()} methods are called in sequence.
        4. + +
        5. Then, if the starting activity is no longer visible +on screen, its {@code onStop()} method is called.
        6. +
        + + +

        Service lifecycle

        A service can be used in two ways: @@ -1243,11 +1551,11 @@ no matter how it's started, can potentially allow clients to bind to it, so any service may receive {@code onBind()} and {@code onUnbind()} calls.

        -

        State diagram for Service callbacks.

        -

        Broadcast receiver lifecycle

        +

        Broadcast receiver lifecycle

        A broadcast receiver has single callback method: @@ -1286,15 +1594,16 @@ The next section has more on the vulnerability of processes to being killed.

        -

        Processes and lifecycles

        +

        Processes and lifecycles

        The Android system tries to maintain an application process for as long as possible, but eventually it will need to remove old processes when memory runs low. To determine which processes to keep and which to kill, Android places each process into an "importance hierarchy" based on the -components running in it and the state of those components. There are -five levels in the hierarchy. The following list presents them in order -of importance: +components running in it and the state of those components. Processes +with the lowest importance are eliminated first, then those with the next +lowest, and so on. There are five levels in the hierarchy. The following +list presents them in order of importance:

          @@ -1348,16 +1657,15 @@ A visible process is considered extremely important and will not be killed unless doing so is required to keep all foreground processes running.

          -
        1. A service process is one that is running a service that has -been started with the +

        2. A service process is one that is running a service that +has been started with the {@link android.content.Context#startService startService()} -method. Although service processes are not directly tied to anything the +method and that does not fall into either of the two higher categories. +Although service processes are not directly tied to anything the user sees, they are generally doing things that the user cares about (such as playing an mp3 in the background or downloading data on the network), so the system keeps them running unless there's not enough -memory to retain them along with all foreground and visible processes. -(Note that a service can be ranked higher than this by virtue of being -bound to a visible or foreground activity). +memory to retain them along with all foreground and visible processes.

        3. A background process is one holding an activity @@ -1365,8 +1673,8 @@ that's not currently visible to the user (the Activity object's {@link android.app.Activity#onStop onStop()} method has been called). These processes have no direct impact on the user experience, and can be killed at any time to reclaim memory for a foreground, visible, or service process. -Usually there are many background processes running, -so they are kept in an LRU list to ensure that the process with the activity that +Usually there are many background processes running, so they are kept in an +LRU (least recently used) list to ensure that the process with the activity that was most recently seen by the user is the last to be killed. If an activity implements its lifecycle methods correctly, and captures its current state, killing its process will not have a deleterious effect on the user experience. diff --git a/docs/html/guide/topics/graphics/2d-graphics.jd b/docs/html/guide/topics/graphics/2d-graphics.jd index 822c66f480a1d..a72962e8c4327 100644 --- a/docs/html/guide/topics/graphics/2d-graphics.jd +++ b/docs/html/guide/topics/graphics/2d-graphics.jd @@ -87,6 +87,13 @@ protected void onCreate(Bundle savedInstanceState) { To do so, create a Drawable from the resource like so:

          Drawable myImage = Resources.getDrawable(R.drawable.my_image);
          +

          Caution: Each unique resource in your project can maintain only one +state, no matter how many different objects you may instantiate for it. For example, if you instantiate two +Drawable objects from the same image resource, then change a property (such as the alpha) for one of the +Drawables, then it will also affect the other. So when dealing with multiple instances of an image resource, +instead of directly transforming the Drawable, you should perform a tween animation.

          + +

          Example XML

          The XML snippet below shows how to add a resource Drawable to an {@link android.widget.ImageView} in the XML layout (with some red tint just for fun). @@ -103,8 +110,8 @@ To do so, create a Drawable from the resource like so:

          Creating from resource XML

          -

          By now, you should be familiar with Android's principles of -Views and Layout. Hence, you understand the power +

          By now, you should be familiar with Android's principles of developing a +User Interface. Hence, you understand the power and flexibility inherent in defining objects in XML. This philosophy caries over from Views to Drawables. If there is a Drawable object that you'd like to create, which is not initially dependent on variables defined by your applicaton code or user interaction, then defining the Drawable in XML is a good option. @@ -309,15 +316,28 @@ stretches to accommodate it.

          Tween Animation

          -

          A tweened animation can perform a series of simple transformations (position, size, rotation, and transparency) on +

          A tween animation can perform a series of simple transformations (position, size, rotation, and transparency) on the contents of a View object. So, if you have a TextView object, you can move, rotate, grow, or shrink the text. -If it has a background image, the background image will be transformed along with the text.

          +If it has a background image, the background image will be transformed along with the text. +The {@link android.view.animation animation package} provides all the classes used in a tween animation.

          -

          The animation is achieved with a sequence of animation instructions, defined in either XML or code. +

          A sequence of animation instructions defines the twen animation, defined by either XML or Android code. Like defining a layout, an XML file is recommended because it's more readable, reusable, and swappable -than hard-coding it. In the example below, we use XML. (To define an animation in code, refer to the +than hard-coding the animation. In the example below, we use XML. (To learn more about defining an animation +in your application code, instead of XML, refer to the {@link android.view.animation.AnimationSet} class and other {@link android.view.animation.Animation} subclasses.)

          +

          The animation instructions define the transformations that you want to occur, when they will occur, +and how long they should take to apply. Transformations can be sequential or simultaneous — +for example, you can have the contents of a TextView move from left to right, and then +rotate 180 degrees, or you can have the text move and rotate simultaneously. Each transformation +takes a set of parameters specific for that transformation (starting size and ending size +for size change, starting angle and ending angle for rotation, and so on), and +also a set of common parameters (for instance, start time and duration). To make +several transformations happen simultaneously, give them the same start time; +to make them sequential, calculate the start time plus the duration of the preceding transformation. +

          +

          The animation XML file belongs in the res/anim/ directory of your Android project. The file must have a single root element: this will be either a single <alpha>, <scale>, <translate>, <rotate>, interpolator element, @@ -389,22 +409,23 @@ spaceshipImage.startAnimation(hyperspaceJumpAnimation);

          As an alternative to startAnimation(), you can define a starting time for the animation with {@link android.view.animation.Animation#setStartTime(long) Animation.setStartTime()}, then assign the animation to the View with -{@link android.view.View#setAnimation(android.view.animation.Animation) View.setAnimation()}.

          +{@link android.view.View#setAnimation(android.view.animation.Animation) View.setAnimation()}. +

          For more information on the XML syntax, available tags and attributes, see the discussion on animation in the Available Resources.

          -

          Note: Animations are drawn in the area designated for the View at the start of -the animation; this area does not change to accommodate size or movement, so if your animation moves or expands -outside the original boundaries of your object, it will be clipped to the size of the original View, even if -the object's LayoutParams are set to WRAP_CONTENT (the object will not resize to accommodate moving or -expanding/shrinking animations).

          +

          Note: Regardless of how your animation may move or resize, the bounds of the +View that holds your animation will not automatically adjust to accomodate it. Even so, the animation will still +be drawn beyond the bounds of its View and will not be clipped. However, clipping will occur +if the animation exceeds the bounds of the parent View.

          Frame Animation

          This is a traditional animation in the sense that it is created with a sequence of different -images, played in order, like a roll of film.

          +images, played in order, like a roll of film. The {@link android.graphics.drawable.AnimationDrawable} +class is the basis for frame animations.

          While you can define the frames of an animation in your code, using the {@link android.graphics.drawable.AnimationDrawable} class API, it's more simply accomplished with a single XML @@ -454,6 +475,6 @@ called during the onCreate() method of your Activity, because the A to the window. If you want to play the animation immediately, without requiring interaction, then you might want to call it from the {@link android.app.Activity#onWindowFocusChanged(boolean) onWindowFocusChanged()} method in -your Activity, which will get called when Android brings your window into focus.

          +your Activity, which will get called when Android brings your window into focus.

          diff --git a/docs/html/guide/topics/graphics/index.jd b/docs/html/guide/topics/graphics/index.jd index 388acc9b09d7a..bc2a8bf7fca26 100644 --- a/docs/html/guide/topics/graphics/index.jd +++ b/docs/html/guide/topics/graphics/index.jd @@ -1,21 +1,203 @@ -page.title=2D and 3D Graphics +page.title=Graphics @jd:body - +

          Android graphics are powered by a custom 2D graphics library and OpenGL ES 1.0 -for 3D graphics.

          +for high performance 3D graphics. The most common 2D graphics APIs can be found in the +{@link android.graphics.drawable drawable package}. OpenGL APIs are available +from the Khronos {@link javax.microedition.khronos.opengles OpenGL ES package}, +plus some Android {@link android.opengl OpenGL utilities}.

          + +

          When starting a project, it's important to consider exactly what your graphical demands will be. +Varying graphical tasks are best accomplished with varying techniques. For example, graphics and animations +for a rather static application should be implemented much differently than graphics and animations +for an interactive game or 3D rendering.

          + +

          Here, we'll discuss a few of the options you have for drawing graphics on Android, +and which tasks they're best suited for.

          + +

          If you're specifically looking for information on drawing 3D graphics, this page won't +help a lot. However, the information below, on Drawing with a Canvas +(and the section on SurfaceView), +will give you a quick idea of how you should draw to the View hierarchy. For more information +on Android's 3D graphic utilities (provided by the OpenGL ES API), +read 3D with OpenGL and refer to other OpenGL documentation.

          + + +

          Consider your Options

          + +

          When drawing 2D graphics, you'll typically do so in one of two ways:

          +
            +
          1. Draw your graphics or animations into a View object from your layout. In this manner, + the drawing (and any animation) of your graphics is handled by the system's + normal View hierarchy drawing process — you simply define the graphics to go inside the View.
          2. +
          3. Draw your graphics directly to a Canvas. This way, you personally call the appropriate class's + draw() method (passing it your Canvas), or one of the Canvas draw...() methods (like + {@link android.graphics.Canvas#drawPicture(Picture,Rect) drawPicture()}). In doing so, you are also in + control of any animation.
          4. +
          + +

          Option "a," drawing to a View, is your best choice when you want to draw simple graphics that do not +need to change dynamically and are not part of a performance-intensive game. For example, you should +draw your graphics into a View when you want to display a static graphic or predefined animation, within +an otherwise static application. Read Simple Graphics Inside a View.

        4. + +

          Option "b," drawing to a Canvas, is better when you're application needs to regularly re-draw itself. +Basically, any video game should be drawing to the Canvas on its own. However, there's more than +one way to do this:

          +
            +
          • In the same thread as your UI Activity, wherein you create a custom View component in + your layout, call {@link android.view.View#invalidate()} and then handle the + {@link android.view.View#onDraw(Canvas) onDraw()} callback..
          • +
          • Or, in a separate thread, wherein you manage a {@link android.view.SurfaceView} and + perform draws to the Canvas as fast as your thread is capable + (you do not need to request invalidate()).
          • +
          +

          ...Begin by reading Draw with a Canvas.

          + +

          Simple Graphics Inside a View

          + +

          If you'll be drawing some simple graphics (images, shapes, colors, pre-defined animations, etc.), +then you should probably just draw to the background of a View or +to the content of an {@link android.widget.ImageView} in your layout. +In this case, you can skip the rest of this document and learn how to +draw graphics and animations in the 2D Graphics document. +

          + + +

          Draw with a Canvas

          + +

          When you're writing an application in which you would like to perform specialized drawing +and/or control the animation of graphics, +you should do so by drawing through a {@link android.graphics.Canvas}. A Canvas works for you as +a pretense, or interface, to the actual surface upon which your graphics will be drawn — it +holds all of your "draw" calls. Via the Canvas, your drawing is actually performed upon an +underlying {@link android.graphics.Bitmap}, which is placed into the window.

          + +

          In the event that you're drawing within the {@link android.view.View#onDraw(Canvas) onDraw()} +callback method, the Canvas is provided for you and you need only place your drawing calls upon it. +You can also acquire a Canvas from {@link android.view.SurfaceHolder#lockCanvas() SurfaceHolder.lockCanvas()}, +when dealing with a SurfaceView object. (Both of these scenarios are discussed in the following sections.) +However, if you need to create a new Canvas, then you must define the {@link android.graphics.Bitmap} +upon which drawing will actually be performed. The Bitmap is always required for a Canvas. You can set up +a new Canvas like this:

          +
          +Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
          +Canvas c = new Canvas(b);
          +
          + +

          Now your Canvas will draw onto the defined Bitmap. After drawing upon it with the Canvas, you can then carry your +Bitmap to another Canvas with one of the {@link android.graphics.Canvas#drawBitmap(Bitmap,Matrix,Paint) +Canvas.drawBitmap(Bitmap,...)} methods. It's recommended that you ultimately draw your final +graphics through a Canvas offered to you +by {@link android.view.View#onDraw(Canvas) View.onDraw()} or +{@link android.view.SurfaceHolder#lockCanvas() SurfaceHolder.lockCanvas()} (see the following sections).

          + +

          The {@link android.graphics.Canvas} class has its own set of drawing methods that you can use, +like drawBitmap(...), drawRect(...), drawText(...), and many more. +Other classes that you might use also have draw() methods. For example, you'll probably +have some {@link android.graphics.drawable.Drawable} objects that you want to put on the Canvas. Drawable +has its own {@link android.graphics.drawable.Drawable#draw(Canvas) draw()} method +that takes your Canvas as an arguement.

          + + +

          On a View

          + +

          If you're application does not require a significant amount of processing or +frame-rate speed (perhaps for a chess game, a snake game, +or another slowly-animated application), then you should consider creating a custom View component +and drawing with a Canvas in {@link android.view.View#onDraw(Canvas) View.onDraw()}. +The most convenient aspect of doing so is that the Android framework will +provide you with a pre-defined Canvas to which you will place your drawing calls.

          + +

          To start, extend the {@link android.view.View} class (or descendent thereof) and define +the {@link android.view.View#onDraw(Canvas) onDraw()} callback method. This method will be called by the Android +framework to request that your View draw itself. This is where you will perform all your calls +to draw through the {@link android.graphics.Canvas}, which is passed to you through the onDraw() callback.

          + +

          The Android framework will only call onDraw() as necessary. Each time that +your application is prepared to be drawn, you must request your View be invalidated by calling +{@link android.view.View#invalidate()}. This indicates that you'd like your View to be drawn and +Android will then call your onDraw() method (though is not guaranteed that the callback will +be instantaneous).

          + +

          Inside your View component's onDraw(), use the Canvas given to you for all your drawing, +using various Canvas.draw...() methods, or other class draw() methods that +take your Canvas as an argument. Once your onDraw() is complete, the Android framework will +use your Canvas to draw a Bitmap handled by the system.

          + +

          Note: In order to request an invalidate from a thread other than your main +Activity's thread, you must call {@link android.view.View#postInvalidate()}.

          + +

          Also read Building Custom Components +for a guide to extending a View class, and 2D Graphics: Drawables for +information on using Drawable objects like images from your resources and other primitive shapes.

          + +

          For a sample application, see the Snake game, in the SDK samples folder: +<your-sdk-directory>/samples/Snake/.

          + +

          On a SurfaceView

          + +

          The {@link android.view.SurfaceView} is a special subclass of View that offers a dedicated +drawing surface within the View hierarchy. The aim is to offer this drawing surface to +an application's secondary thread, so that the application isn't required +to wait until the system's View hierarchy is ready to draw. Instead, a secondary thread +that has reference to a SurfaceView can draw to its own Canvas at its own pace.

          + +

          To begin, you need to create a new class that extends {@link android.view.SurfaceView}. The class should also +implement {@link android.view.SurfaceHolder.Callback}. This subclass is an interface that will notify you +with information about the underlying {@link android.view.Surface}, such as when it is created, changed, or destroyed. +These events are important so that you know when you can start drawing, whether you need +to make adjustments based on new surface properties, and when to stop drawing and potentially +kill some tasks. Inside your SurfaceView class is also a good place to define your secondary Thread class, which will +perform all the drawing procedures to your Canvas.

          + +

          Instead of handling the Surface object directly, you should handle it via +a {@link android.view.SurfaceHolder}. So, when your SurfaceView is initialized, get the SurfaceHolder by calling +{@link android.view.SurfaceView#getHolder()}. You should then notify the SurfaceHolder that you'd +like to receive SurfaceHolder callbacks (from {@link android.view.SurfaceHolder.Callback}) by calling +{@link android.view.SurfaceHolder#addCallback(SurfaceHolder.Callback) addCallback()} +(pass it this). Then override each of the +{@link android.view.SurfaceHolder.Callback} methods inside your SurfaceView class.

          + +

          In order to draw to the Surface Canvas from within your second thread, you must pass the thread your SurfaceHandler +and retrieve the Canvas with {@link android.view.SurfaceHolder#lockCanvas() lockCanvas()}. +You can now take the Canvas given to you by the SurfaceHolder and do your necessary drawing upon it. +Once you're done drawing with the Canvas, call +{@link android.view.SurfaceHolder#unlockCanvasAndPost(Canvas) unlockCanvasAndPost()}, passing it +your Canvas object. The Surface will now draw the Canvas as you left it. Perform this sequence of locking and +unlocking the canvas each time you want to redraw.

          + +

          Note: On each pass you retrieve the Canvas from the SurfaceHolder, +the previous state of the Canvas will be retained. In order to properly animate your graphics, you must re-paint the +entire surface. For example, you can clear the previous state of the Canvas by filling in a color +with {@link android.graphics.Canvas#drawColor(int) drawColor()} or setting a background image +with {@link android.graphics.Canvas#drawBitmap(Bitmap,Rect,RectF,Paint) drawBitmap()}. Otherwise, +you will see traces of the drawings you previously performed.

          + + +

          For a sample application, see the Lunar Landar game, in the SDK samples folder: +<your-sdk-directory>/samples/LunarLander/. Or, +browse the source in the Sample Code section.

          + + + + + -

          2D Graphics

          -

          Android offers a custom 2D graphics library for drawing shapes and images.

          -

          The {@link android.graphics} and {@link android.graphics.drawable} -packages are where you'll find the classes used for drawing in two-dimensions. -For common drawing tasks, though, the {@link android.graphics.drawable} package -is where you'll find what you need.

          -

          For an introduction to drawing shapes and images, read the -2D Graphics document.

          -

          3D with OpenGL

          -

          High performance 3D graphic utilities are provided on Android with the OpenGL ES API. -You'll find the OpenGL APIs in the {@link android.opengl} package. -Read more about 3D with OpenGL.

          \ No newline at end of file diff --git a/docs/html/guide/topics/location/geo/mapkey.jd b/docs/html/guide/topics/location/geo/mapkey.jd index 110876f80412c..9aa824c1d61bb 100644 --- a/docs/html/guide/topics/location/geo/mapkey.jd +++ b/docs/html/guide/topics/location/geo/mapkey.jd @@ -1,28 +1,210 @@ -page.title=Obtaining a MapView API Key +page.title=Obtaining a Maps API Key @jd:body -

          {@link-fixme com.google.android.maps.MapView} is a very useful class that lets you easily integrate Google Maps into your application. It provides built-in map downloading, rendering, and caching, as well as a variety of display options and controls. It provides a wrapper around the Google Maps API that lets your application request and manipulate Google Maps data through class methods, and it lets you work with Maps data as you would other types of Views.

          + -

          Registering your application is simple, and has two parts:

          +

          com.google.android.maps.MapView is a very useful class that lets you easily integrate Google Maps into your application. It provides built-in map downloading, rendering, and caching of Maps tiles, as well as a variety of display options and controls. It provides a wrapper around the Google Maps API that lets your application request and manipulate Google Maps data through class methods, and it lets you work with Maps data as you would other types of Views.

          + +

          Because MapView gives you access to Google Maps data, you need to register with the Google Maps service and agree to the applicable Terms of Service before your MapView will be able to obtain data from Google Maps. This will apply whether you are developing your application on the emulator or preparing your application for deployment to mobile devices.

          + +

          Registering for a Maps API Key is simple, free, and has two parts:

            -
          1. Registering a public key fingerprint from the certificate that you will use to sign the .apk. The registration service then provides you a Maps API Key that is associated with your application's signer certificate.
          2. -
          3. Adding the Maps API Key to a special attribute of the MapView element — android:apiKey. You can use the same Maps API Key for any MapView in any application, provided that the application's .apk is signed with the certificate whose fingerprint you registered with the service.
          4. +
          5. Registering the MD5 fingerprint of the certificate that you will use to sign your application. The Maps registration service then provides you a Maps API Key that is associated with your application's signer certificate.
          6. +
          7. Adding a reference to the Maps API Key in each MapView, whether declared in XML or instantiated directly from code. You can use the same Maps API Key for any MapView in any Android application, provided that the application is signed with the certificate whose fingerprint you registered with the service.
          -

          Once you have registered your application as described above, your MapView will be able to retrieve data from the Google Maps servers.

          +

          During registration, you also need to agree to the Maps API Terms of Service, which describe how your application can use the Maps data. In general, the terms of service are permissive and place few restrictions on how you can use the data. For example, the terms allow you to build "friend finder" type applications.

          -
          -

          The MapView registration service is not yet active and Google Maps is not yet enforcing the Maps API Key requirement. The registration service will be activated soon, so that MapViews in any application deployed to a mobile device will require registration and a valid Maps API Key.

          +

          The sections below describe how to obtain your Maps API Key and how to reference it from your MapView elements.

          -

          As soon as the registration service becomes available, this page (http://code.google.com/android/toolbox/apis/mapkey.html) will be updated with details about how and where to register and how to add your Maps API Key to your application.

          + -

          In the meantime, you can continue developing your MapView without registration, provided that you:

          -
            -
          1. Add the attribute "android:apiKey" to the MapView element in your layout XML, with any value. Or
          2. -
          3. Include an arbitrary string in the apikey parameter of the MapView constructor, if creating the MapView programmatically.
          4. +

            Overview

            + +

            MapView objects are views that display Maps tiles downloaded from the Google Maps service. To ensure that applications use Maps data in an appropriate manner, the Google Maps service requires application developers to register with the service, agreeing to the Terms of Service and supplying an MD5 fingerprint of the certificate(s) that they will use to sign applications. For each registered certificate fingerprint, the service then provides the developer with a Maps API Key — an alphanumeric string that uniquely identifies the certificate and developer registered with the service.

            + +

            The Google Maps service also requires that each MapView identify itself to the service using a Maps API Key. Before providing Maps tiles to a MapView, the service checks the Maps API Key supplied by the MapView to ensure that it:

            +
              +
            • References a certificate/developer registered with the service, and
            • +
            • References a certificate that matches the certificate with which the application (containing the MapView) was signed.
            • +
            + +

            Unless both conditions are met, the service does not provide Maps tiles to the MapView.

            + +

            Each MapView object in your application must reference a Maps API Key. Since the Key is associated with a certificate, all Mapview elements in an application should reference the same Key. Going a step further, all MapView elements in all applications that you sign with the same certificate should reference the same Key.

            + +

            On the other hand, you can register for multiple Maps API Keys, each being associated with a specific certificate. You would want to do this if, for example, you were developing several independent applications that you will sign using different certificates. In this case, note that all MapView elements in a given application can reference the same Maps API Key, but must reference the Key that is associated with the certificate used to sign the application.

            + +

            Because MapView elements must refer to a Maps API Key, you need to register your certificate and receive a Key before you can make use of MapView elements in your application. To make it easier for you to get started using MapView elements, you are welcome to register the debug certificate generated by the SDK tools and receive a temporary Maps API Key. The details of how to do that are given below.

            + +

            When you are preparing to release your application, however, note that you must sign your application with a suitable cryptographic key, rather than the SDK debug key. That means that you will also need to register your application's release certificate with the Google Maps service. After you've done so, you will receive a new Maps API Key that is uniquely associated with your release certificate. To enable the MapView elements in your application to work after release, you must remember to change the Maps API Key for all MapViews in your application so that they refer to the Key associated with your release certificate (rather than your debug certificate).

            + +

            To summarize, the important points to understand about MapViews and the Maps API Key are:

            + +
              +
            • To display Maps data in a MapView, you need to register for a Maps API Key
            • +
            • Each Maps API Key is uniquely associated with a specific certificate, based on an MD5 fingerprint of the certificate
            • +
            • Each MapView must reference a Maps API Key, and the Key referenced must be registered to the certificate used to sign the application
            • +
            • All MapView elements in an application can reference the same Maps API Key
            • +
            • You can register multiple certificates under your developer identity
            • +
            • You can get a temporary Maps API Key based on your debug certificate, but before you publish your application, you must register for a new Key based on your release certificate and update references in your MapViews accordingly
            • +
            + +

            Getting the MD5 Fingerprint of Your Signing Certificate

            + + + +

            To register for a Maps API Key, you need to provide an MD5 fingerprint of the certificate that you will use to sign your application.

            + +

            Before you visit the registration page, use Keytool to generate the fingerprint of the appropriate certificate. + +

            First, determine which key you will use to sign your application at release and make sure of the path to the keystore that contains it.

            + +

            Next, run Keytool with the -list option, against the target keystore and key alias. The table below lists the options you should use.

            + + + + + + + + + + + + + + + + + + + + + +
            Keytool OptionDescription
            -listPrint an MD5 fingerprint of a certificate.
            -keystore <keystore-name>.keystoreThe name of the keystore containing the target key.
            -storepass <password>

            A password for the +keystore.

            As a security precaution, do not include this option +in your command line unless you are working at a secure computer. +If not supplied, Keytool prompts you to enter the password. In this +way, your password is not stored in your shell history.

            -alias <alias_name>The alias for the key for which to generate the MD5 certificate fingerprint.
            -keypass <password>

            The password for the key.

            +

            As a security precaution, do not include this option +in your command line unless you are working at a secure computer. +If not supplied, Keytool prompts you to enter the password. In this +way, your password is not stored in your shell history.

            + +

            Here's an example of a Keytool command that generates an MD5 certificate fingerprint for the key alias_name in the keystore my-release-key.keystore:

            + +
            $ keytool -list -alias alias_name -keystore my-release-key.keystore
            + +

            Keytool will prompt you to enter passwords for the keystore and key. As output of the command, Keytool prints the fingerprint to the shell. For example:

            + +
            Certificate fingerprint (MD5): 94:1E:43:49:87:73:BB:E6:A6:88:D7:20:F1:8E:B5:98
            + +

            Note that, if you happen to forget your Maps API Key, you can repeat the process described above and register the fingerprint again. The server will give you the same key for the specified certificate fingerprint.

            + +

            Once you have the fingerprint, you can go to the Maps API registration site, described next.

            + +

            Getting the MD5 Fingerprint of the SDK Debug Certificate

            + +

            While you are developing and debugging your application, you will likely be +sigining your application in debug mode — that is, the SDK build tools +will automatically sign your application using the debug certificate. To let +your MapView elements properly display Maps data during this period, you should +obtain a temporary Maps API Key registered to the debug certificate. To do so, +you first need to get the MD5 fingerprint of the debug certificate. When +you are ready to release your application, you must register your release +certificate with the Google Maps service and obtain a new Maps API Key. You must +then change the MapView elements in your application to reference the new API +key.

            + +

            To generate an MD5 fingerprint of the debug certificate, first locate the debug keystore. The location at which the SDK tools create the default debug keystore varies by platform:

            + +
              +
            • Windows Vista: C:\Users\<user>\AppData\Local\Android\debug.keystore
            • +
            • Windows XP: C:\Documents and Settings\<user>\Local Settings\Application Data\Android\debug.keystore
            • +
            • OS X and Linux: ~/.android/debug.keystore
            • +
            + +

            If you are using Eclipse/ADT and are unsure where the debug keystore is located, you can select Windows > Prefs > Android > Build to check the full path, which you can then paste into a file explorer to locate the directory containing the keystore.

            + +

            Once you have located the keystore, use this Keytool command to get the MD5 fingerprint of the debug certificate:

            + +
            $ keytool -list -alias androiddebugkey \
            +-keystore <path_to_debug_keystore>.keystore \
            +-storepass android -keypass android
            + +

            Registering the Certificate Fingerprint with the Google Maps Service

            + +

            When you are ready to register for a Maps API Key, load this page in a browser:

            + +

            http://code.google.com/android/maps-api-signup.html

            + +

            To register for a Maps API Key, follow these steps:

            + +
              +
            1. If you don't have a Google account, use the link on the page to set one up.
            2. +
            3. Read the Android Maps API Terms of Service carefully. If you agree to the terms, indicate so using the checkbox on the screen.
            4. +
            5. Paste the MD5 certificate fingerprint of the certificate that you are registering into the appropriate form field.
            6. +
            7. Click "Generate API Key"
            -

            When the Maps API Key checking is activated in the service, any MapViews that do not have a properly registered apiKey will stop working. The map data (tile images) of the MapView will never load (even if the device is on the network). In this case, go to the page linked above and read about how to register your certificate fingerprint and obtain a Maps API Key.

            +

            The server will handle your request, associating the fingerprint with your developer identity and generating a unique Maps API Key, then returning a results page that gives you your Key string.

            + +

            To use the Maps API Key string, copy and paste it into your code as described in the next section.

            + +

            Adding the Maps API Key to your Application

            + +

            Once you've registered with the Google Maps service and have obtained a Maps API Key, you must add it to your application's MapView objects, so that the Maps server will allow them to download Maps tiles.

            + +

            For <MapView> elements declared in XML layout files, add the Maps API Key as the value of a special attribute — android:apiKey. For example: + +

            <com.google.android.maps.MapView
            + android:layout_width="fill_parent"
            + android:layout_height="fill_parent"
            + android:enabled="true"
            + android:clickable="true"
            + android:apiKey="example_Maps_ApiKey_String"
            + />
            + + +

            For MapView objects instantiated directly from code, pass the Maps API Key string as a parameter in the constructor. For example:

            + +
            mMapView = new MapView(this, "example_Maps_ApiKey_String");
            + +

            For more information about MapView, see the MapView class Documentation.

            + +

            Final Steps to Enable MapView Elements

            + +

            If you've added the Maps API Key to the MapViews in your application, here are the final steps to enable the MapView elements to run properly:

            + +
              +
            • Make sure that you added a <uses-library> element referencing the external com.google.android.maps library. The element must be a child of the <application> element in the application's manifest. For example: + +

              <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              + package="com.example.package.name">
              + ...
              + <application android:name="MyApplication" >
              +   <uses-library android:name="com.google.android.maps" />
              + ...
              + </application>

            • + +
            • Sign your application with the certificate that corresponds to the Maps API Key referenced in your MapView elements.
            • + +
            + +

            Note that, when you are ready to publish your application, you must get a Maps API Key that is based on the certificate that you will use to sign the application for release. You must then change the Maps API Key string referenced by all of your MapView elements, so that they reference the new Key.

            + + + diff --git a/docs/html/guide/topics/location/index.jd b/docs/html/guide/topics/location/index.jd index eeaab394e82d4..53f1d290ba648 100644 --- a/docs/html/guide/topics/location/index.jd +++ b/docs/html/guide/topics/location/index.jd @@ -95,7 +95,7 @@ MapView must extend {@link-fixme com.google.android.maps.MapActivity}.

            Also note that you must obtain a MapView API Key from the Google Maps service, before your MapView can load maps data. For more information, see -Obtaining a MapView API Key.

            +Obtaining a MapView API Key.

            Once you've created a MapView, you'll probably want to use {@link-fixme com.google.android.maps.MapView#getController()} to diff --git a/docs/html/guide/topics/resources/available-resources.jd b/docs/html/guide/topics/resources/available-resources.jd index 7ba9e52a2f210..2a6a6ac6a1126 100644 --- a/docs/html/guide/topics/resources/available-resources.jd +++ b/docs/html/guide/topics/resources/available-resources.jd @@ -853,7 +853,7 @@ of an <item> (to create a Sub Menu).

            For more discussion on how to create menus in XML and inflate them in your application, -read Creating Menus.

            +read Creating Menus.

            @@ -877,7 +877,7 @@ the Android namespace "http://schemas.android.com/apk/res/android" defined in the root element.

            For a complete discussion on creating layouts, see the -Views and Layout topic.

            +User Interface topic.

            Source file format: XML file requiring a <?xml version="1.0" encoding="utf-8"?> @@ -971,7 +971,7 @@ res/layout/some_file.xml.

            Attributes exposed by all the superclasses of that element. For example, the TextView class extends the View class, so the <TextView> element supports all the attributes that the <View> element exposes — a long list, including View_paddingBottom and View_scrollbars. These too are used without the class name: <TextView android:paddingBottom="20" android:scrollbars="horizontal" />.
          5. - Attributes of the object's {@link android.view.ViewGroup.LayoutParams} subclass. All View objects support a LayoutParams member (see LayoutParams in Implementing a UI). To set properties on an element's LayoutParams member, the attribute to use is "android:layout_layoutParamsProperty". For example: android:layout_gravity for an object wrapped by a <LinearLayout> element. Remember that each LayoutParams subclass also supports inherited attributes. Attributes exposed by each subclass are given in the format someLayoutParamsSubclass_Layout_layout_someproperty. This defines an attribute "android:layout_someproperty". Here is an example of how Android documentation lists the properties of the {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams} class: + Attributes of the object's {@link android.view.ViewGroup.LayoutParams} subclass. All View objects support a LayoutParams member (see Declaring Layout). To set properties on an element's LayoutParams member, the attribute to use is "android:layout_layoutParamsProperty". For example: android:layout_gravity for an object wrapped by a <LinearLayout> element. Remember that each LayoutParams subclass also supports inherited attributes. Attributes exposed by each subclass are given in the format someLayoutParamsSubclass_Layout_layout_someproperty. This defines an attribute "android:layout_someproperty". Here is an example of how Android documentation lists the properties of the {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams} class:
      @@ -1032,7 +1032,7 @@ setContentView(R.layout.main_screen); However, layout elements can also represent repeating elements used as templates.

      -

      Also see Views and Layout for more information on layouts.

      +

      Also see User Interface for more information on layouts.

      @@ -1078,7 +1078,7 @@ setContentView(R.layout.main_screen);

      For a complete discussion on styles and themes, read -Applying Styles and Themes.

      +Applying Styles and Themes.

      Source file format: XML file requiring a <?xml version="1.0" encoding="utf-8"?> declaration, and a root <resources> element containing one or more <style> tags. @@ -1132,4 +1132,4 @@ setContentView(R.layout.main_screen);

      For examples of how to declare and apply styles and themes, read -Applying Styles and Themes.

      +Applying Styles and Themes.

      diff --git a/docs/html/guide/topics/resources/resources-i18n.jd b/docs/html/guide/topics/resources/resources-i18n.jd index 8a9bd43ac66f0..b1da4cd6584de 100644 --- a/docs/html/guide/topics/resources/resources-i18n.jd +++ b/docs/html/guide/topics/resources/resources-i18n.jd @@ -120,7 +120,7 @@ the containing file.

      res/layout/ XML files that are compiled into screen layouts (or part of a screen). - See Declaring Layout + See Declaring Layout res/values/ diff --git a/docs/html/guide/topics/views/binding.jd b/docs/html/guide/topics/ui/binding.jd similarity index 99% rename from docs/html/guide/topics/views/binding.jd rename to docs/html/guide/topics/ui/binding.jd index ce57ac453a33a..f9afbc5681f76 100644 --- a/docs/html/guide/topics/views/binding.jd +++ b/docs/html/guide/topics/ui/binding.jd @@ -1,5 +1,5 @@ page.title=Binding to Data with AdapterView -parent.title=Views and Layout +parent.title=User Interface parent.link=index.html @jd:body diff --git a/docs/html/guide/topics/views/custom-components.jd b/docs/html/guide/topics/ui/custom-components.jd similarity index 99% rename from docs/html/guide/topics/views/custom-components.jd rename to docs/html/guide/topics/ui/custom-components.jd index 9e7943b1babd6..eccc2cac445b7 100644 --- a/docs/html/guide/topics/views/custom-components.jd +++ b/docs/html/guide/topics/ui/custom-components.jd @@ -1,5 +1,5 @@ page.title=Building Custom Components -parent.title=Views and Layout +parent.title=User Interface parent.link=index.html @jd:body @@ -35,7 +35,7 @@ that you can use to construct your UI.

      Among the layouts available are {@link android.widget.LinearLayout LinearLayout}, {@link android.widget.FrameLayout FrameLayout}, {@link android.widget.AbsoluteLayout AbsoluteLayout}, -and others. For more examples, see Common Layout Objects.

      +and others. For more examples, see Common Layout Objects.

      If none of the prebuilt widgets or layouts meets your needs, you can create your own View subclass. If you only need to make small adjustments to an existing widget or layout, you can simply subclass diff --git a/docs/html/guide/topics/ui/declaring-layout.jd b/docs/html/guide/topics/ui/declaring-layout.jd new file mode 100644 index 0000000000000..dd0b7deb5c06e --- /dev/null +++ b/docs/html/guide/topics/ui/declaring-layout.jd @@ -0,0 +1,269 @@ +page.title=Declaring Layout +parent.title=User Interface +parent.link=index.html +@jd:body + +

      + +

      Your layout is the architecture for the user interface in an Activity. +It defines the layout structure and holds all the elements that appear to the user. +You can declare your layout in two ways:

      +
        +
      • Declare UI elements in XML. Android provides a straightforward XML +vocabulary that corresponds to the View classes and subclasses, such as those for widgets and layouts.
      • +
      • Instantiate layout elements at runtime. Your +application can create View and ViewGroup objects (and manipulate their properties) programmatically.
      • +
      + +

      The Android framework gives you the flexibility to use either or both of these methods for declaring and managing your application's UI. For example, you could declare your application's default layouts in XML, including the screen elements that will appear in them and their properties. You could then add code in your application that would modify the state of the screen objects, including those declared in XML, at run time.

      + + + +

      The advantage to declaring your UI in XML is that it enables you to better separate the presentation of your application from the code that controls its behavior. Your UI descriptions are external to your application code, which means that you can modify or adapt it without having to modify your source code and recompile. For example, you can create XML layouts for different screen orientations, different device screen sizes, and different languages. Additionally, declaring the layout in XML makes it easier to visualize the structure of your UI, so it's easier to debug problems. As such, this document focuses on teaching you how to declare your layout in XML. If you're +interested in instantiating View objects at runtime, refer to the {@link android.view.ViewGroup} and +{@link android.view.View} class references.

      + +

      In general, the XML vocabulary for declaring UI elements closely follows the structure and naming of the classes and methods, where element names correspond to class names and attribute names correspond to methods. In fact, the correspondence is often so direct that you can guess what XML attribute corresponds to a class method, or guess what class corresponds to a given xml element. However, note that not all vocabulary is identical. In some cases, there are slight naming differences. For +example, the EditText element has a text attribute that corresponds to +EditText.setText().

      + +

      Tip: Learn more about different layout types in Common +Layout Objects. There are also a collection of tutorials on building various layouts in the +Hello Views tutorial guide.

      + +

      Write the XML

      + + + +

      Using Android's XML vocabulary, you can quickly design UI layouts and the screen elements they contain, in the same way you create web pages in HTML — with a series of nested elements.

      + +

      Each layout file must contain exactly one root element, which must be a View or ViewGroup object. Once you've defined the root element, you can add additional layout objects or widgets as child elements to gradually build a View hierarchy that defines your layout. For example, here's an XML layout that uses a vertical {@link android.widget.LinearLayout} +to hold a {@link android.widget.TextView} and a {@link android.widget.Button}:

      +
      +<?xml version="1.0" encoding="utf-8"?>
      +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      +              android:layout_width="fill_parent" 
      +              android:layout_height="fill_parent" 
      +              android:orientation="vertical" >
      +    <TextView android:id="@+id/text"
      +              android:layout_width="wrap_content"
      +              android:layout_height="wrap_content"
      +              android:text="Hello, I am a TextView" />
      +    <Button android:id="@+id/button"
      +            android:layout_width="wrap_content"
      +            android:layout_height="wrap_content"
      +            android:text="Hello, I am a Button" />
      +</LinearLayout>
      +
      + +

      After you've declared your layout in XML, save the file with the .xml extension, +in your Android project's res/layout/ directory, so it will properly compile.

      + +

      We'll discuss each of the attributes shown here a little later.

      + +

      Load the XML Resource

      + +

      When you compile your application, each XML layout file is compiled into a +{@link android.view.View} resource. You should load the layout resource from your application code, in your +{@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()} callback implementation. +Do so by calling {@link android.app.Activity#setContentView(int) setContentView()}, +passing it the reference to your layout resource in the form of: +R.layout.layout_file_name +For example, if your XML layout is saved as main_layout.xml, you would load it +for your Activity like so:

      +
      +public void onCreate(Bundle savedInstanceState) {
      +    super.onCreate(savedInstanceState);
      +    setContentView.(R.layout.main_layout);
      +}
      +
      + +

      The onCreate() callback method in your Activity is called by the Android framework when +your Activity is launched (see the discussion on Lifecycles, in the +Application Fundamantals, for more on this).

      + + +

      Attributes

      + +

      Every View and ViewGroup object supports their own variety of XML attributes. +Some attributes are specific to a View object (for example, TextView supports the textSize +attribute), but these attributes are also inherited by any View objects that may extend this class. +Some are common to all View objects, because they are inherited from the root View class (like +the id attribute). And, other attributes are considered "layout parameters," which are +attributes that describe certain layout orientations of the View object, as defined by that object's +parent ViewGroup object.

      + +

      ID

      + +

      Any View object may have an integer ID associated with it, to uniquely identify the View within the tree. +When the application is compiled, this ID is referenced as an integer, but the ID is typically +assigned in the layout XML file as a string, in the id attribute. +This is an XML attribute common to all View objects +(defined by the {@link android.view.View} class) and you will use it very often. +The syntax for an ID, inside an XML tag is:

      +
      android:id="@+id/my_button"
      + +

      The at-symbol (@) at the beginning of the string indicates that the XML parser should parse and expand the rest +of the ID string and identify it as an ID resource. The plus-symbol (+) means that this is a new resource name that must +be created and added to our resources (in the R.java file). There are a number of other ID resources that +are offered by the Android framework. When referencing an Android resource ID, you do not need the plus-symbol, +but must add the android package namespace, like so:

      +
      android:id="@android:id/empty"
      +

      With the android package namespace in place, we're now referencing an ID from the android.R +resources class, rather than the local resources class.

      + +

      In order to create views and reference them from the application, a common pattern is to:

      +
        +
      1. Define a view/widget in the layout file and assign it a unique ID: +
        +<Button android:id="@+id/my_button"
        +        android:layout_width="wrap_content"
        +        android:layout_height="wrap_content"
        +        android:text="@string/my_button_text"/>
        +
        +
      2. +
      3. Then create an instance of the view object and capture it from the layout +(typically in the {@link android.app.Activity#onCreate(Bundle) onCreate()} method): +
        +Button myButton = (Button) findViewById(R.id.my_button);
        +
        +
      4. +
      +

      Defining IDs for view objects is important when creating a {@link android.widget.RelativeLayout}. +In a relative layout, sibling views can define their layout relative to another sibling view, +which is referenced by the unique ID.

      +

      An ID need not be unique throughout the entire tree, but it should be +unique within the part of the tree you are searching (which may often be the entire tree, so it's best +to be completely unique when possible).

      + + +

      Layout Parameters

      + +

      XML layout attributes named layout_something define +layout parameters for the View that are appropriate for the ViewGroup in which it resides.

      + +

      Every ViewGroup class implements a nested class that extends {@link +android.view.ViewGroup.LayoutParams}. This subclass +contains property types that define the size and position for each child view, as +appropriate for the view group. As you can see in the figure below, the parent +view group defines layout parameters for each child view (including the child view group).

      + + + +

      Note that every LayoutParams subclass has its own syntax for setting +values. Each child element must define LayoutParams that are appropriate for its parent, +though it may also define different LayoutParams for its own children.

      + +

      All view groups include a width and height (layout_width and layout_height), +and each view is required to define them. +Many LayoutParams also include optional margins and +borders. You can specify width and height with exact measurements, though you probably won't want +to do this often. More often, you will tell your view to size itself either to +the dimensions required by its content, or to become as big as its parent view group +will allow (with the wrap_content and fill_parent values, respectively). +The accepted measurement types are defined in the +Available Resources document.

      + + +

      Layout Position

      +

      + The geometry of a view is that of a rectangle. A view has a location, + expressed as a pair of left and top coordinates, and + two dimensions, expressed as a width and a height. The unit for location + and dimensions is the pixel. +

      + +

      + It is possible to retrieve the location of a view by invoking the methods + {@link android.view.View#getLeft()} and {@link android.view.View#getTop()}. The former returns the left, or X, + coordinate of the rectangle representing the view. The latter returns the + top, or Y, coordinate of the rectangle representing the view. These methods + both return the location of the view relative to its parent. For instance, + when getLeft() returns 20, that means the view is located 20 pixels to the + right of the left edge of its direct parent. +

      + +

      + In addition, several convenience methods are offered to avoid unnecessary + computations, namely {@link android.view.View#getRight()} and {@link android.view.View#getBottom()}. + These methods return the coordinates of the right and bottom edges of the + rectangle representing the view. For instance, calling {@link android.view.View#getRight()} + is similar to the following computation: getLeft() + getWidth(). +

      + + +

      Size, Padding and Margins

      +

      + The size of a view is expressed with a width and a height. A view actually + possess two pairs of width and height values. +

      + +

      + The first pair is known as measured width and + measured height. These dimensions define how big a view wants to be + within its parent. The + measured dimensions can be obtained by calling {@link android.view.View#getMeasuredWidth()} + and {@link android.view.View#getMeasuredHeight()}. +

      + +

      + The second pair is simply known as width and height, or + sometimes drawing width and drawing height. These + dimensions define the actual size of the view on screen, at drawing time and + after layout. These values may, but do not have to, be different from the + measured width and height. The width and height can be obtained by calling + {@link android.view.View#getWidth()} and {@link android.view.View#getHeight()}. +

      + +

      + To measure its dimensions, a view takes into account its padding. The padding + is expressed in pixels for the left, top, right and bottom parts of the view. + Padding can be used to offset the content of the view by a specific amount of + pixels. For instance, a left padding of 2 will push the view's content by + 2 pixels to the right of the left edge. Padding can be set using the + {@link android.view.View#setPadding(int, int, int, int)} method and queried by calling + {@link android.view.View#getPaddingLeft()}, {@link android.view.View#getPaddingTop()}, + {@link android.view.View#getPaddingRight()} and {@link android.view.View#getPaddingBottom()}. +

      + +

      + Even though a view can define a padding, it does not provide any support for + margins. However, view groups provide such a support. Refer to + {@link android.view.ViewGroup} and + {@link android.view.ViewGroup.MarginLayoutParams} for further information. +

      + +

      For more information about dimensions, see Dimension Values.

      + + + + diff --git a/docs/html/guide/topics/ui/how-android-draws.jd b/docs/html/guide/topics/ui/how-android-draws.jd new file mode 100644 index 0000000000000..a511005c79297 --- /dev/null +++ b/docs/html/guide/topics/ui/how-android-draws.jd @@ -0,0 +1,94 @@ +page.title=How Android Draws Views +parent.title=User Interface +parent.link=index.html +@jd:body + + +

      When an Activity receives focus, it will be requested to draw its layout. +The Android framework will handle the procedure for drawing, but the Activity must provide +the root node of its layout hierarchy.

      + +

      Drawing begins with the root node of the layout. It is requested to measure and +draw the layout tree. Drawing is handled by walking the tree and rendering each View that + intersects the invalid region. In turn, each View group is responsible for requesting +each of its children to be drawn (with the {@link android.view.View#draw(Canvas) draw()} method) +and each View is responsible for drawing itself. + Because the tree is traversed in-order, + this means that parents will be drawn before (i.e., behind) their children, with + siblings drawn in the order they appear in the tree. +

      + + + +

      + Drawing the layout is a two pass process: a measure pass and a layout pass. The measuring + pass is implemented in {@link android.view.View#measure(int, int)} and is a top-down traversal + of the View tree. Each View pushes dimension specifications down the tree + during the recursion. At the end of the measure pass, every View has stored + its measurements. The second pass happens in + {@link android.view.View#layout(int,int,int,int)} and is also top-down. During + this pass each parent is responsible for positioning all of its children + using the sizes computed in the measure pass. +

      + +

      + When a View's measure() method returns, its {@link android.view.View#getMeasuredWidth()} and + {@link android.view.View#getMeasuredHeight()} values must be set, along with those for all of + that View's descendants. A View's measured width and measured height values + must respect the constraints imposed by the View's parents. This guarantees + that at the end of the measure pass, all parents accept all of their + children's measurements. A parent View may call measure() more than once on + its children. For example, the parent may measure each child once with + unspecified dimensions to find out how big they want to be, then call + measure() on them again with actual numbers if the sum of all the children's + unconstrained sizes is too big or too small (i.e., if the children don't agree among themselves + as to how much space they each get, the parent will intervene and set the rules on the second pass). +

      + + + +

      + The measure pass uses two classes to communicate dimensions. The + {@link android.view.View.MeasureSpec} class is used by Views to tell their parents how they + want to be measured and positioned. The base LayoutParams class just + describes how big the View wants to be for both width and height. For each + dimension, it can specify one of:

      +
        +
      • an exact number +
      • FILL_PARENT, which means the View wants to be as big as its parent + (minus padding)
      • +
      • WRAP_CONTENT, which means that the View wants to be just big enough to + enclose its content (plus padding).
      • +
      +

      There are subclasses of LayoutParams for different subclasses of ViewGroup. + For example, AbsoluteLayout has its own subclass of LayoutParams which adds + an X and Y value. +

      + +

      + MeasureSpecs are used to push requirements down the tree from parent to + child. A MeasureSpec can be in one of three modes:

      +
        +
      • UNSPECIFIED: This is used by a parent to determine the desired dimension + of a child View. For example, a LinearLayout may call measure() on its child + with the height set to UNSPECIFIED and a width of EXACTLY 240 to find out how + tall the child View wants to be given a width of 240 pixels.
      • +
      • EXACTLY: This is used by the parent to impose an exact size on the + child. The child must use this size, and guarantee that all of its + descendants will fit within this size.
      • +
      • AT_MOST: This is used by the parent to impose a maximum size on the + child. The child must gurantee that it and all of its descendants will fit + within this size.
      • +
      + + + diff --git a/docs/html/guide/topics/ui/index.jd b/docs/html/guide/topics/ui/index.jd new file mode 100644 index 0000000000000..ccc8ff68575c3 --- /dev/null +++ b/docs/html/guide/topics/ui/index.jd @@ -0,0 +1,235 @@ +page.title=User Interface +@jd:body + +
      +
      + +

      Key classes and packages

      +
        +
      1. {@link android.view.View}
      2. +
      3. {@link android.view.ViewGroup}
      4. +
      5. {@link android.widget}
      6. +
      + +

      In this document

      +
        +
      1. View Hierarchy
      2. +
      3. Layout
      4. +
      5. Widgets
      6. +
      7. UI Events
      8. +
      9. Menus
      10. +
      11. Advanced Topics +
          +
        1. Adapters
        2. +
        3. Styles and Themes
        4. +
        +
      12. +
      +
      +
      + +

      In an Android application, the user interface is built using {@link android.view.View} and +{@link android.view.ViewGroup} objects. There are many types of views and view groups, each of which +is a descendant of the {@link android.view.View} class.

      + +

      View objects are the basic units of user interface expression on the Android platform. +The View class serves as the base for subclasses called "widgets," which offer fully implemented +UI objects, like text fields and buttons. The ViewGroup class serves as the base for subclasses called "layouts," +which offer different kinds of layout architecture, like linear, tabular and relative.

      + +

      A View object is a data structure whose properties store the layout parameters and content for a specific +rectangular area of the screen. A View object handles its own measurement, layout, drawing, focus change, +scrolling, and key/gesture interactions for the rectangular area of the screen in which it resides. As an +object in the user interface, a View is also a point of interaction for the user and the receiver +of the interaction events.

      + + +

      View Hierarchy

      + +

      On the Android platform, you define an Activity's UI using a hierarchy of View and ViewGroup nodes, +as shown in the diagram below. This hierarchy tree can be as simple or complex as you need it to be, and you +can build it up using Android's set of predefined widgets and layouts, or with custom Views that you +create yourself.

      + + + +

      +In order to attach the view hierarchy tree to the screen for rendering, your Activity must call the +{@link android.app.Activity#setContentView(int) setContentView()} +method and pass a reference to the root node object. The Android system +receives this reference and uses it to invalidate, measure, and draw the tree. The root node of the hierarchy requests +that its child nodes draw themselves — in turn, each view group node is responsible for calling +upon each of its own child views to draw themselves. +The children may request a size and location within the parent, but the parent object has the final +decision on where how big each child can be. Android parses +the elements of your layout in-order (from the top of the hierarchy tree), instantiating the Views and +adding them to their parent(s). Because these are drawn in-order, if there are elements that +overlap positions, the last one to be drawn will lie on top of others previously drawn to that space.

      + +

      For a more detailed discussion on how view hierarchies are measured +and drawn, read How Android Draws Views.

      + + +

      Layout

      + +

      The most common way to define your layout and express the view hierarchy is with an XML layout file. +XML offers a human-readable structure for the layout, much like HTML. Each element in XML is +either a View or ViewGroup object (or descendent thereof). View objects are leaves in the tree, +ViewGroup objects are branches in the tree (see the View Hierarchy figure above).

      +

      The name of an XML element +is respective to the Java class that it represents. So a <TextView> element creates +a {@link android.widget.TextView} in your UI, and a <LinearLayout> element creates +a {@link android.widget.LinearLayout} view group. When you load a layout resource, +the Android system initializes these run-time objects, corresponding to the elements in your layout.

      + +

      For example, a simple vertical layout with a text view and a button looks like this:

      +
      +<?xml version="1.0" encoding="utf-8"?>
      +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      +              android:layout_width="fill_parent" 
      +              android:layout_height="fill_parent"
      +              android:orientation="vertical" >
      +    <TextView android:id="@+id/text"
      +              android:layout_width="wrap_content"
      +              android:layout_height="wrap_content"
      +              android:text="Hello, I am a TextView" />
      +    <Button android:id="@+id/button"
      +            android:layout_width="wrap_content"
      +            android:layout_height="wrap_content"
      +            android:text="Hello, I am a Button" />
      +</LinearLayout>
      +
      + +

      Notice that the LinearLayout element contains both the TextView and the Button. You can nest +another LinearLayout (or other type of view group) inside here, to lengthen the view hierarchy and create a more +complex layout.

      + +

      For more on building a UI layout, read Declaring Layout. + +

      + +

      There are a variety of ways in which you can layout your views. Using more and different kinds of view groups, +you can structure child views and view groups in an infinite number of ways. +Some pre-defined view groups offered by Android (called layouts) include LinearLayout, RelativeLayout, AbsoluteLayout, +TableLayout, GridLayout and others. Each offers a unique set of layout parameters that are used to define the +positions of child views and layout structure.

      +

      To learn about some of the different kinds of view groups used for a layout, +read Common Layout Objects.

      + + +

      Widgets

      + +

      A widget is a View object that serves as an interface for interaction with the user. +Android provides a set of fully implemented +widgets, like buttons, checkboxes, and text-entry fields, so you can quickly build your UI. +Some widgets provided by Android are more complex, like a date picker, a clock, and zoom controls. +But you're not limited to the kinds of widgets provided by the Android platform. If you'd +like to do something more customized and create your own actionable elements, you can, by defining your own +View object or by extending and combining existing widgets.

      +

      Read more in Building Custom Components.

      + +

      For a list of the widgets provided by Android, see the {@link android.widget} package.

      + + +

      UI Events

      + +

      Once you've added some Views/widgets to the UI, you probably want to know about the +user's interaction with them, so you can perform actions. To be informed of UI events, you need to +do one of two things:

      +
        +
      • Define an event listener and register it with the View. More often than not, +this is how you'll listen for events. The View class contains a collection of nested interfaces named +On<something>Listener, each with a callback method called On<something>(). +For example, {@link android.view.View.OnClickListener} (for handling "clicks" on a View), +{@link android.view.View.OnTouchListener} (for handling touch screen events in a View), and +{@link android.view.View.OnKeyListener} (for handling device key presses within a View). So if you want your View +to be notified when it is "clicked" (such as when a button is selected), implement OnClickListener and define +its onClick() callback method (where you perform the action upon click), and register it +to the View with {@link android.view.View#setOnClickListener(View.OnClickListener) setOnClickListener()}. +
      • +
      • Override an existing callback method for the View. This is +what you should do when you've implemented your own View class and want to listen for specific events +that occur within it. Example events you can handle include when the +screen is touched ({@link android.view.View#onTouchEvent(MotionEvent) onTouchEvent()}), when +the trackball is moved ({@link android.view.View#onTrackballEvent(MotionEvent) onTrackballEvent()}), +or when a key on the device is pressed ({@link android.view.View#onKeyDown(int, KeyEvent) +onKeyDown()}). This allows you to define the default behavior for each event inside your custom View and determine +whether the event should be passed on to some other child View. Again, these are callbacks to the View class, +so your only chance to define them is when you +build a custom component. +
      • +
      + +

      Continue reading about handling user interaction with Views in the Handling UI Events +document.

      + + + + +

      Application menus are another important part of an application's UI. Menus offers a reliable interface that reveals +application functions and settings. The most common application menu is revealed by pressing +the MENU key on the device. However, you can also add Context Menus, which may be revealed when the user presses +and holds down on an item.

      + +

      Menus are also structured using a View hierarchy, but you don't define this structure yourself. Instead, +you define the {@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} or +{@link android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) onCreateContextMenu()} +callback methods for your Activity and declare the items that you want to include in your menu. +At the appropriate time, Android will automatically create the necessary View hierarchy for the menu and +draw each of your menu items in it.

      + +

      Menus also handle their own events, so there's no need to register event listeners on the items in your menu. +When an item in your menu is selected, the {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} or +{@link android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()} +method will be called by the framework.

      + +

      And just like your application layout, you have the option to declare the items for you menu in an XML file.

      + +

      Read Creating Menus to learn more.

      + + +

      Advanced Topics

      + +

      Once you've grappled the fundamentals of creating a user interface, you can explore +some advanced features for creating a more complex application interface.

      + +

      Adapters

      + +

      Sometimes you'll want to populate a view group with some information that can't be hard-coded, instead, +you want to bind your view to an external source of data. To do this, you use an AdapterView as +your view group and each child View is initialized and populated with data from the Adapter.

      +

      The AdapterView object is an implementation of ViewGroup that determines its child views +based on a given Adapter object. The Adapter acts like a courier between your data source (perhaps an +array of external strings) and the AdapterView, which displays it. There are several implementations +of the Adapter class, for specific tasks, such as the CursorAdapter for reading database data from a Cursor, +or an ArrayAdapter for reading from an arbitrary array.

      +

      To learn more about using an Adapter to populate your views, read +Binding to Data with AdapterView.

      + + +

      Styles and Themes

      + +

      Perhaps you're not satisfied with the look of the standard widgets. To revise them, you can create some +of your own styles and themes.

      + +
        +
      • A style is a set of one or more formatting attributes that you can apply as a unit to individual elements +in your layout. For example, you could define a style that specifies a certain text size and color, then +apply it to only specific View elements.
      • +
      • A theme is a set of one or more formatting attributes that you can apply as a unit to all activities in +an application, or just a single activity. For example, you could define a theme that sets specific colors for +the window frame and the panel background, and sets text sizes and colors for menus. This theme can then be +applied to specific activities or the entire application.
      • +
      + +

      Styles and themes are resources. Android provides some default style and theme resources that you can use, +or you can declare your own custom style and theme resources.

      +

      Learn more about using styles and themes in the +Applying Styles and Themes document.

      diff --git a/docs/html/guide/topics/views/layout.jd b/docs/html/guide/topics/ui/layout-objects.jd similarity index 76% rename from docs/html/guide/topics/views/layout.jd rename to docs/html/guide/topics/ui/layout-objects.jd index a6fec358c69e9..cf85fd62b962e 100644 --- a/docs/html/guide/topics/views/layout.jd +++ b/docs/html/guide/topics/ui/layout-objects.jd @@ -1,5 +1,5 @@ page.title=Common Layout Objects -parent.title=Views and Layout +parent.title=User Interface parent.link=index.html @jd:body @@ -20,6 +20,8 @@ parent.link=index.html

      This section describes some of the more common types of layout objects to use in your applications. Like all layouts, they are subclasses of {@link android.view.ViewGroup ViewGroup}.

      +

      Also see the Hello Views tutorials for +some guidance on using more Android View layouts.

      FrameLayout

      {@link android.widget.FrameLayout FrameLayout} is the simplest type of layout @@ -95,9 +97,42 @@ cells empty, but cells cannot span columns, as they can in HTML.

      Each row has zero or more cells, each of which is defined by any kind of other View. So, the cells of a row may be composed of a variety of View objects, like ImageView or TextView objects. A cell may also be a ViewGroup object (for example, you can nest another TableLayout as a cell).

      -

      The following image shows a table layout, with the invisible cell borders displayed as dotted lines.

      +

      The following sample layout has two rows and two cells in each. The accompanying screenshot shows the +result, with cell borders displayed as dotted lines (added for visual effect).

      - + + + + + +
      +
      +<?xml version="1.0" encoding="utf-8"?>
      +<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
      +    android:layout_width="fill_parent"
      +    android:layout_height="fill_parent"
      +    android:stretchColumns="1">
      +    <TableRow>
      +        <TextView
      +            android:text="@string/table_layout_4_open"
      +            android:padding="3dip" />
      +        <TextView
      +            android:text="@string/table_layout_4_open_shortcut"
      +            android:gravity="right"
      +            android:padding="3dip" />
      +    </TableRow>
      +
      +    <TableRow>
      +        <TextView
      +            android:text="@string/table_layout_4_save"
      +            android:padding="3dip" />
      +        <TextView
      +            android:text="@string/table_layout_4_save_shortcut"
      +            android:gravity="right"
      +            android:padding="3dip" />
      +    </TableRow>
      +</TableLayout>
      +

      Columns can be hidden, marked to stretch and fill the available screen space, or can be marked as shrinkable to force the column to shrink until the table @@ -127,13 +162,54 @@ TableLayout tutorial.

      will be aligned relative to screen center. Also, because of this ordering, if using XML to specify this layout, the element that you will reference (in order to position other view objects) must be listed in the XML file before you refer to it from the other views via its reference ID.

      -

      Here is an example relative layout with the visible and invisible elements outlined. - The root screen layout object is a RelativeLayout object.

      +

      The example below shows an XML file and the resulting screen in the UI. +Note that the attributes that refer to relative elements (e.g., layout_toLeft) +refer to the ID using the syntax of a relative resource +(@id/id).

      - + + + + + +
      +
      +<?xml version="1.0" encoding="utf-8"?>
      +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android
      +                android:layout_width="fill_parent" 
      +                android:layout_height="wrap_content"
      +                android:background="@drawable/blue"
      +                android:padding="10px" >
       
      -

      This diagram shows the class names of the screen elements, followed by a list - of the properties of each. Some of these properties are supported directly by + <TextView android:id="@+id/label" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Type here:" /> + + <EditText android:id="@+id/entry" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:background="@android:drawable/editbox_background" + android:layout_below="@id/label" /> + + <Button android:id="@+id/ok" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/entry" + android:layout_alignParentRight="true" + android:layout_marginLeft="10px" + android:text="OK" /> + + <Button android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toLeftOf="@id/ok" + android:layout_alignTop="@id/ok" + android:text="Cancel" /> +</RelativeLayout> +

      + + +

      Some of these properties are supported directly by the element, and some are supported by its LayoutParams member (subclass RelativeLayout for all the elements in this screen, because all elements are children of a RelativeLayout parent object). The defined RelativeLayout parameters are: width, height, diff --git a/docs/html/guide/topics/views/menus.jd b/docs/html/guide/topics/ui/menus.jd similarity index 96% rename from docs/html/guide/topics/views/menus.jd rename to docs/html/guide/topics/ui/menus.jd index 489cd07b62d3b..ed796eecc8d9f 100644 --- a/docs/html/guide/topics/views/menus.jd +++ b/docs/html/guide/topics/ui/menus.jd @@ -9,7 +9,7 @@ parent.link=index.html

      1. Options Menu
      2. Context Menu
      3. -
      4. Sub Menu
      5. +
      6. Submenu
      7. Define Menus in XML
      8. Menu Features
          @@ -20,6 +20,12 @@ parent.link=index.html
      +

      Key classes

      +
        +
      1. {@link android.view.Menu}
      2. +
      3. {@link android.view.ContextMenu}
      4. +
      5. {@link android.view.SubMenu}
      6. +
      @@ -47,9 +53,9 @@ for developers to provide standardized application menus for various situations.
      Context Menu
      This is a floating list of menu items that may appear when you perform a long-press on a View (such as a list item).
      -
      Sub Menu
      +
      Submenu
      This is a floating list of menu items that is revealed by an item in the Options Menu - or a Context Menu. A Sub Menu item cannot support nested Sub Menus.
      + or a Context Menu. A Submenu item cannot support nested Submenus. @@ -63,7 +69,7 @@ is automatically added when there are more than six items.

      The Options Menu is where you should include basic application functions and any necessary navigation items (e.g., to a home screen or application settings). -You can also add Sub Menus for organizing topics +You can also add Submenus for organizing topics and including extra menu functionality.

      When this menu is opened for the first time, @@ -127,7 +133,7 @@ and we recommend you do it that way for easier localization).

      Tip: If you have several menu items that can be grouped together with a title, -consider organizing them into a Sub Menu.

      +consider organizing them into a Submenu.

      Adding icons

      Icons can also be added to items that appears in the Icon Menu with @@ -136,8 +142,8 @@ consider organizing them into a Sub Menu.

      menu.add(0, MENU_QUIT, 0, "Quit") .setIcon(R.drawable.menu_quit_icon); -

      Modifying the options menu

      -

      If you want to sometimes re-write the options menu as it is opened, override the +

      Modifying the menu

      +

      If you want to sometimes re-write the Options Menu as it is opened, override the {@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()} method, which is called each time the menu is opened. This will pass you the Menu object, just like the onCreateOptionsMenu() callback. This is useful if you'd like to add or remove @@ -225,7 +231,7 @@ in the list is registered to this context menu.

      - +

      A sub menu can be added within any menu, except another sub menu. These are very useful when your application has a lot of functions that may be organized in topics, like the items in a PC application's menu bar (File, Edit, View, etc.).

      @@ -254,7 +260,7 @@ public boolean onCreateOptionsMenu(Menu menu) {

      Callbacks for items selected in a sub menu are made to the parent menu's callback method. For the example above, selections in the sub menu will be handled by the onOptionsItemSelected() callback.

      -

      You can also add Sub Menus when you define the parent menu in XML.

      +

      You can also add Submenus when you define the parent menu in XML.

      Define Menus in XML

      @@ -269,7 +275,7 @@ This is where you should keep all XML files that define your application menus.< three valid elements: <menu>, <group> and <item>. The item and group elements must be children of a menu, but item elements may also be the children of a group, and another menu element may be the child -of an item (to create a Sub Menu). Of course, the root node of any file +of an item (to create a Submenu). Of course, the root node of any file must be a menu element.

      As an example, we'll define the same menu created in the Options Menu section, @@ -302,7 +308,7 @@ passing it a pointer to our menu resource and the Menu object given by the callb and it keeps your application code clean.

      You can define menu groups by wrapping item elements in a group -element, and create Sub Menus by nesting another menu inside an item. +element, and create Submenus by nesting another menu inside an item. Each element also supports all the necessary attributes to control features like shortcut keys, checkboxes, icons, and more. To learn about these attributes and more about the XML syntax, see the Menus topic in the Available @@ -367,7 +373,7 @@ assign the same group ID to each menu item and call {@link android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}. In this case, you don't need to call setCheckable() on each menu items, because the group as a whole is set checkable. Here's an example of -two mutually exclusive options in a sub-menu:

      +two mutually exclusive options in a Submenu:

       SubMenu subMenu = menu.addSubMenu("Color");
       subMenu.add(COLOR_MENU_GROUP, COLOR_RED_ID, 0, "Red");
      diff --git a/docs/html/guide/topics/views/themes.jd b/docs/html/guide/topics/ui/themes.jd
      similarity index 99%
      rename from docs/html/guide/topics/views/themes.jd
      rename to docs/html/guide/topics/ui/themes.jd
      index a206a4baa1c1d..d684512bf07e2 100644
      --- a/docs/html/guide/topics/views/themes.jd
      +++ b/docs/html/guide/topics/ui/themes.jd
      @@ -1,5 +1,5 @@
       page.title=Applying Styles and Themes
      -parent.title=Views and Layout
      +parent.title=User Interface
       parent.link=index.html
       @jd:body
       
      diff --git a/docs/html/guide/topics/ui/ui-events.jd b/docs/html/guide/topics/ui/ui-events.jd
      new file mode 100644
      index 0000000000000..f4d114a30e7d2
      --- /dev/null
      +++ b/docs/html/guide/topics/ui/ui-events.jd
      @@ -0,0 +1,283 @@
      +page.title=Handling UI Events
      +parent.title=User Interface
      +parent.link=index.html
      +@jd:body
      +
      +
      +
      +

      On Android, there's more than one way to intercept the events from a user's interaction with your application. +When considering events within your user interface, the approach is to capture the events from +the specific View object that the user interacts with. The View class provides the means to do so.

      + +

      Within the various View classes that you'll use to compose your layout, you may notice several public callback +methods that look useful for UI events. These methods are called by the Android framework when the +respective action occurs on that object. For instance, when a View (such as a Button) is touched, +the onTouchEvent() method is called on that object. However, in order to intercept this, you must extend +the class and override the method. Obviously, extending every View object +you want to use (just to handle an event) would be obsurd. This is why the View class also contains +a collection of nested interfaces with callbacks that you can much more easily define. These interfaces, +called event listeners, are your ticket to capturing the user interaction with your UI.

      + +

      While you will more commonly use the event listeners to listen for user interaction, there may +come a time when you do want to extend a View class, in order to build a custom component. +Perhaps you want to extend the {@link android.widget.Button} +class to make something more fancy. In this case, you'll be able to define the default event behaviors for your +class using the class event handlers.

      + + +

      Event Listeners

      + +

      An event listener is an interface in the {@link android.view.View} class that contains a single +callback method. These methods will be called by the Android framework when the View to which the listener has +been registered is triggered by user interaction with the item in the UI.

      + +

      Included in the event listener interfaces are the following callback methods:

      + +
      +
      onClick()
      +
      From {@link android.view.View.OnClickListener}. + This is called when the user either touches the item + (when in touch mode), or focuses upon the item with the navigation-keys or trackball and + presses the suitable "enter" key or presses down on the trackball.
      +
      onLongClick()
      +
      From {@link android.view.View.OnLongClickListener}. + This is called when the user either touches and holds the item (when in touch mode), or + focuses upon the item with the navigation-keys or trackball and + presses and holds the suitable "enter" key or presses and holds down on the trackball (for one second).
      +
      onFocusChange()
      +
      From {@link android.view.View.OnFocusChangeListener}. + This is called when the user navigates onto or away from the item, using the navigation-keys or trackball.
      +
      onKey()
      +
      From {@link android.view.View.OnKeyListener}. + This is called when the user is focused on the item and presses or releases a key on the device.
      +
      onTouch()
      +
      From {@link android.view.View.OnTouchListener}. + This is called when the user performs an action qualified as a touch event, including a press, a release, + or any movement gesture on the screen (within the bounds of the item).
      +
      onCreateContextMenu()
      +
      From {@link android.view.View.OnCreateContextMenuListener}. + This is called when a Context Menu is being built (as the result of a sustained "long click"). See the discussion + on context menus in Creating Menus for more information.
      +
      + +

      These methods are the sole inhabitants of their respective interface. To define one of these methods +and handle your events, implement the nested interface in your Activity or define it as an anonymous class. +Then, pass an instance of your implementation +to the respective View.set...Listener() method. (E.g., call +{@link android.view.View#setOnClickListener(View.OnClickListener) setOnClickListener()} +and pass it your implementation of the {@link android.view.View.OnClickListener OnClickListener}.)

      + +

      The example below shows how to register an on-click listener for a Button.

      + +
      +// Create an anonymous implementation of OnClickListener
      +private OnClickListener mCorkyListener = new OnClickListener() {
      +    public void onClick(View v) {
      +      // do something when the button is clicked
      +    }
      +};
      +
      +protected void onCreate(Bundle savedValues) {
      +    ...
      +    // Capture our button from layout
      +    Button button = (Button)findViewById(R.id.corky);
      +    // Register the onClick listener with the implementation above
      +    button.setOnClickListener(mCorkyListener);
      +    ...
      +}
      +
      + +

      You may also find it more conventient to implement OnClickListener as a part of your Activity. +This will avoid the extra class load and object allocation. For example:

      +
      +public class ExampleActivity extends Activity implements OnClickListener {
      +    protected void onCreate(Bundle savedValues) {
      +        ...
      +        Button button = (Button)findViewById(R.id.corky);
      +        button.setOnClickListener(this);
      +    }
      +
      +    // Implement the OnClickListener callback
      +    public void onClick(View v) {
      +      // do something when the button is clicked
      +    }
      +    ...
      +}
      +
      + +

      Notice that the onClick() callback in the above example has +no return value, but some other event listener methods must return a boolean. The reason +depends on the event. For the few that do, here's why:

      +
        +
      • {@link android.view.View.OnLongClickListener#onLongClick(View) onLongClick()} - + This returns a boolean to indicate whether you have consumed the event and it should not be carried further. + That is, return true to indicate that you have handled the event and it should stop here; + return false if you have not handled it and/or the event should continue to any other + on-click listeners.
      • +
      • {@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent) onKey()} - + This returns a boolean to indicate whether you have consumed the event and it should not be carried further. + That is, return true to indicate that you have handled the event and it should stop here; + return false if you have not handled it and/or the event should continue to any other + on-key listeners.
      • +
      • {@link android.view.View.OnTouchListener#onTouch(View,MotionEvent) onTouch()} - + This returns a boolean to indicate whether your listener consumes this event. The important thing is that + this event can have multiple actions that follow each other. So, if you return false when the + down action event is received, you indicate that you have not consumed the event and are also + not interested in subsequent actions from this event. Thus, you will not be called for any other actions + within the event, such as a fingure gesture, or the eventual up action event.
      • +
      + +

      Remember that key events are always delivered to the View currently in focus. They are dispatched starting from the top +of the View hierarchy, and then down, until they reach the appropriate destination. If your View (or a child of your View) +currently has focus, then you can see the event travel through the {@link android.view.View#dispatchKeyEvent(KeyEvent) +dispatchKeyEvent()} method. As an alternative to capturing key events through your View, you can also receive +all of the events inside your Activity with {@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()} +and {@link android.app.Activity#onKeyUp(int,KeyEvent) onKeyUp()}.

      + +

      Note: Android will call event handlers first and then the appropriate default +handlers from the class definition second. As such, returning true from these event listeners will stop +the propagation of the event to other event listeners and will also block the callback to the +default event handler in the View. So be certain that you want to terminate the event when you return true.

      + + +

      Event Handlers

      + +

      If you're building a custom component from View, then you'll be able to define several callback methods +used as default event handlers. +In the document on Building Custom Components, +you'll learn see some of the common callbacks used for event handling, including:

      +
        +
      • {@link android.view.View#onKeyDown} - Called when a new key event occurs.
      • +
      • {@link android.view.View#onKeyUp} - Called when a key up event occurs.
      • +
      • {@link android.view.View#onTrackballEvent} - Called when a trackball motion event occurs.
      • +
      • {@link android.view.View#onTouchEvent} - Called when a touch screen motion event occurs.
      • +
      • {@link android.view.View#onFocusChanged} - Called when the view gains or loses focus.
      • +
      +

      There are some other methods that you should be awere of, which are not part of the View class, +but can directly impact the way you're able to handle events. So, when managing more complex events inside +a layout, consider these other methods:

      +
        +
      • {@link android.app.Activity#dispatchTouchEvent(MotionEvent) + Activity.dispatchTouchEvent(MotionEvent)} - This allows your {@link + android.app.Activity} to intercept all touch events before they are dispatched to the window.
      • +
      • {@link android.view.ViewGroup#onInterceptTouchEvent(MotionEvent) + ViewGroup.onInterceptTouchEvent(MotionEvent)} - This allows a {@link + android.view.ViewGroup} to watch events as they are dispatched to child Views.
      • +
      • {@link android.view.ViewParent#requestDisallowInterceptTouchEvent(boolean) + ViewParent.requestDisallowInterceptTouchEvent(boolean)} - Call this + upon a parent View to indicate that it should not intercept touch events with {@link + android.view.ViewGroup#onInterceptTouchEvent(MotionEvent)}.
      • +
      + +

      Touch Mode

      +

      +When a user is navigating a user interface with directional keys or a trackball, it is +necessary to give focus to actionable items (like buttons) so the user can see +what will accept input. If the device has touch capabilities, however, and the user +begins interacting with the interface by touching it, then it is no longer necessary to +highlight items, or give focus to a particular View. Thus, there is a mode +for interaction named "touch mode." +

      +

      +For a touch-capable device, once the user touches the screen, the device +will enter touch mode. From this point onward, only Views for which +{@link android.view.View#isFocusableInTouchMode} is true will be focusable, such as text editing widgets. +Other Views that are touchable, like buttons, will not take focus when touched; they will +simply fire their on-click listeners when pressed. +

      +

      +Any time a user hits a directional key or scrolls with a trackball, the device will +exit touch mode, and find a view to take focus. Now, the user may resume interacting +with the user interface without touching the screen. +

      +

      +The touch mode state is maintained throughout the entire system (all windows and activities). +To query the current state, you can call +{@link android.view.View#isInTouchMode} to see whether the device is currently in touch mode. +

      + + +

      Handling Focus

      + +

      The framework will handle routine focus movement in response to user input. +This includes changing the focus as Views are removed or hidden, or as new +Views become available. Views indicate their willingness to take focus +through the {@link android.view.View#isFocusable()} method. To change whether a View can take +focus, call {@link android.view.View#setFocusable(boolean) setFocusable()}. When in touch mode, +you may query whether a View allows focus with {@link android.view.View#isFocusableInTouchMode()}. +You can change this with {@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode()}. +

      + +

      Focus movement is based on an algorithm which finds the nearest neighbor in a +given direction. In rare cases, the default algorithm may not match the +intended behavior of the developer. In these situations, you can provide +explicit overrides with the following XML attributes in the layout file: +nextFocusDown, nextFocusLeft, nextFocusRight, and +nextFocusUp. Add one of these attributes to the View from which +the focus is leaving. Define the value of the attribute to be the id of the View +to which focus should be given. For example:

      +
      +<LinearLayout
      +    android:orientation="vertical"
      +    ... >
      +  <Button android:id="@+id/top"
      +          android:nextFocusUp="@+id/bottom"
      +          ... />
      +  <Button android:id="@+id/bottom"
      +          android:nextFocusDown="@+id/top"
      +          ... />
      +</LinearLayout>
      +
      + +

      Ordinarily, in this vertical layout, navigating up from the first Button would not go +anywhere, nor would navigating down from the second Button. Now that the top Button has +defined the bottom one as the nextFocusUp (and vice versa), the navigation focus will +cycle from top-to-bottom and bottom-to-top.

      + +

      If you'd like to declare a View as focusable in your UI (when it is traditionally not), +add the android:focusable XML attribute to the View, in your layout declaration. +Set the value true. You can also declare a View +as focusable while in Touch Mode with android:focusableInTouchMode.

      +

      To request a particular View to take focus, call {@link android.view.View#requestFocus()}.

      +

      To listen for focus events (be notified when a View receives or looses focus), use +{@link android.view.View.OnFocusChangeListener#onFocusChange(View,boolean) onFocusChange()}, +as discussed in the Event Listeners section, above.

      + + + + diff --git a/docs/html/guide/topics/views/declaring-layout.jd b/docs/html/guide/topics/views/declaring-layout.jd deleted file mode 100644 index 43afdf7c38627..0000000000000 --- a/docs/html/guide/topics/views/declaring-layout.jd +++ /dev/null @@ -1,228 +0,0 @@ -page.title=Declaring Layout -parent.title=Views and Layout -parent.link=index.html -@jd:body - -
      - -
      - -

      You can create your application's user interface in two ways: -

        -
      • Declare UI elements statically, in XML. Android provides a straightforward XML -vocabulary that corresponds to the View classes and subclasses, such as those for widgets and layouts.
      • -
      • Instantiate screen elements at runtime. Your -application can refer to or create View or other class objects and manipulate their properties programmatically.
      • -
      - -

      One advantage of declaring your UI in XML is that it enables you to better separate the presentation of your application from the code that controls it's behavior. Your UI description is external to your application code, which means that you can modify or adapt it without having to modify your source code and recompile. For example, you can create XML layouts for different screen orientations and for a variety of device screen sizes or languages. Additionally, declaring in XML makes it easier to see the elements and structure of your UI, so it's easier to debug problems.

      - - - -

      The Android framework gives you the flexibility to use either or both of these ways of declaring and managing your application's UI. For example, you could declare your application's default layouts in XML, including the screen elements that will appear in them and their properties. You could then add code in your application that would modify the state of the screen objects, including those declared in XML, at run time.

      - -

      You build your application's UI in approximately the same way, whether you are declaring it in XML or programmatically. In both cases, your UI will be a tree structure that may include multiple View or Viewgroup subclasses.

      - -

      In general, the XML vocabulary for declaring UI elements closely follows the structure and naming of the framework's UI-related classes and methods, where element names correspond to class names and attribute names correspond to methods. In fact, the correspondence is often so direct that you can guess what XML attribute corresponds to a class method, or guess what class corresponds to a given xml element.

      - -

      However, note that the XML vocabulary for defining UI is not entirely identical to the framework's classes and methods. In some cases, there are slight naming differences. For -example, the EditText element has a text attribute that corresponds to -EditText.setText().

      - - - -

      Using Android's XML vocabulary, you can quickly design UI layouts and the screen elements they contain, in the same way you create HTML files — as a series of nested tags.

      - -

      Each layout file must contain exactly one root element, and the root element must be a View or ViewGroup object. Once you've defined the root element, you can add additional layout objects or controls as child elements of the root element, if needed. In the example below, the tree of XML elements evaluates to the outermost LinearLayout object. - -

      After you've declared your layout in XML, you must save the file, with the .xml extension, -in your application's res/layout/ directory, so it will properly compile.

      - -

      When you compile your application, each XML layout file is compiled into an -android.view.View resource. You can then load the layout resource from your application code, by calling setContentView(R.layout.layout_file_name) in your {@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()} -implementation.

      - -

      When you load a layout resource, the Android system initializes run-time objects corresponding to the elements in your layout. It parses the elements of your layout in-order (depth-first), instantiating the Views and adding them to their parent(s). -See How Android Draws Views for more information.

      - -

      Attributes named layout_something apply to that -object's LayoutParams member. Layout -Resources also describes how to learn the syntax for specifying -LayoutParams properties.

      - -

      Also note that Android draws elements in the order in which they -appear in the XML. Therefore, if elements overlap, the last one in the XML -file will probably be drawn on top of any previously listed elements in that -same space.

      - -

      The following values are supported for dimensions (described in {@link -android.util.TypedValue TypedValue}):

      - -
        -
      • px (pixels)
      • -
      • dip (device independent pixels)
      • -
      • sp (scaled pixels — best for text size)
      • -
      • pt (points)
      • -
      • in (inches)
      • -
      • mm (millimeters)
      • -
      - -

      Example: android:layout_width="25px"

      - -

      For more information about these dimensions, see Dimension Values.

      - -

      The example below shows an XML file and the resulting screen in the UI. Note that the text on the -top of the screen was set by calling {@link -android.app.Activity#setTitle(java.lang.CharSequence) Activity.setTitle}. Note -that the attributes that refer to relative elements (i.e., layout_toLeft) -refer to the ID using the syntax of a relative resource -(@id/id_number).

      - - - - - - -
      -
      <?xml version="1.0" encoding="utf-8"?>
      -<!-- Demonstrates using a relative layout to create a form -->
      -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android
      -                android:layout_width="fill_parent" 
      -                android:layout_height="wrap_content"
      -                android:background="@drawable/blue"
      -                android:padding="10px">
      -
      -    <TextView id="@+id/label" 
      -              android:layout_width="fill_parent" 
      -              android:layout_height="wrap_content" 
      -              android:text="Type here:"/>
      -
      -    <EditText id="@+id/entry" 
      -              android:layout_width="fill_parent" 
      -              android:layout_height="wrap_content" 
      -              android:background="@android:drawable/editbox_background"
      -              android:layout_below="@id/label"/>
      -  
      -    <Button id="@+id/ok" 
      -            android:layout_width="wrap_content" 
      -            android:layout_height="wrap_content" 
      -            android:layout_below="@id/entry"
      -            android:layout_alignParentRight="true"
      -            android:layout_marginLeft="10px"
      -            android:text="OK" />
      -
      -    <Button android:layout_width="wrap_content" 
      -            android:layout_height="wrap_content"
      -            android:layout_toLeftOf="@id/ok"
      -            android:layout_alignTop="@id/ok"
      -            android:text="Cancel" />
      -</RelativeLayout>
      Screen shot showing how this layout XML file is rendered.
      - - -

      Loading the XML Resource

      -

      Loading the compiled layout resource is very easy, and done with a single -call in the activity's onCreate() method, as shown here:

      - -
      -protected void onCreate(Bundle savedInstanceState)
      -{
      -   // Be sure to call the super class.
      -   super.onCreate(savedInstanceState);
      -
      -   // Load the compiled layout resource into the window's
      -   // default ViewGroup.
      -   // The source file is res/layout/hello_activity.xml
      -    setContentView(R.layout.hello_activity);
      -  
      -   // Retrieve any important stored values.
      -   restoreValues(savedInstanceState);
      -} 
      - -

      Position

      -

      - The geometry of a view is that of a rectangle. A view has a location, - expressed as a pair of left and top coordinates, and - two dimensions, expressed as a width and a height. The unit for location - and dimensions is the pixel. -

      - -

      - It is possible to retrieve the location of a view by invoking the methods - {@link android.view.View#getLeft()} and {@link android.view.View#getTop()}. The former returns the left, or X, - coordinate of the rectangle representing the view. The latter returns the - top, or Y, coordinate of the rectangle representing the view. These methods - both return the location of the view relative to its parent. For instance, - when getLeft() returns 20, that means the view is located 20 pixels to the - right of the left edge of its direct parent. -

      - -

      - In addition, several convenience methods are offered to avoid unnecessary - computations, namely {@link android.view.View#getRight()} and {@link android.view.View#getBottom()}. - These methods return the coordinates of the right and bottom edges of the - rectangle representing the view. For instance, calling {@link android.view.View#getRight()} - is similar to the following computation: getLeft() + getWidth(). -

      - - -

      Size, Padding and Margins

      -

      - The size of a view is expressed with a width and a height. A view actually - possess two pairs of width and height values. -

      - -

      - The first pair is known as measured width and - measured height. These dimensions define how big a view wants to be - within its parent. The - measured dimensions can be obtained by calling {@link android.view.View#getMeasuredWidth()} - and {@link android.view.View#getMeasuredHeight()}. -

      - -

      - The second pair is simply known as width and height, or - sometimes drawing width and drawing height. These - dimensions define the actual size of the view on screen, at drawing time and - after layout. These values may, but do not have to, be different from the - measured width and height. The width and height can be obtained by calling - {@link android.view.View#getWidth()} and {@link android.view.View#getHeight()}. -

      - -

      - To measure its dimensions, a view takes into account its padding. The padding - is expressed in pixels for the left, top, right and bottom parts of the view. - Padding can be used to offset the content of the view by a specific amount of - pixels. For instance, a left padding of 2 will push the view's content by - 2 pixels to the right of the left edge. Padding can be set using the - {@link android.view.View#setPadding(int, int, int, int)} method and queried by calling - {@link android.view.View#getPaddingLeft()}, {@link android.view.View#getPaddingTop()}, - {@link android.view.View#getPaddingRight()} and {@link android.view.View#getPaddingBottom()}. -

      - -

      - Even though a view can define a padding, it does not provide any support for - margins. However, view groups provide such a support. Refer to - {@link android.view.ViewGroup} and - {@link android.view.ViewGroup.MarginLayoutParams} for further information. -

      - \ No newline at end of file diff --git a/docs/html/guide/topics/views/how-android-draws.jd b/docs/html/guide/topics/views/how-android-draws.jd deleted file mode 100644 index f497db7d919c6..0000000000000 --- a/docs/html/guide/topics/views/how-android-draws.jd +++ /dev/null @@ -1,90 +0,0 @@ -page.title=How Android Draws Views -parent.title=Views and Layout -parent.link=index.html -@jd:body - - -

      As mentioned in the introduction to Views and Layout, -drawing begins when the Activity requests the root node of the layout to measure and -draw the layout tree. Drawing is handled by walking the tree and rendering each view that - intersects the invalid region. In turn, each view group is responsible for requesting -each of its children to be drawn and each view is responsible for drawing itself. - Because the tree is traversed in-order, - this means that parents will be drawn before (i.e., behind) their children, with - siblings drawn in the order they appear in the tree. -

      - - - -

      - Drawing the layout is a two pass process: a measure pass and a layout pass. The measuring - pass is implemented in {@link android.view.View#measure(int, int)} and is a top-down traversal - of the view tree. Each view pushes dimension specifications down the tree - during the recursion. At the end of the measure pass, every view has stored - its measurements. The second pass happens in - {@link android.view.View#layout(int,int,int,int)} and is also top-down. During - this pass each parent is responsible for positioning all of its children - using the sizes computed in the measure pass. -

      - -

      - When a view's measure() method returns, its {@link android.view.View#getMeasuredWidth()} and - {@link android.view.View#getMeasuredHeight()} values must be set, along with those for all of - that view's descendants. A view's measured width and measured height values - must respect the constraints imposed by the view's parents. This guarantees - that at the end of the measure pass, all parents accept all of their - children's measurements. A parent view may call measure() more than once on - its children. For example, the parent may measure each child once with - unspecified dimensions to find out how big they want to be, then call - measure() on them again with actual numbers if the sum of all the children's - unconstrained sizes is too big or too small (i.e., if the children don't agree among themselves - as to how much space they each get, the parent will intervene and set the rules on the second pass). -

      - - - -

      - The measure pass uses two classes to communicate dimensions. The - {@link android.view.View.MeasureSpec} class is used by views to tell their parents how they - want to be measured and positioned. The base LayoutParams class just - describes how big the view wants to be for both width and height. For each - dimension, it can specify one of:

      -
        -
      • an exact number -
      • FILL_PARENT, which means the view wants to be as big as its parent - (minus padding)
      • -
      • WRAP_CONTENT, which means that the view wants to be just big enough to - enclose its content (plus padding).
      • -
      -

      There are subclasses of LayoutParams for different subclasses of ViewGroup. - For example, AbsoluteLayout has its own subclass of LayoutParams which adds - an X and Y value. -

      - -

      - MeasureSpecs are used to push requirements down the tree from parent to - child. A MeasureSpec can be in one of three modes:

      -
        -
      • UNSPECIFIED: This is used by a parent to determine the desired dimension - of a child view. For example, a LinearLayout may call measure() on its child - with the height set to UNSPECIFIED and a width of EXACTLY 240 to find out how - tall the child view wants to be given a width of 240 pixels.
      • -
      • EXACTLY: This is used by the parent to impose an exact size on the - child. The child must use this size, and guarantee that all of its - descendants will fit within this size.
      • -
      • AT_MOST: This is used by the parent to impose a maximum size on the - child. The child must gurantee that it and all of its descendants will fit - within this size.
      • -
      - - - diff --git a/docs/html/guide/topics/views/index.jd b/docs/html/guide/topics/views/index.jd deleted file mode 100644 index a246ba183f81a..0000000000000 --- a/docs/html/guide/topics/views/index.jd +++ /dev/null @@ -1,216 +0,0 @@ -page.title=Views and Layout -@jd:body - -
      -
      - -

      Key classes and packages

      -
        -
      1. {@link android.view.View}
      2. -
      3. {@link android.view.ViewGroup}
      4. -
      5. {@link android.widget}
      6. -
      - -

      In this document

      -
        -
      1. View Hierarchy
      2. -
      3. Layout
      4. -
      5. Widgets
      6. -
      7. Events
      8. -
      9. Adapters
      10. -
      11. Styles and Themes
      12. -
      -
      -
      - -

      In an Android application, a user interface is built using {@link android.view.View} and -{@link android.view.ViewGroup} objects. There are many types of views and view groups, each of which -is a descendant of the {@link android.view.View} class.

      - -

      View objects are the basic units of user interface expression on the Android platform. -The View class serves as the base for subclasses called "widgets," which offer fully implemented -UI objects, like text fields and buttons. The ViewGroup class serves as the base for subclasses called "layouts," -which offer different kinds of layout architecture, like linear, tabular and relative.

      - -

      A View object is a data structure whose properties store the layout properties and content for a specific -rectangular area of the screen. A View object handles its own measurement, layout, drawing, focus change, -scrolling, and key/gesture interactions for the rectangular screen area it represents.

      - - -

      View Hierarchy

      - -

      On the Android platform, you will define an Activity's UI using a hierarchy of view and view group nodes, -as shown in the diagram below. This hierarchy tree can be as simple or complex as you need it to be, and you -can build it up using Android's set of predefined widgets and layouts, or with custom view types that you -create yourself.

      - - - -

      The Android system will notify your Activity when it becomes active and receives focus. -In order to attach the view hierarchy tree to the screen for rendering, your Activity must call its -setContentView() method and pass a reference to the root node object. The Android system -receives this reference so that it can invalidate, measure, and draw the tree. The root node requests -that its child nodes draw themselves — in turn, each view group node is responsible for -calling Draw() on each of its own child views, so that each child view can render itself. -The children may request a size and location within the parent, but the parent object has the final -decision on where how big each child can be. To learn more about how a view group and its children are measured -and drawn, read How Android Draws Views.

      - - -

      Layout

      - -

      The most common way to define your layout and express the view hierarchy is with an XML layout file. -XML offers a human-readable structure for the layout, much like HTML. Each element in XML is -(usually a descendant of) either a View or ViewGroup object. View objects are leaves in the tree, -ViewGroup objects are branches in the tree. The name of an XML element -is the respective class object name. So a <TextView> element creates -a {@link android.widget.TextView} widget in your UI, and a <LinearLayout> element creates -a {@link android.widget.LinearLayout} layout branch in the tree. To learn more about how to write your layout in XML, -read Declaring and Querying Layout. - -

      - -

      There are a variety of ways in which you can layout your views, using different view groups. -Some pre-defined view groups offered by Android (called layouts) include LinearLayout, RelativeLayout, AbsoluteLayout, -TableLayout, GridLayout and others. Each offers a different mechanisms for arranging child views -among each other. -To learn more about how to use some of these view groups for your layout, -read Common Layout Objects. - -

      IDs

      - -

      Views may have an integer ID associated with them. The ID is written as a string, but once the application is -compiled, it will be referenced as an integer. -These IDs are typically assigned in the layout XML file as an attribute of the view, -and are used to uniquely identify a view within the tree. The usual syntax for an ID, inside an XML tag is:

      -
      id="@+id/my_button"
      -

      The at-symbol (@) at the beginning of the string indicates that the XML parser should parse and expand the rest -of the ID string and identify it as an ID resource. The plus-symbol (+) means that this is a new resource name that must -be created and added to our resources (in the R.java file). There are a number of other ID resources that -are offered by the Android framework. When referencing an Android resource ID, you do not need the plus-symbol, -but must add the android package namespace, like so:

      -
      id="@android:id/empty"
      -

      With the android package namespace in place, we're now referencing an ID from the android.R -resources class, rather than the local resources class.

      - -

      In order to create views and reference them from the application, a common pattern is to:

      -
        -
      1. Define a view/widget in the layout file and assign it a unique ID. E.g.: -
        -<Button id="@+id/my_button"
        -  android:layout_width="wrap_content"
        -  android:layout_height="wrap_content"
        -  android:text="@string/my_button_text"/>
        -
        -
      2. -
      3. Then create an instance of the view object and capture it from the layout -(typically in the onCreate() method). -
        -Button myButton = (Button) findViewById(R.id.my_button);
        -
        -
      4. -
      -

      Defining IDs for view objects is important when creating a {@link android.widget.RelativeLayout}. -In a relative layout, sibling views can define their layout relative to another sibling view, -which is referenced by the unique ID.

      -

      An ID need not be unique throughout the entire tree, but it should be -unique within the part of the tree you are searching (which may often be the entire tree, so it's best -to be completely unique when possible).

      - - -

      Layout Parameters

      - -

      Every ViewGroup class implements a nested class that extends {@link -android.view.ViewGroup.LayoutParams}. This subclass -contains property types that define each child view's size and position, as -appropriate for that type of view group. As you can see in the figure below, the parent -view group defines layout parameters for each child view (including the child view group).

      - - - -

      Note that every LayoutParams subclass has its own syntax for setting -values. Each child element must define LayoutParams that are appropriate for its parent, -though it may also define different LayoutParams for its own children.

      - -

      All view groups include a width and height, and each view is required to define them. -Many LayoutParams also include optional margins and -borders. You can specify width and height with exact measurements, though you probably won't want -to do this often. More often, you will tell your view to size itself either to -the dimensions required by its content, or to become as big as its parent view group -will allow (with the wrap_content and fill_parent values, respectively). -The accepted measurement types are defined in the -Available Resources document.

      - -

      Widgets

      - - -

      The View class also serves as a base class for widgets — a set of fully implemented -View subclasses that draw interactive screen elements, so you can quickly build your UI. -Android provides a vast collection of widgets for special actions. -Some of them are basic interaction elements like buttons and text fields, while others are more complex, -like a date picker or zoom controls.

      -

      For a list of all built-in widgets, see the {@link android.widget widget}.

      - -

      You're not limited to the kinds of views, layouts and widgets provided by the Android platform. If you'd -like to do something more customized, create your own widget or layout, you can. -Read more in Building Custom Components. - - -

      Events

      - -

      Once you've designed and built your UI layout, you need to handle the user interaction with it. -Obviously, the views that you've implemented in the layout are the -receptors for such interaction events. Because the View class is built to listen for most interaction events, -receiving and handling them is pretty straigh-forward. When you want to perform an action upon an event, -you need to do one of two things:

      -
        -
      • Override an existing callback method for the view you've implemented, which will be called when something -like a touch or focus event occurs.
      • -
      • Define a listener interface, like {@link android.view.View.OnClickListener} (for handling "clicks" on a View). -You must then define the listener for your view with the respective set...Listener() -method (such as {@link android.view.View#setOnClickListener(android.view.View.OnClickListener) setOnCLickListener()}).
      • -
      - -

      To learn more about handling events and writing your own listeners, -read Handling UI Events.

      - - -

      Adapters

      - -

      Sometimes you'll want to populate a view group with some information that can't be hard-coded, instead, -you want to bind your view to an external source of data. To do this, you use an AdapterView as -your view group and each child View is initialized and populated with data from the Adapter.

      -

      The AdapterView object is an implementation of ViewGroup that determines its child views -based on a given Adapter object. The Adapter acts like a courier between your data source (perhaps an -array of external strings) and the AdapterView, which displays it. There are several implementations -of the Adapter class, for specific tasks, such as the CursorAdapter for reading database data from a Cursor, -or an ArrayAdapter for reading from an arbitrary array.

      -

      To learn more about using an Adapter to populate your views, read -Binding to Data with AdapterView.

      - - -

      Styles and Themes

      - -

      Perhaps you're not satisfied with the look of the standard widgets. To revise them, you can create some -of your own styles and themes.

      - -
        -
      • A style is a set of one or more formatting attributes that you can apply as a unit to individual elements -in your layout. For example, you could define a style that specifies a certain text size and color, then -apply it to only specific View elements.
      • -
      • A theme is a set of one or more formatting attributes that you can apply as a unit to all activities in -an application, or just a single activity. For example, you could define a theme that sets specific colors for -the window frame and the panel background, and sets text sizes and colors for menus. This theme can then be -applied to specific activities or the entire application.
      • -
      - -

      Styles and themes are resources. Android provides some default style and theme resources that you can use, -or you can declare your own custom style and theme resources. Learn more about using styles and themes in the -Applying Styles and Themes document.

      diff --git a/docs/html/guide/topics/views/ui-events.jd b/docs/html/guide/topics/views/ui-events.jd deleted file mode 100644 index 96136f8ad4ab7..0000000000000 --- a/docs/html/guide/topics/views/ui-events.jd +++ /dev/null @@ -1,113 +0,0 @@ -page.title=Handling UI Events -parent.title=Views and Layout -parent.link=index.html -@jd:body - -
      -
      -

      In this document

      -
        -
      1. Touch Mode
      2. -
      3. Scrolling
      4. -
      5. Event Cycle
      6. -
      - -

      See also

      -
        -
      1. Hello Form Stuff tutorial
      2. -
      -
      -
      - -

      Many Android classes declare callback methods for handling relevant UI events such as keypresses, -touch events, focus changes, and so on. For example, {@link android.app.Activity Activity} provides -the methods onKeyDown() and onKeyUp() and {@link android.widget.TextView TextView} -provides onFocusChanged().

      - -

      In most cases, you can handle events just by overriding the appropriate handler methods. -When an event is received, the Android system calls your handler method with the event data.

      - -

      However, some classes do not declare handler methods for specific events. For example, -{@link android.widget.Button} does not declare an onClick() handler method. So, to handle a click event, -you need to create an anonymous class to act as a listener for the event, then register the listener with the -target class object (via the appropriate set...Listener() method). The example below shows how to set -up a handler for click events in a Button object.

      - -

      -
      public class ExampleSendResult extends Activity
      -{
      -    protected void onCreate(Bundle savedValues)
      -    {
      -        ...
      -
      -        // Capture our button from layout and listen for clicks.
      -        Button button = (Button)findViewById(R.id.corky);
      -        button.setOnClickListener(mCorkyListener);
      -    }
      -
      -    // Create an anonymous class to act as a button click listener.
      -    private OnClickListener mCorkyListener = new OnClickListener()
      -    {
      -        public void onClick(View v)
      -        {
      -          //do something when the button is clicked
      -        }
      -    };
      -}
      -
      - - -

      Touch Mode

      -

      - When a user is navigating a user interface via directional keys such as a D-pad, it is - necessary to give focus to actionable items such as buttons so the user can see - what will take input. If the device has touch capabilities, however, and the user - begins interacting with the interface by touching it, it is no longer necessary to - always highlight, or give focus to, a particular view. Thus, there is a mode - for interaction named "touch mode." -

      -

      - For a touch-capable device, once the user touches the screen, the device - will enter touch mode. From this point onward, only views for which - {@link android.view.View#isFocusableInTouchMode} is true will be focusable, such as text editing widgets. - Other views that are touchable, like buttons, will not take focus when touched; they will - simply fire their on-click listeners when pressed. -

      -

      - Any time a user hits a directional key, such as a D-pad direction, the view device will - exit touch mode, and find a view to take focus, so that the user may resume interacting - with the user interface without touching the screen. -

      -

      - The touch mode state is maintained across {@link android.app.Activity}s. To query the current state, you can call - {@link android.view.View#isInTouchMode} to see whether the device is currently in touch mode. -

      - - -

      Scrolling

      -

      - The framework provides basic support for views that wish to internally - scroll their content. This includes keeping track of the X and Y scroll - offset as well as mechanisms for drawing scrollbars. See - {@link android.view.View#scrollBy(int, int)}, {@link android.view.View#scrollTo(int, int)} for more details. -

      - -

      Event Cycle

      -

      The basic cycle of a view is as follows:

      -
        -
      1. An event comes in and is dispatched to the appropriate view. The view - handles the event and notifies any listeners.
      2. -
      3. If, in the course of processing the event, the view's bounds may need - to be changed, the view will call {@link android.view.View#requestLayout()}.
      4. -
      5. Similarly, if in the course of processing the event the view's appearance - may need to be changed, the view will call {@link android.view.View#invalidate()}.
      6. -
      7. If either {@link android.view.View#requestLayout()} or {@link android.view.View#invalidate()} were called, - the framework will take care of measuring, laying out, and drawing the tree - as appropriate.
      8. -
      - -

      Note: The entire view tree is single threaded. You must always be on - the UI thread when calling any method on any view. - If you are doing work on other threads and want to update the state of a view - from that thread, you should use a {@link android.os.Handler}. -

      \ No newline at end of file diff --git a/docs/html/guide/tutorials/hello-world.jd b/docs/html/guide/tutorials/hello-world.jd index c9930484de4c3..bbc4f77987305 100644 --- a/docs/html/guide/tutorials/hello-world.jd +++ b/docs/html/guide/tutorials/hello-world.jd @@ -330,7 +330,7 @@ you saw before! After all, the point was to show that the two different layout approaches produce identical results.

      There's a lot more to creating these XML layouts, but that's as far as we'll go -here. Read the Views and Layout documentation for more +here. Read the User Interface documentation for more information on creating layouts.

      diff --git a/docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip b/docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip old mode 100755 new mode 100644 index 86f5e9dc5e6b7..c7dd989f2eba8 Binary files a/docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip and b/docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip differ diff --git a/docs/html/guide/tutorials/notepad/notepad-ex1.jd b/docs/html/guide/tutorials/notepad/notepad-ex1.jd index 45ed97eacd69c..b7f42bf095839 100644 --- a/docs/html/guide/tutorials/notepad/notepad-ex1.jd +++ b/docs/html/guide/tutorials/notepad/notepad-ex1.jd @@ -1,4 +1,6 @@ page.title=Notepad Exercise 1 +parent.title=Notepad Tutorial +parent.link=index.html @jd:body @@ -584,5 +586,4 @@ the zip file to compare with your own.

      Once you are ready, move on to Tutorial Exercise 2 to add the ability to create, edit and delete notes.

      -

      Back to the Tutorial main page...

      diff --git a/docs/html/guide/tutorials/notepad/notepad-ex2.jd b/docs/html/guide/tutorials/notepad/notepad-ex2.jd index b4608fb970383..3b8fa0bc09039 100644 --- a/docs/html/guide/tutorials/notepad/notepad-ex2.jd +++ b/docs/html/guide/tutorials/notepad/notepad-ex2.jd @@ -1,9 +1,12 @@ page.title=Notepad Exercise 2 +parent.title=Notepad Tutorial +parent.link=index.html @jd:body

      In this exercise, you will add a second Activity to your notepad application, to let the user -create, edit, and delete notes. The new Activity assumes responsibility for creating new notes by +create and edit notes. You will also allow the user to delete existing notes through a context menu. +The new Activity assumes responsibility for creating new notes by collecting user input and packing it into a return Bundle provided by the intent. This exercise demonstrates:

        @@ -11,6 +14,7 @@ demonstrates:

      • Invoking another Activity asynchronously using startActivityForResult()
      • Passing data between Activity in Bundle objects
      • How to use a more advanced screen layout
      • +
      • How to create a context menu
      @@ -51,7 +55,8 @@ Tools > Fix Project Properties.

    • There are also a couple of new overridden methods - (onListItemClick() and onActivityResult()) + (onCreateContextMenu(), onContextItemSelected(), + onListItemClick() and onActivityResult()) which we will be filling in below.
    @@ -59,59 +64,62 @@ Tools > Fix Project Properties.

    Step 2

    -

    Add an entry to the menu for deleting a note:

    + + +

    First, let's create the context menu that will allow users to delete individual notes. +Open the Notepadv2 class.

    +
      +
    1. In order for each list item in the ListView to register for the context menu, we call + registerForContextMenu() and pass it our ListView. So, at the very end of + the onCreate() method add this line: +
      registerForContextMenu(getListView());
      +

      Because our Activity extends the ListActivity class, getListView() will return us + the local ListView object for the Activity. Now, each list item in this ListView will activate the + context menu.

    2. - In the onCreateOptionsMenu() method, add a new line: -
      menu.add(0, DELETE_ID, 0, R.string.menu_delete);
      -
    3. -
    4. - The whole method should now look like this:
      + Now fill in the onCreateContextMenu() method. This callback is similar to the other + menu callback used for the options menu. Here, we add just one line, which will add a menu item + to delete a note. Call menu.add() like so:
      -@Override
      -public boolean onCreateOptionsMenu(Menu menu) {
      -    super.onCreateOptionsMenu(menu);
      -    menu.add(0, INSERT_ID, 0, R.string.menu_insert);
      +public boolean onCreateContextMenu(Menu menu, View v
      +        ContextMenuInfo menuInfo) {
      +    super.onCreateContextMenu(menu, v, menuInfo);
           menu.add(0, DELETE_ID, 0, R.string.menu_delete);
      -    return true;
       }
      +

      The onCreateContextMenu() callback some passes other information in addition to the Menu object, + such as the View that has been triggered for the menu and + an extra object that may contain additional information about the object selected. However, we don't care about + these here, because we only have one kind of object in the Activity that uses context menus. In the next + step, we'll handle the menu item selection.

    Step 3

    -

    In the onMenuItemSelected() method, add a new case for - DELETE_ID:

    -
    -mDbHelper.deleteNote(getListView().getSelectedItemId());
    -fillData();
    -return true;
    - -
      -
    1. - Here, we use the deleteNote method to remove the note specified by ID. - In order to get the ID, we call getListView().getSelectedItemId(). -
    2. -
    3. - Then we fill the data to keep everything up to date. -
    4. -
    -

    - The whole method should now look like this:

    -
    -@Override
    -public boolean onMenuItemSelected(int featureId, MenuItem item) {
    +  

    Now that the we've registered our ListView for a context menu and defined our context menu item, we need + to handle the callback when it is selected. For this, we need to identify the list ID of the + selected item, then delete it. So fill in the + onContextItemSelected() method like this:

    +
    +public boolean onContextItemSelected(MenuItem item) {
         switch(item.getItemId()) {
    -    case INSERT_ID:
    -        createNote();
    -        return true;
         case DELETE_ID:
    -        mDbHelper.deleteNote(getListView().getSelectedItemId());
    +        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
    +        mDbHelper.deleteNote(info.id);
             fillData();
             return true;
         }
    -       
    -    return super.onMenuItemSelected(featureId, item);
    +    return super.onContextItemSelected(item);
     }
    +

    Here, we retrieve the {@link android.widget.AdapterView.AdapterContextMenuInfo AdapterContextMenuInfo} +with {@link android.view.MenuItem#getMenuInfo()}. The id field of this object tells us +the position of the item in the ListView. We then pass this to the deleteNote() +method of our NotesDbAdapter and the note is deleted. That's it for the context menu — notes +can now be deleted.

    Step 4

    Finally, the new Activity has to be defined in the manifest file:

    @@ -636,5 +644,4 @@ Once you are ready, move on to Tutorial Exercise 3 where you will fix the problems with the back button and lost edits by introducing a proper life cycle into the NoteEdit Activity.

    -

    Back to the Tutorial main page....

    diff --git a/docs/html/guide/tutorials/notepad/notepad-ex3.jd b/docs/html/guide/tutorials/notepad/notepad-ex3.jd index a2eaa48dca741..8737280cceae7 100644 --- a/docs/html/guide/tutorials/notepad/notepad-ex3.jd +++ b/docs/html/guide/tutorials/notepad/notepad-ex3.jd @@ -1,4 +1,6 @@ page.title=Notepad Exercise 3 +parent.title=Notepad Tutorial +parent.link=index.html @jd:body @@ -354,4 +356,3 @@ the zip file to compare with your own.

    When you are ready, move on to the Tutorial Extra Credit exercise, where you can use the Eclipse debugger to examine the life-cycle events as they happen.

    -

    Back to the Tutorial main page...

    diff --git a/docs/html/guide/tutorials/notepad/notepad-extra-credit.jd b/docs/html/guide/tutorials/notepad/notepad-extra-credit.jd index f64e90ebee4c3..0d59b56b7a7b4 100644 --- a/docs/html/guide/tutorials/notepad/notepad-extra-credit.jd +++ b/docs/html/guide/tutorials/notepad/notepad-extra-credit.jd @@ -1,4 +1,6 @@ -page.title=Tutorial: Extra Credit +page.title=Notepad Extra Credit +parent.title=Notepad Tutorial +parent.link=index.html @jd:body @@ -65,6 +67,4 @@ when.

    your application development, but also superb profiling support. You can also try using Traceview to profile your application. If your application is running too slow, this can help you find the bottlenecks and fix them.

    -

    Back to the Tutorial main -page...

    diff --git a/docs/html/guide/tutorials/views/hello-mapview.jd b/docs/html/guide/tutorials/views/hello-mapview.jd index fcdf05644596d..b0f59de175e28 100644 --- a/docs/html/guide/tutorials/views/hello-mapview.jd +++ b/docs/html/guide/tutorials/views/hello-mapview.jd @@ -43,7 +43,7 @@ First, we'll create a simple Activity that can view and navigate a map. Then we The API key is generated using the MD5 fingerprint of your application certificate. For the purposes of this exercise, you should use the fingerprint of your debug certificate (which cannot be used to release your application for Android devices, but will work while developing). See how to - generate a fingerprint from your + generate a fingerprint from your debug certificate, then register the certificate to retieve an API key. Insert the API key as the value of the apiKey attribute. If you do not insert a valid diff --git a/docs/html/guide/tutorials/views/index.jd b/docs/html/guide/tutorials/views/index.jd index 6a6ac4b2ca2af..2248c68c16ad8 100644 --- a/docs/html/guide/tutorials/views/index.jd +++ b/docs/html/guide/tutorials/views/index.jd @@ -30,67 +30,67 @@ your strings.xml file.

    +

    Developer Announcements

    +
    + + Google I/O Developer Conference 2009 +
    +

    Google I/O is a two-day developer event that will take place May 27-28 at Moscone Center, San Francisco.

    +

    Learn more »

    +
    +
    @@ -27,51 +29,32 @@ page.title=Home
    - +
     
    - -
    - - - - - -
    -
    -

    Featured Developer App: BreadCrumbz

    -

    Amos Yoffe takes navigation to the next level with BreadCrumbz.

    -

    Learn more about this developer »

    -
    - -
    -
    - + - - - - - + @@ -79,16 +62,25 @@ page.title=Home - + + + + + +

    Download

    -

    Latest SDK

    -

    Download now

    +

    The Android SDK has the tools, sample code, and docs you need to create great apps.

    +

    Learn more »

     

    Market

    -
    -

    Android Market is an open service that will give developers the opportunity to distribute applications to handsets.

    +
    +

    Publish

    +

    Android Market is an open service that lets you distribute your apps to handsets.

    Learn more »

     

    Contribute

    -

    Create using our open source codebase and help us keep this project growing.

    +

    Android Open Source Project gives you access to the entire platform source.

    Learn more »

     
    +

    Watch

    + +

    More Android videos »

    +
    @@ -125,10 +117,10 @@ page.title=Home 'sdk': { 'layout':"imgLeft", 'icon':"sdk-small.png", - 'name':"SDK 1.0 r1", + 'name':"SDK 1.1 r1", 'img':"sdk-large.png", - 'title':"Android 1.0 SDK r1", - 'desc': "

    The first Android 1.0 SDK is available for download. This is the first release guaranteed to operate on the first Android devices. If you want to release an application for Android, you should be developing with this SDK.

    Download Android 1.0 SDK r1

    " + 'title':"Android 1.1 SDK r1", + 'desc': "

    A new Android SDK is available for download. The new SDK includes minor API changes, new UI localizations, bug fixes and some new application features.

    Download Android 1.1 SDK r1

    " }, 'mapskey': { @@ -137,7 +129,7 @@ page.title=Home 'name':"Maps API Key", 'img':"maps-large.png", 'title':"Maps API Key", - 'desc':"

    If you're writing an Android application that uses Google Maps (with MapView), you must register your application to obtain a Maps API Key. Without the key, your maps application will not work on Android devices. Obtaining a key requires just a couple of steps.

    Learn how to get a Maps API Key

    " + 'desc':"

    If you're writing an Android application that uses Google Maps (with MapView), you must register your application to obtain a Maps API Key. Without the key, your maps application will not work on Android devices. Obtaining a key requires just a couple of steps.

    Learn how to get a Maps API Key

    " }, 'market': { @@ -147,6 +139,15 @@ page.title=Home 'img':"market-large.png", 'title':"", 'desc': "

    Android Market helps you get your applications into the hands of users. The beta version of Market is now open and you can begin sharing your applications with users of the first Android-powered phone, the T-Mobile G1.

    Publish your Android app on Market »

    " + }, + + 'devphone': { + 'layout':"imgLeft", + 'icon':"devphone-small.png", + 'name':"Dev Phone 1", + 'img':"devphone-large.png", + 'title':"Android Dev Phone 1", + 'desc': "

    Run and debug your Android applications directly on this device. Modify and rebuild the Android operating system, and flash it onto the phone. The Android Dev Phone 1 is carrier independent, and available for purchase by any developer registered with Android Market.

    Learn more about the Android Dev Phone 1 »

    " } } diff --git a/docs/html/license.jd b/docs/html/license.jd new file mode 100644 index 0000000000000..39fb3a0930a82 --- /dev/null +++ b/docs/html/license.jd @@ -0,0 +1,142 @@ +page.title=Content License +@jd:body + +
    +

    Content License

    + +

    For the purposes of licensing, the content of this site is divided +into two categories:

    +
      +
    • Documentation content, found under the "Dev Guide" and "Reference" + tabs, including both static content and content extracted from source + code modules, as well as sample code, and
    • +
    • All other site content
    • +
    + +

    The documentation content on this site is made available to +you as part of the Android Open +Source Project. This documentation, including any code shown in it, +is licensed under the Apache 2.0 +license, the preferred license for all parts of the of the Android +Open Source Project.

    + +

    Apache 2.0 is a commercial and open-source-friendly software +license. The majority of the Android platform and documentation is +licensed under the Apache 2.0 license. While the project strives to +adhere to the preferred license, there may be exceptions, such as for +documentation (code comments) extracted from a source code module that +is licensed under GPLv2 or other license. In those cases, the license +covering the source code module will apply to the documentation +extracted from it.

    + +

    All other content on this site, except as otherwise noted, +is licensed under the Creative Commons +Attribution 2.5 license.

    + +

    You may use the content of this site in any way that is consistent +with the specific license that applies to the content, as described +above. For content licensed under Creative Commons Attribution 2.5, we +ask that you give proper attribution.

    + + +

    Terms of Use

    + +

    We are pleased to license the Android documentation and sample code under +terms that encourage you to take, modify, reuse, re-purpose, and remix the +content as you see fit. Except as noted in the Restrictions section below, you +are free to use the documentation content in your own creations. For example, +you could quote the text in a book, cut-and-paste sections to your blog, record +it as an audiobook for the visually impaired, or even translate it.

    + + +

    Restrictions

    + +
      +
    • While the documentation itself is available to you under the Apache 2.0 +license, note that proprietary trademarks and brand features are not +included in that license.
    • + +
    • Google's trademarks and other brand features (including the +ANDROID stylized typeface logo) are not included in the license. +Please see +Guidelines for Third Party Use of Google Brand Features for +information about this usage.
    • + +
    • In some cases, a page may include content, such as an image, that is not +covered by the license. In that case, we will label the content that is not licensed.
    • + +
    • In addition, content linked from a page on this site is not covered +by the license unless specifically noted. For example, pages may link to videos or +slide decks that are not covered.
    • + +
    • The use of sample source code provided in the SDK or shown in this +documentation is subject to the conditions detailed in the SDK Terms and Conditions.
    • +
    + + +

    Attribution

    +

    +Proper attribution is required when you reuse or create modified +versions of content that appears on a page made available under the +terms of the Creative Commons Attribution license. On this site, the +requirement for attribution applies only to the non-documentation +content, as described earlier in this document. The complete +requirements for attribution can be found in section 4b of the + +Creative Commons legal code. +

    +

    + In practice we ask that you provide attribution to the Android Open + Source project to the best of the ability of the medium in which you + are producing the work. There are several typical ways in which this + might apply: +

    +

    Exact Reproductions

    +

    + If your online work exactly reproduces text or images from this + site, in whole or in part, please include a paragraph at the bottom + of your page that reads: +

    +
    + Portions of this page are reproduced from work created and shared by the Android Open Source Project + and used according to terms described in the Creative Commons + 2.5 Attribution License. + +
    +

    + Also, please link back to the original source page so that readers can + refer there for more information. +

    +

    Modified Versions

    +

    + If your online work shows modified text or images based on + the content from this site, please include a paragraph at the bottom of + your page that reads: +

    +
    + Portions of this page are modifications based on work created and shared by the Android Open + Source Project and used according to terms described in the Creative Commons + 2.5 Attribution License. +
    +

    + Again, please link back to the original source page so that readers can + refer there for more information. This is even more important when + the content has been modified. +

    +

    Other Media

    +

    + If you produce non-hypertext works, such as books, audio, or + video, we ask that you make a best effort to include a spoken or + written attribution in the spirit of the messages above. +

    + +
    + + diff --git a/docs/html/offline.jd b/docs/html/offline.jd new file mode 100644 index 0000000000000..07c20d63d924d --- /dev/null +++ b/docs/html/offline.jd @@ -0,0 +1,28 @@ +home=true +page.title=Welcome +@jd:body + +
    + +

    Welcome to the Android SDK!

    + + + +

    If you've just downloaded the SDK, then continue with the +Installing guide.

    + +

    If you're upgrading from a previously installed version, then refer to the +Upgrading guide.

    + +

    Once you've completed the SDK installation, you can start learning about development on the Android framework +by reading the Dev Guide. And don't forget that the SDK comes packaged with +a wide variety of very helpful code samples.

    + +

    Please note that you are currently viewing a local, offline version of the Android developer documentation. +The offline documentation will behave just like the online documentation, except searches in the above +search bar will not work. However, the search bar is still able to provide auto-completion for package and class names, +which can help you quickly find reference documentation for specific classes that you remember. +If you'd like fully searchable documentation, please visit +http://developer.android.com.

    + +
    \ No newline at end of file diff --git a/docs/html/resources/bootcamp.pdf b/docs/html/resources/bootcamp.pdf deleted file mode 100644 index a817b96384f1e..0000000000000 Binary files a/docs/html/resources/bootcamp.pdf and /dev/null differ diff --git a/docs/html/samples/index.jd b/docs/html/samples/index.jd index 6219f9abd6009..c5c0b7132eb9b 100644 --- a/docs/html/samples/index.jd +++ b/docs/html/samples/index.jd @@ -17,7 +17,7 @@ page.onlyfortemplate=codesite

    To run these samples, you should import them into -Eclipse or use +href="{@docRoot}guide/developing/eclipse-adt.html#creatingaproject">import them into +Eclipse or use activitycreator.py, as described in the Installing the SDK.

    +href="{@docRoot}sdk/1.1_r1/installing.html">Installing the SDK.

    diff --git a/docs/html/sdk/1.0_r1/RELEASENOTES.jd b/docs/html/sdk/1.0_r1/RELEASENOTES.jd deleted file mode 100644 index c6a38bb9179f7..0000000000000 --- a/docs/html/sdk/1.0_r1/RELEASENOTES.jd +++ /dev/null @@ -1,100 +0,0 @@ -page.title=Release Notes, 1.0 r1 -@jd:body - - -

    This SDK release is the first to include the Android 1.0 platform and application API. Applications developed on this SDK will be compatible with mobile devices running the Android 1.0 platform, when such devices are available.

    - -

    This release includes mainly bug fixes, although some smaller features were added. The Android 1.0 also includes several API changes from the 0.9 version. For more information on API changes, see the Overview of Changes and the API Differences Report. For those porting from the M5 release, the SDK also includes the legacy changes overview and API Differences Reports. See the current Overview of Changes for more information.

    - -

    ADT Plugin Compatibility

    - -

    For this version of the SDK — Android 1.0 SDK, Release 1 — the compatible version of the Android Development Tools (ADT) Plugin for Eclipse is 0.8.0. If you are using a previous version of ADT, you should update to the latest version for use with this SDK. For information about how to update your ADT plugin, see Upgrading the SDK.

    - -

    Installation and Upgrade Notes

    - -

    If you've been developing an application using a previous SDK version and you want the application to run on Android-powered mobile devices, you must port the application to the Android 1.0 SDK. Please see Upgrading the SDK for detailed instructions on how to make the transition to this release. Be sure to wipe application user data (emulator option -wipe-data) when running your application on the Android 1.0 SDK emulator.

    - -

    If you're installing the Android SDK for the first time, please see the instructions in Installing the SDK. - -

    Other Notes

    - -

    MapView API Key

    - -

    MapView is a class that lets you easily integrate Google Maps into your application. Before you can access the maps data, you will need to register with the Google Maps service and receive a Maps API Key, which you then add to your MapView for authentication to the server.

    - -

    Currently, the registration service for MapView is not yet active and Google Maps is not yet enforcing the Maps API Key requirement. However, note that the registration service will be activated soon, so that MapViews in any application deployed to a mobile device will require registration and a valid Maps API Key.

    - -

    As soon as the registration service becomes available, we will update the page at http://code.google.com/android/toolbox/apis/mapkey.html with details about how and where to register. Please check that page periodically for registration information, if you are using a MapView.

    - - -

    Resolved Issues, Changes

    - -

    Emulator

    -
      -
    • Emulator now saves the user image in <android>/SDK1.0/
    • -
    • Fixed EsounD-related freezes on Linux.
    • -
    • Fixed the documentation in -help-audio. '-audio list' doesn't work, one - needs to call -help-audio-out and -help-audio-in to get the list of valid - audio backends.
    • -
    • Fixed scrollwheel Dpad emulation in rotated mode. before that, using the - scroll-wheel would always generated Dpad Up/Down events, even when in - landscape mode.
    • -
    • Several Obsolete command options were removed.
    • -
    • Setting the network speed through the console or the -netspeed option will - properly modify the connectivity icon on the device.
    • -
    • Setting the GSM voice registration state to 'roaming' in the console will - properly modify the voice icon on the device
    • -
    - -

    SQLite

    -
      -
    • SQLite is now included in the SDK package on all platforms.
    • -
    - -

    Known Issues

    - -

    JUnit and Eclipse/ADT

    -
      -
    • If you are developing in Eclipse/ADT and want to add JUnit test -classes, you can do so. However, you need to set up a custom JUnit configuration -before your tests will run properly. For detailed information about how to set -up the JUnit configuration, see the troubleshooting topic Running a Junit test class -in Eclipse.
    • -
    - -

    Other

    - -
      -
    • It is not possible to send MMS messages between emulator instances.
    • -
    • In some cases, you may encounter problems when using the browser on an -emulator started with the command-line option -http-proxy.
    • -
    • We regret to inform developers that Android 1.0 will not include support for -dot-matrix printers.
    • -
    • On the OSX platform, if you manually remove the ~/.android directory -using rm -rf ~/.android, then try to run -the emulator, it crashes. This happens because the emulator fails to create -a new .android directory before attempting to create the child SDK1.0 directory. -To work around this issue, manually create a new .android directory using -mkdir ~/.android, then run the emulator. The emulator -creates the SDK1.0 directory and starts normally.
    • -
    • The final set of Intent patterns honored by Android 1.0 has not yet been -fully documented. Documentation will be provided in future releases.
    • -
    • In ADT Editor, you can add at most ten new resource values at a time, -in a given res/values/*.xml, using the form in the Android Resources pane. -If you add more than ten, the Android Resources pane will not display the -attributes fields for the additional resource entries. To work around this -problem, you can close the file in the editor and open it again, or you -can edit the resource entries in the XML text mode.
    • -
    • The emulator's battery-control commands (power <option>) -are not working in this release.
    • -
    • We regret to inform developers that Android 1.0 will not support 3.5" floppy disks. -
    • -
    • Unfortunately, the ability to play audio streams from memory (such as via an InputStream or Reader) will not be possible in Android 1.0.  As a workaround, we recommend that developers save media content to SD card and use MediaPlayer to play from a file URI, or embed a small HTTP server and play from a URI on localhost (such as http://127.0.0.1:4242/something). -
    • -
    • Android now supports modules or libraries that can be optionally linked into applications; a good example is the MapView, which has been moved into such a library. However, Android 1.0 will not support the ability for third-party developers to create such libraries for sharing with other applications. -
    • -
    • We believe that we have eliminated the problem with very long emulator startups on Windows, but had some trouble reproducing the issue.  We are interested in feedback from developers, if this issue persists. -
    • -
    - diff --git a/docs/html/sdk/1.0_r1/index.jd b/docs/html/sdk/1.0_r1/index.jd index 7ad4e882bc638..d236844b70c05 100644 --- a/docs/html/sdk/1.0_r1/index.jd +++ b/docs/html/sdk/1.0_r1/index.jd @@ -1,6 +1,8 @@ -page.title=Download 1.0r1 +page.title=Android 1.0 SDK, release 1 -sdk.version=Android 1.0 SDK, release 1 +sdk.not_latest_version=true + +sdk.version=1.0_r1 sdk.date=September 23, 2008 sdk.win_download=android-sdk-windows-1.0_r1.zip @@ -15,4 +17,48 @@ sdk.linux_download=android-sdk-linux_x86-1.0_r1.zip sdk.linux_bytes=87.8 MB sdk.linux_checksum=2660b4029039b7d714e59827e9a9a11d + @jd:body + +

    Included in this SDK

    + +

    This SDK includes some awesome stuff.

    + + +

    System and Software Requirements

    + +

    The following systems and development environments are supported by this SDK.

    + +

    Supported Operating Systems:

    +
      +
    • Windows XP or Vista
    • +
    • Mac OS X 10.4.8 or later (x86 only)
    • +
    • Linux (tested on Linux Ubuntu Dapper Drake)
    • +
    + +

    Supported Development Environments:

    +
      +
    • Eclipse IDE + +
    • +
    • Other development environments or IDEs +
        +
      • JDK 5 or JDK 6 (JRE alone is not sufficient)
      • +
      • Apache Ant 1.6.5 or later for Linux and Mac, 1.7 or later for Windows
      • +
      • Not compatible with Gnu Compiler for Java (gcj)
      • +
      +
    • +
    + +

    Note: If JDK is already installed on your development computer, please take a moment to make sure that it meets the version requirements listed above. In +particular, note that some Linux distributions may include JDK 1.4 or Gnu Compiler for Java, both of which are not supported for Android development.

    \ No newline at end of file diff --git a/docs/html/sdk/1.0_r1/terms.jd b/docs/html/sdk/1.0_r1/terms.jd deleted file mode 100644 index 5c6e6fcb9d5c2..0000000000000 --- a/docs/html/sdk/1.0_r1/terms.jd +++ /dev/null @@ -1,7 +0,0 @@ -page.title=Terms and Conditions -@jd:body - - - - - diff --git a/docs/html/sdk/1.0_r2/index.jd b/docs/html/sdk/1.0_r2/index.jd new file mode 100644 index 0000000000000..6fbca6dc8f8a0 --- /dev/null +++ b/docs/html/sdk/1.0_r2/index.jd @@ -0,0 +1,64 @@ +page.title=Android 1.0 SDK, release 2 + +sdk.not_latest_version=true + +sdk.version=1.0_r2 +sdk.date=November 2008 + +sdk.win_download=android-sdk-windows-1.0_r2.zip +sdk.win_bytes=98360564 +sdk.win_checksum=a5e1af8ac145946b4a9627516ad4a711 + +sdk.mac_download=android-sdk-mac_x86-1.0_r2.zip +sdk.mac_bytes=93771410 +sdk.mac_checksum=87b99d5e9f59b78363a63200c11498e8 + +sdk.linux_download=android-sdk-linux_x86-1.0_r2.zip +sdk.linux_bytes=94186463 +sdk.linux_checksum=a1f3b6d854596f850f5008856d0f380e + + +@jd:body + +

    Included in this SDK

    + +

    This SDK includes some awesome stuff.

    + + +

    System and Software Requirements

    + +

    The following systems and development environments are supported by this SDK.

    + +

    Supported Operating Systems:

    +
      +
    • Windows XP or Vista
    • +
    • Mac OS X 10.4.8 or later (x86 only)
    • +
    • Linux (tested on Linux Ubuntu Dapper Drake)
    • +
    + +

    Supported Development Environments:

    +
      +
    • Eclipse IDE + +
    • +
    • Other development environments or IDEs +
        +
      • JDK 5 or JDK 6 (JRE alone is not sufficient)
      • +
      • Apache Ant 1.6.5 or later for Linux and Mac, 1.7 or later for Windows
      • +
      • Not compatible with Gnu Compiler for Java (gcj)
      • +
      +
    • +
    + +

    Note: If JDK is already installed on your development computer, please take a moment to make sure that it meets the version requirements listed above. In +particular, note that some Linux distributions may include JDK 1.4 or Gnu Compiler for Java, both of which are not supported for Android development.

    \ No newline at end of file diff --git a/docs/html/sdk/1.0_r2/installing.jd b/docs/html/sdk/1.0_r2/installing.jd new file mode 100644 index 0000000000000..0f15396025880 --- /dev/null +++ b/docs/html/sdk/1.0_r2/installing.jd @@ -0,0 +1,124 @@ +page.title=Installing the SDK +@jd:body + + +

    This page describes how to install the Android SDK and set up your development environment. If you haven't +downloaded the SDK yet, follow the link below.

    + + + +

    Before you begin, be sure that you're development environment meets the SDK +System and Software Requirements.

    + +
    +

    Upgrading?

    +

    If you have already developed applications using an earlier version of the +SDK, please skip this page and read the +Upgrading the SDK document. +

    +
    + + + + +

    Installing the SDK

    + +

    After downloading the SDK, unpack the .zip archive to a suitable location on your machine. By default, the SDK files are unpacked into a directory named android_sdk_<platform>_<release>_<build>. The directory contains the subdirectories tools/, samples/, and others.

    + +

    Make a note of the name and location of the unpacked SDK directory on your system — you will need to refer to the SDK directory later, when setting up the Android plugin or using SDK tools.

    + +

    Optionally, you can add the path to the SDK tools directory to your path. As mentioned above, the tools/ directory is located in the SDK directory.

    +
      +
    • On Linux, edit your ~/.bash_profile or ~/.bashrc file. Look + for a line that sets the PATH environment variable and add the + full path to the tools/ directory to it. If you don't + see a line setting the path, you can add one:
    • + +
        export PATH=${PATH}:<your_sdk_dir>/tools
      + +
    • On a Mac, look in your home directory for .bash_profile and + proceed as for Linux. You can create the .bash_profile, if + you haven't already set one up on your machine.
    • + +
    • On Windows, right click on My Computer, and select Properties. + Under the Advanced tab, hit the Environment Variables button, and in the + dialog that comes up, double-click on Path under System Variables. Add the full path to the tools/ directory to the path.
    • +
    + +

    Adding tools to your path lets you run Android Debug Bridge (adb) and the other command line tools without needing to supply the full path to the tools directory. Note that, if you update your SDK, you should remember to update your PATH settings to point to the new location, if different.

    + +

    Setting up Eclipse

    +

    If you'll be developing with the Eclipse IDE, follow the following procedure to setup the IDE +to use the Android SDK.

    +

    Basically, you just need to update your Eclipse preferences to point to the Android SDK directory:

    +
      +
    1. Select Window > Preferences... to open the Preferences + panel. (Mac OS X: Eclipse > Preferences)
    2. +
    3. Select Android from the left panel.
    4. +
    5. For the SDK Location in the main panel, click Browse... and locate the SDK directory.
    6. +
    7. Click Apply, then OK.
    8. +
    +

    Done! We now recommend that you install the ADT Eclipse plugin, which will provide some much-appreciated assistance in developing Android apps with Eclipse...

    + + +

    Installing the Eclipse Plugin (ADT)

    + +

    If you will be using the Eclipse IDE as your environment for developing Android applications, you can install a custom plugin called Android Development Tools (ADT), which adds integrated support for Android projects and tools. The ADT plugin includes a variety of powerful extensions that make creating, running, and debugging Android applications faster and easier. This plugin is highly recommended for Eclipse users.

    + +

    If you will not be using the Eclipse IDE, you do not need to download or install the ADT plugin.

    + +

    Follow this guide to install the ADT Plugin

    + + +

    Installation Notes

    +

    Ubuntu Linux Notes

    +
      +
    • If you need help installing and configuring Java on your +development machine, you might find these resources helpful: + +
    • +
    • Here are the steps to install Java and Eclipse, prior to installing +the Android SDK and ADT Plugin. +
        +
      1. If you are running a 64-bit distribution on your development +machine, you need to install the ia32-libs package using +apt-get:: +
        apt-get install ia32-libs
      2. +
      3. Next, install Java: +
        apt-get install sun-java6-bin
      4. +
      5. The Ubuntu package manager does not currently offer an Eclipse 3.3 + version for download, so we recommend that you download Eclipse from +eclipse.org (http://www.eclipse.org/ +downloads/). A Java or RCP version of Eclipse is recommended.
      6. +
      7. Follow the steps given in previous sections to install the SDK +and the ADT plugin.
      8. +
      +
    +

    Other Linux Notes

    +
      +
    • If JDK is already installed on your development computer, please +take a moment to make sure that it meets the version requirements listed +at the top of this page. In particular, note that some Linux +distributions may include JDK 1.4 or Gnu Compiler for Java, both of +which are not supported for Android development.
    • +
    + + + + + + + + + + + + + + + + diff --git a/docs/html/sdk/1.0_r2/upgrading.jd b/docs/html/sdk/1.0_r2/upgrading.jd new file mode 100644 index 0000000000000..168f1be288a50 --- /dev/null +++ b/docs/html/sdk/1.0_r2/upgrading.jd @@ -0,0 +1,151 @@ +page.title=Upgrading the SDK +@jd:body + + + + +

    This guide will help you migrate your development environment and applications +to the latest version of the SDK. Use this guide if you've been developing applications +on a previous version of the Android SDK. +

    + +

    To ensure that your applications are compliant with the Android 1.0 system available +on mobile devices, you need to install the new SDK and port your existing Android +applications to the updated API. The sections below guide you through the process.

    + +

    Install the new SDK

    + +

    Download the SDK and unpack it into a safe location.

    + +

    After unpacking the new SDK, you should:

    + +
      +
    • Wipe your emulator data.

      Some data formats have changed since the last + SDK release, so any previously saved data in your emulator must be removed. Open a console/terminal + and navigate to the /tools directory of your SDK. Launch the + emulator with the -wipe-data option.

      +

      Windows: emulator -wipe-data
      + Mac/Linux: ./emulator -wipe-data

      +
    • +
    • Update your PATH variable (Mac/Linux; optional).

      If you had previously setup your + PATH variable to point to the SDK tools directory, then you'll need to update it to + point to the new SDK. E.g., for a .bashrc or .bash_profile file: + export PATH=$PATH:<your_new_sdk_dir>/tools

      +
    • +
    + +

    Update your ADT Eclipse Plugin

    + +

    If you develop on Eclipse and are using the ADT plugin, follow these steps to install the new plugin that accompanies the latest SDK.

    + + + + + + + +
    Eclipse 3.3 (Europa)Eclipse 3.4 (Ganymede)
    +
      +
    1. Select Help > Software Updates > Find and Install....
    2. +
    3. Select Search for updates of the currently installed features and click Finish.
    4. +
    5. If any update for ADT is available, select and install.
    6. +
    7. Restart Eclipse.
    8. +
    +
    +
      +
    1. Select Help > Software Updates...
    2. +
    3. Select the Installed Software tab.
    4. +
    5. Click Update...
    6. +
    7. If an update for ADT is available, select it and click Finish.
    8. +
    9. Restart Eclipse.
    10. +
    +
    + +

    After restart, update your Eclipse preferences to point to the SDK directory:

    +
      +
    1. Select Window > Preferences... to open the Preferences panel. (Mac OSX: Eclipse > Preferences)
    2. +
    3. Select Android from the left panel.
    4. +
    5. For the SDK Location in the main panel, click Browse... and locate the SDK directory.
    6. +
    7. Click Apply, then OK.
    8. +
    + +

    Set Up Application Signing

    + +

    All applications must now be signed before you can install them on the emulator. Both +the ADT plugin and the Ant-based build tools support this requirement by signing compiled +.apk files with a debug key. To do so, the build tools use the Keytool utility included +in the JDK to to create a keystore and a key with a known alias and password. For more +information, see Signing Your Applications. + +

    To support signing, you should first make sure that Keytool is available to the SDK build +tools. In most cases, you can tell the SDK build tools how to find Keytool by making sure that +your JAVA_HOME environment variable is set and that it references a suitable JDK. Alternatively, +you can add the JDK version of Keytool to your PATH variable.

    + +

    If you are developing on a version of Linux that originally came with Gnu Compiler for Java, +make sure that the system is using the JDK version of Keytool, rather than the gcj version. +If keytool is already in your PATH, it might be pointing to a symlink at /usr/bin/keytool. +In this case, check the symlink target to make sure that it points to the keytool in the JDK.

    + +

    If you use Ant to build your .apk files (rather than ADT for Eclipse), you must regenerate +your build.xml file. To do that, follow these steps:

    +
      +
    1. In your Android application project directory, locate and delete the current build.xml file.
    2. +
    3. Run activitycreator, directing output to the folder containing your application project. + +
      - exec activitycreator --out <project folder> your.activity.YourActivity
      + +
    4. +
    + +

    Run in this way, activitycreator will not erase or create new Java files (or manifest files), +provided the activity and package already exists. It is important that the package and the activity +are real. The tool creates a new build.xml file, as well as a new directory called "libs" in which +to place 3rd jar files, which are now automatically handled by the Ant script.

    + +

    Migrate your applications

    + +

    After updating your SDK, you will likely encounter breakages in your code, due to +framework and API changes. You'll need to update your code to match changes in the Android APIs.

    + +

    One way to start is to open your project in Eclipse and see where the ADT +identifies errors in your application. From there, you can lookup +respective changes in the +Overview of Changes +and API Diffs Report.

    + +

    If you have additional trouble updating your code, visit the +Android Developers Group +to seek help from other Android developers.

    + +

    If you have modified one of the ApiDemos applications and would like to migrate it +to the new SDK, note that you will need to uninstall the version of ApiDemos that comes +preinstalled in the emulator. For more information, or if you encounter an "reinstallation" +error when running or installing ApiDemos, see the troubleshooting topic +I can't install ApiDemos +apps in my IDE because of a signing error for information about how to solve the problem.

    + diff --git a/docs/html/sdk/1.1_r1/index.jd b/docs/html/sdk/1.1_r1/index.jd new file mode 100644 index 0000000000000..b516acbe3880e --- /dev/null +++ b/docs/html/sdk/1.1_r1/index.jd @@ -0,0 +1,62 @@ +page.title=Download Android 1.1 SDK, Release 1 + +sdk.version=1.1_r1 +sdk.date=February 2009 + +sdk.win_download=android-sdk-windows-1.1_r1.zip +sdk.win_bytes=unknown +sdk.win_checksum=unknown + +sdk.mac_download=android-sdk-mac_x86-1.1_r1.zip +sdk.mac_bytes=unknown +sdk.mac_checksum=unknown + +sdk.linux_download=android-sdk-linux_x86-1.1_r1.zip +sdk.linux_bytes=unknown +sdk.linux_checksum=unknown + + +@jd:body + +

    SDK Contents

    + +

    Development tools

    + +

    The SDK includes a variety of tools for developing and debugging application code and designing an application UI. You can read about the tools in the +Dev Guide and access them in the <sdk>/tools/ directory. + +

    The tools package included in this SDK is the same as that included in the Android 1.0, Release 2 SDK.

    + +

    System Images

    + +

    The Android system images listed below are included in this SDK. For more information about a system image — features, applications included, localizations, API changes, and so on — see its Version Notes.

    + + + + + + + + + + + + + + +
    System ImageAPI LevelNotesDescription
    Android 1.12Version NotesIncludes com.google.android.maps external library and a set of standard development applications.
    + +

    Sample Code and Applications

    + +

    You can look at a variety of tutorials and samples in the Dev Guide and access the sample code itself +in the <sdk>/samples/ directory of the SDK package.

    + +

    Documentation

    + +

    The SDK package includes a full set of local documentation. To view it, open the <sdk>/documentation.html file in a web browser. If you are developing in an IDE such as Eclipse, you can also view the reference documentation directly in the IDE.

    + +

    The most current documentation is always available on the Android Developers site:

    + +

    http://developer.android.com/

    + + diff --git a/docs/html/sdk/1.1_r1/installing.jd b/docs/html/sdk/1.1_r1/installing.jd new file mode 100644 index 0000000000000..1319237c678f8 --- /dev/null +++ b/docs/html/sdk/1.1_r1/installing.jd @@ -0,0 +1,216 @@ +page.title=Installing the Android SDK +sdk.version=1.1_r1 + +@jd:body + + +

    This page describes how to install the Android 1.1 SDK, Release 1, and set up your development environment. +If you haven't downloaded the SDK yet, you can so so from the Download page.

    + +

    Before you begin, be sure that your development environment meets the SDK +System Requirements.

    + +

    Upgrading?

    +

    If you have already developed applications using an earlier version of the +SDK, please skip this page and read the +Upgrading the SDK document instead. +

    + + + +

    Installing the SDK

    + +

    After downloading the SDK, unpack the .zip archive to a suitable location on your machine. By default, the SDK files are unpacked into a directory named android_sdk_<platform>_<release>_<build>. The directory contains the subdirectories tools/, samples/, and others.

    + +

    Make a note of the name and location of the unpacked SDK directory on your system — you will need to refer to the SDK directory later, when setting up the Android plugin or using SDK tools.

    + +

    Optionally, you can add the path to the SDK tools directory to your path. As mentioned above, the tools/ directory is located in the SDK directory.

    +
      +
    • On Linux, edit your ~/.bash_profile or ~/.bashrc file. Look + for a line that sets the PATH environment variable and add the + full path to the tools/ directory to it. If you don't + see a line setting the path, you can add one:
    • + +
        export PATH=${PATH}:<your_sdk_dir>/tools
      + +
    • On a Mac, look in your home directory for .bash_profile and + proceed as for Linux. You can create the .bash_profile, if + you haven't already set one up on your machine.
    • + +
    • On Windows, right click on My Computer, and select Properties. + Under the Advanced tab, hit the Environment Variables button, and in the + dialog that comes up, double-click on Path under System Variables. Add the full path to the tools/ directory to the path.
    • +
    + +

    Adding tools to your path lets you run Android Debug Bridge (adb) and the other command line tools without needing to supply the full path to the tools directory. Note that, if you update your SDK, you should remember to update your PATH settings to point to the new location, if different.

    + +

    Setting up Eclipse

    +

    If you'll be developing with the Eclipse IDE, follow the following procedure to setup the IDE +to use the Android SDK.

    +

    Basically, you just need to update your Eclipse preferences to point to the Android SDK directory:

    +
      +
    1. Select Window > Preferences... to open the Preferences + panel. (Mac OS X: Eclipse > Preferences)
    2. +
    3. Select Android from the left panel.
    4. +
    5. For the SDK Location in the main panel, click Browse... and locate the SDK directory.
    6. +
    7. Click Apply, then OK.
    8. +
    +

    Done! We now recommend that you install the ADT Eclipse plugin, which will provide some much-appreciated assistance in developing Android apps with Eclipse...

    + + +

    Installing the Eclipse Plugin (ADT)

    + +

    If you will be using the Eclipse IDE as your environment for developing Android applications, you can install a custom plugin called Android Development Tools (ADT), which adds integrated support for Android projects and tools. The ADT plugin includes a variety of powerful extensions that make creating, running, and debugging Android applications faster and easier. Developing in ADT/Eclipse is highly recommended for Eclipse users and those new to Android.

    + +

    If you will not be using the Eclipse IDE, you do not need to download or install the ADT plugin. You can still develop Android applications using other tools.

    + +

    To download and install the ADT plugin, follow the steps below for your respective Eclipse version.

    + + + + + + + +
    Eclipse 3.3 (Europa)Eclipse 3.4 (Ganymede)
    +
      +
    1. Start Eclipse, then select Help > Software Updates > Find + and Install....
    2. + +
    3. In the dialog that appears, select Search for new features to install and click Next.
    4. +
    5. Click New Remote Site.
    6. +
    7. In the resulting dialog box, enter a name for the remote site (e.g. Android Plugin) and enter this as its URL: +
      https://dl-ssl.google.com/android/eclipse/
      +

      Alternatively, you can use http in the Location URL, if you are having + trouble with https (https is preferred for security reasons).

      +
      http://dl-ssl.google.com/android/eclipse/
      +

      Click OK.

    8. +
    9. You should now see the new site added to the search list (and checked). + Click Finish.
    10. +
    11. In the subsequent Search Results dialog box, select the checkbox for + Android Plugin > Developer Tools. + This will check both features: "Android Developer Tools", and "Android + Editors". The Android Editors feature is optional, but recommended. If + you choose to install it, you need the WST plugin mentioned earlier in this + page. Click Next.
    12. +
    13. Read the license agreement and then select Accept terms of the license agreement. + Click Next.
    14. +
    15. Click Finish.
    16. + +
    17. The ADT plugin is not signed; you can accept the installation anyway + by clicking Install All.
    18. +
    19. Restart Eclipse.
    20. +
    + +
    + +
      +
    1. Start Eclipse, then select Help > Software Updates.... +
    2. +
    3. In the dialog that appears, click the Available Software tab. +
    4. +
    5. Click Add Site... +
    6. +
    7. Enter this as the Location: +
      https://dl-ssl.google.com/android/eclipse/
      +

      Alternatively, you can use http in the Location URL, if you are having + trouble with https (https is preferred for security reasons).

      +
      http://dl-ssl.google.com/android/eclipse/
      +

      Click OK.

    8. +
    9. Back in the Available Software view, you should see the plugin. Select the checkbox next to + Developer Tools and click Install... +
    10. +
    11. On the subsequent Install window, "Android Developer Tools", and "Android Editors" should both be checked. + The Android Editors feature is optional, but recommended. If + you choose to install it, you need the WST plugin mentioned earlier in this + page. Click Next. +
    12. +
    13. Accept the license agreement and click Finish.
    14. +
    15. Restart Eclipse.
    16. +
    + +
    + +

    Troubleshooting ADT Installation

    +

    +If you are having trouble downloading the ADT plugin after following the steps above, here are some suggestions:

    + +
      +
    • If Eclipse can not find the remote update site containing the ADT plugin, try changing the remote site URL to use http, rather than https. That is, set the Location for the remote site to: +
      http://dl-ssl.google.com/android/eclipse/
    • +
    • If you are behind a firewall (such as a corporate firewall), make + sure that you have properly configured your proxy settings in Eclipse. + In Eclipse 3.3/3.4, you can configure proxy information from the main + Eclipse menu in Window (on Mac, Eclipse) > Preferences > General > Network Connections.
    • +
    +

    +If you are still unable to use Eclipse to download the ADT plugin as a remote update site, you can download the ADT files to your local machine using a browser and the install the files in Eclipse from there: +

    +
      +
    1. Download the ADT zip file (do not unpack it). +
    2. Follow steps 1 and 2 in the default install instructions (above). +
    3. In Eclipse 3.3, click New Archive Site....
      + In Eclipse 3.4, click Add Site..., then Archive... +
    4. Browse and select the downloaded the zip file. +
    5. Follow the remaining procedures, above, starting from steps 5. +
    +

    +Note that to update your plugin, you will have to follow these steps again instead of the default update instructions.

    + +

    Note that the "Android Editors" feature of ADT requires several optional +Eclipse components (for example, WST). If you encounter an error when +installing ADT, your Eclipse installion might not include those components. +For information about how to quickly add the necessary components to your +Eclipse installation, see the troubleshooting topic +ADT Installation Error: "requires plug-in org.eclipse.wst.sse.ui".

    + +

    For Linux users

    +

    If you encounter this error when installing the ADT Plugin for Eclipse: +

    +An error occurred during provisioning.
    +Cannot connect to keystore.
    +JKS
    +

    +...then your development machine lacks a suitable Java VM. Installing Sun +Java 6 will resolve this issue and you can then reinstall the ADT +Plugin.

    + + +

    Installation Notes

    +

    Ubuntu Linux Notes

    +
      +
    • If you need help installing and configuring Java on your +development machine, you might find these resources helpful: + +
    • +
    • Here are the steps to install Java and Eclipse, prior to installing +the Android SDK and ADT Plugin. +
        +
      1. If you are running a 64-bit distribution on your development +machine, you need to install the ia32-libs package using +apt-get:: +
        apt-get install ia32-libs
      2. +
      3. Next, install Java: +
        apt-get install sun-java6-bin
      4. +
      5. The Ubuntu package manager does not currently offer an Eclipse 3.3 + version for download, so we recommend that you download Eclipse from +eclipse.org (http://www.eclipse.org/ +downloads/). A Java or RCP version of Eclipse is recommended.
      6. +
      7. Follow the steps given in previous sections to install the SDK +and the ADT plugin.
      8. +
      +
    +

    Other Linux Notes

    +
      +
    • If JDK is already installed on your development computer, please +take a moment to make sure that it meets the version requirements listed +at the top of this page. In particular, note that some Linux +distributions may include JDK 1.4 or Gnu Compiler for Java, both of +which are not supported for Android development.
    • +
    + + diff --git a/docs/html/sdk/1.0_r1/requirements.jd b/docs/html/sdk/1.1_r1/requirements.jd similarity index 80% rename from docs/html/sdk/1.0_r1/requirements.jd rename to docs/html/sdk/1.1_r1/requirements.jd index 6af3900e68114..95b658bea2037 100644 --- a/docs/html/sdk/1.0_r1/requirements.jd +++ b/docs/html/sdk/1.1_r1/requirements.jd @@ -1,18 +1,20 @@ -page.title=System and Software Requirements +page.title=System Requirements + +sdk.version=1.1_r1 +sdk.date=February 2009 + @jd:body +

    The sections below describe the system and software requirements for developing Android applications using the Android SDK tools included in Android 1.1 SDK, Release 1.

    -

    To develop Android applications using the code and tools in the Android SDK, -you need a suitable development computer and development environment, as described below.

    - -

    Supported Operating Systems:

    +

    Supported Operating Systems

      -
    • Windows XP or Vista
    • +
    • Windows XP (32-bit) or Vista (32- or 64-bit)
    • Mac OS X 10.4.8 or later (x86 only)
    • Linux (tested on Linux Ubuntu Dapper Drake)
    -

    Supported Development Environments:

    +

    Supported Development Environments

    • Eclipse IDE
        diff --git a/docs/html/sdk/1.1_r1/upgrading.jd b/docs/html/sdk/1.1_r1/upgrading.jd new file mode 100644 index 0000000000000..aa7c3a5d3fcc8 --- /dev/null +++ b/docs/html/sdk/1.1_r1/upgrading.jd @@ -0,0 +1,185 @@ +page.title=Upgrading the SDK +sdk.version=1.1_r1 +@jd:body + + + +

        This document describes how to move your devlopment environment and existing +Android applications from an Android 1.0 SDK to the Android 1.1, Release 1 SDK. +If you are migrating applications from an earlier SDK, please read the upgrading +document available in the Android 1.0 SDK package. +

        + +

        To ensure that your applications are compliant with the Android 1.1 system available +on mobile devices, you need to install the Android 1.1 SDK and port your existing Android +applications to it. The sections below guide you through the process.

        + +

        Installing the Latest SDK

        + +

        Download the SDK and unpack it into a safe location.

        + +

        After unpacking the new SDK, you should:

        + +
          +
        • Wipe your emulator data.

          Some data formats have changed since the last + SDK release, so any previously saved data in your emulator must be removed. Open a console/terminal + and navigate to the /tools directory of your SDK. Launch the + emulator with the -wipe-data option. +

          Windows: emulator -wipe-data
          + Mac/Linux: ./emulator -wipe-data

          +
        • +
        • Update your PATH variable (Mac/Linux; optional).

          If you had previously setup your + PATH variable to point to the SDK tools directory, then you'll need to update it to + point to the new SDK. For example, for a .bashrc or .bash_profile file: + export PATH=$PATH:<your_new_sdk_dir>/tools

          +
        • +
        • If (and only if) you are developing using Ant, you will also need to modify + your build.xml properties to point to the new SDK. +

          Open the default.properties file associated with your build.xml + file (typically located in the same directory). In the default.properties + file, update the sdk-folder property with the full path to + the new SDK directory.

        • +
        + + +

        Update your ADT Eclipse Plugin

        + +

        If you develop on Eclipse and are migrating from an Android 1.0 +SDK, no update of the ADT plugin is needed.

        + +

        If you are migrating from an earlier version of the SDK, you will +need to update the ADT plugin.

        You may also want to upgrade your +ADT plugin when a new version becomes available for your existing version +of the SDK.

        + +

        The steps below describe how to update the ADT plugin to the latest +version available.

        + + + + + + + +
        Eclipse 3.3 (Europa)Eclipse 3.4 (Ganymede)
        +
          +
        1. Select Help > Software Updates > Find and Install....
        2. +
        3. Select Search for updates of the currently installed features and click Finish.
        4. +
        5. If any update for ADT is available, select and install.
        6. +
        7. Restart Eclipse.
        8. +
        +

        Alternatively,

        +
          +
        1. Select Help > Software Updates > Manage Configuration.
        2. + +
        3. Navigate down the tree and select Android Development Tools <version>
        4. +
        5. Select Scan for Updates under Available Tasks.
        6. +
        +
        +
          +
        1. Select Help > Software Updates...
        2. +
        3. Select the Installed Software tab.
        4. +
        5. Click Update...
        6. +
        7. If an update for ADT is available, select it and click Finish.
        8. +
        9. Restart Eclipse.
        10. +
        +
        + +

        After restart, update your Eclipse preferences to point to the SDK directory:

        +
          +
        1. Select Window > Preferences... to open the Preferences panel. (Mac OSX: Eclipse > Preferences)
        2. +
        3. Select Android from the left panel.
        4. +
        5. For the SDK Location in the main panel, click Browse... and locate the SDK directory.
        6. +
        7. Click Apply, then OK.
        8. +
        + + + + +

        Migrate Your Applications, if Necessary

        + +

        If (and only if) you have written apps in an SDK released previous to +the Android 1.0 SDK, you will need to migrate your applications. After +installing the new SDK and updating the ADT Plugin (if applicable), you +may encounter breakages in your application code, due to +framework and API changes. You'll need to update your code to match the +latest APIs.

        + +

        One way to start is to open your project in Eclipse and see where the ADT +identifies errors in your application. From there, you can lookup +specific API changes in the Android 1.0 APIs in the + +Overview of Changes and +API Diffs Report.

        + +

        If you have additional trouble updating your code, visit the +Android Developers Group +to seek help from other Android developers.

        + +

        If you have modified one of the ApiDemos applications and would like to migrate it +to the new SDK, note that you will need to uninstall the version of ApiDemos that comes +preinstalled in the emulator. For more information, or if you encounter an "reinstallation" +error when running or installing ApiDemos, see the troubleshooting topic +I can't install ApiDemos +apps in my IDE because of a signing error for information about how to solve the problem.

        + diff --git a/docs/html/sdk/RELEASENOTES.jd b/docs/html/sdk/RELEASENOTES.jd new file mode 100644 index 0000000000000..6e0716c18bb99 --- /dev/null +++ b/docs/html/sdk/RELEASENOTES.jd @@ -0,0 +1,138 @@ +page.title=SDK Release Notes +sdk.version=1.1_r1 +@jd:body + +

        This document provides version-specific information about Android SDK releases. For the latest known issues, please ensure that you're viewing this page at: http://developer.android.com/sdk/RELEASENOTES.html.

        + +

        Android 1.1 SDK, Release 1

        + +

        This SDK provides the development tools and Android system image you need to create applications for Android-powered devices. Applications developed on this SDK will be compatible with mobile devices running the Android 1.1 platform.

        + +

        This release provides an updated system image (Android 1.1), updated documentation, and the same set of development tools provided in the Android 1.0 r2 SDK. The updated system image includes bug fixes and some smaller features, as well as a few minor API changes from the 1.0 version.

        + +

        For details about the Android 1.1 system image included in the SDK — including bug fixes, features, and API changes — please read the Android 1.1 Version Notes.

        + +

        App Versioning for Android 1.1

        + +

        If you are using this SDK to build an application that is compatible only with Android-powered devices running the Android 1.1 platform, please note that you must set the the android:minSdkVersion attribute in the application's manifest to the API Level of Android 1.1 — "2".

        + +

        Specifically, you specify the android:minSdkVersion attribute in a <uses-sdk> element as a child of <manifest> in the manifest file. When set, the attribute looks like this:

        + +
        <manifest>
        +  ...
        +  <uses-sdk minSdkVersion="2">
        +  ...
        +</manifest>
        +
        + +

        By setting android:minSdkVersion in this way, you ensure that users will only be able to install your application if their devices are running the Android 1.1 platform. In turn, this ensures that your application will function properly on their devices, especially if it uses APIs introduced in Android 1.1.

        + +

        If your application uses APIs introduced in Android 1.1 but does not declare <uses-sdk minSdkVersion="2">, then it will run properly on Android 1.1 devices but not on Android 1.0 devices.

        + +

        If your application does not use any new APIs introduced in Android 1.1, you can indicate Android 1.0 compatibility by removing minSdkVersion or setting the attribute to "1". However, before publishing your application, you must make sure to compile your application against the Android 1.0 system image (available in the Android 1.0 SDK), to ensure that it builds and functions properly for Android 1.0 devices. You should test the application against system images corresponding to the API Levels that the application is designed to be compatible with.

        + +

        If you are sure your application is not using Android 1.1 APIs and has no need to use them, you might find it easier to keep working in the Android 1.0 SDK, rather than migrating to the Android 1.1 SDK and having to do additional testing.

        + + +

        ADT Plugin Compatibility

        + +

        For this version of the SDK — Android 1.1 SDK, Release 1 +— the compatible version of the Android Development Tools (ADT) +Plugin for Eclipse is 0.8.0. If you are using a +previous version of ADT, you should update to the latest version for use +with this SDK. For information about how to update your ADT plugin, see +Upgrading +the SDK.

        + +

        Installation and Upgrade Notes

        + +

        If you've been developing an application using an Android 1.0 SDK no +changes to your application are needed. You may want to wipe application +user data (emulator option -wipe-data) when running your +application on the Android 1.1 emulator for the first time.

        + +

        If you're installing the Android SDK for the first time, please see +the instructions in Installing the SDK. + +

        Other Notes

        + +

        MapView API Key

        + +

        com.google.android.maps.MapView is a class that lets you +easily integrate Google Maps into your application. Before you can +access the maps data, you will need to register with the Google Maps +service and receive a Maps API Key, which you then add to your MapView +for authentication to the server.

        + +

        Developers should note that the registration service for MapView is now +active and Google Maps is actively enforcing the Maps API Key requirement. +For information about how to register for a Maps API Key, see + +Obtaining a Maps API Key.

        + +

        USB Drivers for Windows

        + +

        If you using Windows and want to develop or test your application on an +Android-powered device (such as the T-Mobile G1), you need an appropriate USB +driver. For your convenience, the Windows version of the Android SDK includes +these USB drivers that you can install, to let you develop on the device:

        + +
          +
        • USB driver for 32-bit XP and Vista
        • +
        • USB driver for 64-bit Vista only
        • +
        + +

        The USB driver files are located in the +<SDK>/usb_driver directory. For details and +installation instructions, see Setting Up a +Device for Development.

        +

        + +

        Resolved Issues, Changes

        + +

        Emulator

        +
          +
        • Emulator now saves the user image in <android>/SDK1.1/
        • +
        + +

        Known Issues

        + +

        JUnit and Eclipse/ADT

        +
          +
        • If you are developing in Eclipse/ADT and want to add JUnit test +classes, you can do so. However, you need to set up a custom JUnit configuration +before your tests will run properly. For detailed information about how to set +up the JUnit configuration, see the troubleshooting topic Running a Junit test class +in Eclipse.
        • +
        + +

        Other

        + +
          +
        • It is not possible to send MMS messages between emulator instances.
        • +
        • In some cases, you may encounter problems when using the browser on an +emulator started with the command-line option -http-proxy.
        • +
        • We regret to inform developers that Android 1.1 will not include support for +dot-matrix printers.
        • +
        • On the OSX platform, if you manually remove the ~/.android directory +using rm -rf ~/.android, then try to run +the emulator, it crashes. This happens because the emulator fails to create +a new .android directory before attempting to create the child SDK1.0 directory. +To work around this issue, manually create a new .android directory using +mkdir ~/.android, then run the emulator. The emulator +creates the SDK1.0 directory and starts normally.
        • +
        • The final set of Intent patterns honored by Android 1.0 has not yet been +fully documented. Documentation will be provided in future releases.
        • +
        • In ADT Editor, you can add at most ten new resource values at a time, +in a given res/values/*.xml, using the form in the Android Resources pane. +If you add more than ten, the Android Resources pane will not display the +attributes fields for the additional resource entries. To work around this +problem, you can close the file in the editor and open it again, or you +can edit the resource entries in the XML text mode.
        • +
        • The emulator's battery-control commands (power <option>) +are not working in this release.
        • +
        + diff --git a/docs/html/guide/developing/tools/adt_download.jd b/docs/html/sdk/adt_download.jd similarity index 90% rename from docs/html/guide/developing/tools/adt_download.jd rename to docs/html/sdk/adt_download.jd index f03cc4651029d..6e9eec4a2b736 100644 --- a/docs/html/guide/developing/tools/adt_download.jd +++ b/docs/html/sdk/adt_download.jd @@ -29,8 +29,8 @@ page. 0.8.0 ADT-0.8.0.zip 23 September 2008 - Android 1.0 SDK, Release 1 - Required for users of Android 1.0 SDK, Release 1. + Android 1.1 SDK, Release 1
        Android 1.0 SDK, Release 1
        + Required for users of Android 1.1 SDK, Release 1 and Android 1.0 SDK, Release 1
        0.7.1 diff --git a/docs/html/sdk/android-1.1.jd b/docs/html/sdk/android-1.1.jd new file mode 100644 index 0000000000000..8e63ba3df8860 --- /dev/null +++ b/docs/html/sdk/android-1.1.jd @@ -0,0 +1,250 @@ +page.title=Android 1.1 Version Notes +sdk.version=1.1_r1 +sys.date=February 2009 +@jd:body + +

        +Date: February 2009
        +API Level: 2

        + + +

        This document provides version notes for the Android 1.1 system image included in the SDK. + +

        + +

        Overview

        + +

        The Android 1.1 system image delivered in the SDK is the development +counterpart to the Android 1.1 production system image, deployable to +Android-powered handsets starting in February 2009.

        + +

        The Android 1.1 system image delivers an updated version of the framework +API. As with the Android 1.0 API, the Android 1.1 API +is assigned an integer identifier — 2 — that is +stored in the system itself. This identifier, called the "API Level", allows the +system to correctly determine whether an application is compatible with +the system, prior to installing the application.

        + +

        Applications can reference a specific API Level value in their +manifest files, to indicate the minimum version of the Android system +required to run the app. To reference a minimum API Level, applications +can add a minSdkVersion attribute in their manifest files. +The value of the attribute is an integer corresponding to an API Level +identifier. Prior to installing an application, the system then checks the value of +minSdkVersion and allows the install only +if the referenced integer is less than or equal to the API Level integer stored +in the system itself.

        + +

        If you use the Android 1.1 system image to build an application +compatible with Android-powered devices running the Android 1.1 +platform, please note that you must set the the +android:minSdkVersion attribute in the application's +manifest to "2", which is the API strictly associated with Android 1.1. +

        + +

        Specifically, you specify the android:minSdkVersion +attribute in a <uses-sdk> element as a child of +<manifest> in the manifest file. When set, the +attribute looks like this:

        + +
        <manifest>
        +  ...
        +  <uses-sdk minSdkVersion="2">
        +  ...
        +</manifest>
        +
        + +

        By setting android:minSdkVersion in this way, you ensure +that users will only be able to install your application if their +devices are running the Android 1.1 platform. In turn, this ensures that +your application will function properly on their devices, especially if +it uses APIs introduced in Android 1.1.

        + +

        If your application uses APIs introduced in Android 1.1 but does not +declare <uses-sdk minSdkVersion="2">, then it will +run properly on Android 1.1 devices but not on Android 1.0 +devices. In the latter case, the application will crash at runtime when +it tries to use the Android 1.1 APIs.

        + +

        If your application does not use any new APIs introduced in Android +1.1, you can indicate Android 1.0 compatibility by removing +minSdkVersion or setting the attribute to "1". However, +before publishing your application, you must make sure to compile your +application against the Android 1.0 system image (available in the +Android 1.0 SDK), to ensure that it builds and functions properly for +Android 1.0 devices. You should test the application against system +images corresponding to the API Levels that the application is designed +to be compatible with.

        + +

        If you are sure your application is not using Android 1.1 APIs and +has no need to use them, you might find it easier to keep working in the +Android 1.0 SDK, rather than migrating to the Android 1.1 SDK and having +to do additional testing.

        + + +

        External Libraries

        + +

        The system image includes these external libraries, which you can +access from your application by adding a +<uses-library>.

        +
          +
        • com.google.android.maps — gives your +application access to Google Maps data. Note that, to use Google Maps +data, a Maps API Key is required.
        • +
        + +

        Device Compatibility

        + +

        The Android 1.1 system image was tested for compatability with the +Android-powered devices listed below:

        + + +

        Built-in Applications

        + +

        The system image includes these built-in applications:

        +
          +
        • Alarm Clock
        • +
        • API Demos
        • +
        • Browser
        • +
        • Calculator
        • +
        • Camera
        • +
        • Contacts
        • +
        • Dev Tools
        • +
        • Dialer
        • +
        • Email
        • +
        • Maps (and StreetView)
        • +
        • Messaging
        • +
        • Music
        • +
        • Pictures
        • +
        • Settings
        • +
        + +

        UI Localizations

        + +

        The system image provides localized UI strings for the languages +listed below.

        +
          +
        • English, US (en_US)
        • +
        • German (de)
        • +
        + +

        Localized UI strings match the locales that are displayable in +the emulator, accessible through the device Settings application.

        + +

        Resolved Issues

        +
          +
        • AlarmClock alert now plays audio/vibe directly, rather than through +AlarmManager. AlarmClock alert starts playing audio/vibe in its +IntentReceiver, rather than on activity start. These changes should +prevent alarms from being blocked by modal dialogs.
        • +
        • Fixes to device sleep.
        • +
        • Single tap no longer opens the in-call dialpad; users now need to +touch and drag it.
        • +
        • Fixes a bug causing approximately 1 in 25 outbound messages to +freeze up the IMAP connection (to a Gmail based server) when transferred +to the Sent folder.
        • +
        • Removes automatic account setup entries that were broken or not +testable. Adds minor fixes to a few of the remaining entries. Makes +improvements to warning dialogs used for a few special cases.
        • +
        • Changes default mail checking interval to every 15 minutes (instead +of defaulting to "never").
        • +
        • Fixes password-quoting bugs in IMAP, so that users can include +special characters in passwords (e.g. spaces).
        • +
        • Fixes various errors in auto and manual account setup
        • +
        • Improves reporting for various connection errors, making it easier +for the user to diagnose failed account setups.
        • +
        • Fixes new-mail notifications for POP3 accounts.
        • +
        • Ensures proper auto-checking of accounts marked as "never +check".
        • +
        • Now displays date and time using user preference (e.g. 24 hr vs. +AM/PM).
        • +
        • Now shows cc: in message view.
        • +
        • Improves recovery from POP3 connection failures.
        • +
        • POP3 parser rules loosened, so the application can work with +non-compliant email servers.
        • +
        • Removes green CALL button as a shortcut for "add a new call".
        • +
        + +

        New Features

        + +
          +
        • Maps: Adds details and reviews when a user does a search on Maps and +clicks on a business to view it's details.
        • +
        • Dialer: In-call screen timeout default is now longer when using the +speakerphone.
        • +
        • Dialer: Adds a "Show dialpad" / "Hide dialpad" item to the in-call +menu, to make it easier to discover the DTMF dialpad.
        • +
        • Adds support for saving attachments from MMS
        • +
        • Adds support for marquee in layouts.
        • +
        + +

        API Changes

        + +

        Overview

        + +
          +
        • Adds annotations for test systems, no actual (non-test) API +changes.
        • +
        • Adds a method to allow a process to easily determine its UID. +
        • Adds support for marquee in layouts.
        • +
        • Adds new methods for determining padding in views. Useful if you are +writing your own +subclasses of {@link android.view.View View}.
        • +
        • Adds new permissions that allow an application to broadcast an SMS +or WAP Push message.
        • +
        • API cleanup: removes protected constructor from +SDK-bound system images.
        • +
        + +

        API Change Details

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Module or FeatureChange Description
        Annotations for test systems
        Added class {@link android.test.suitebuilder.annotation.LargeTest LargeTest} to package {@link android.test.suitebuilder.annotation}
        Added class {@link android.test.suitebuilder.annotation.MediumTest MediumTest} to package {@link android.test.suitebuilder.annotation}
        Added class {@link android.test.suitebuilder.annotation.SmallTest SmallTest} to package {@link android.test.suitebuilder.annotation}
        Allow a process to easily know its UID.
        Added public method {@link android.os.Process#myUid} to class {@link android.os.Process android.os.Process}
        Padding in views
        Added public method {@link android.view.View#getBottomPaddingOffset} to class {@link android.view.View android.view.View}.
        Added public method {@link android.view.View#getLeftPaddingOffset} to class {@link android.view.View android.view.View}.
        Added public method {@link android.view.View#getRightPaddingOffset} to class {@link android.view.View android.view.View}.
        Added public method {@link android.view.View#getTopPaddingOffset} to class {@link android.view.View android.view.View}.
        Added public method {@link android.view.View#isPaddingOffsetRequired} to class {@link android.view.View android.view.View}.
        Marquee support
        Added public method {@link android.widget.TextView#setMarqueeRepeatLimit} to class {@link android.widget.TextView}
        Added public field {@link android.R.attr#marqueeRepeatLimit android.R.attr.marqueeRepeatLimit}
        New permissions
        Added public field {@link android.Manifest.permission#BROADCAST_SMS android.Manifest.permission.BROADCAST_SMS}
        Added public field {@link android.Manifest.permission#BROADCAST_WAP_PUSH android.Manifest.permission.BROADCAST_WAP_PUSH}
        API cleanup
        Removed protected constructor java.net.ServerSocket.ServerSocket(java.net.SocketImpl).
        + + + + + + diff --git a/docs/html/sdk/download.jd b/docs/html/sdk/download.jd new file mode 100644 index 0000000000000..d64b781b7521b --- /dev/null +++ b/docs/html/sdk/download.jd @@ -0,0 +1,76 @@ +page.title=Download the Android SDK +@jd:body + +

        Please carefully review the Android SDK License Agreement before downloading the SDK. +The License Agreement constitutes a contract between you and Google with respect to your use of the SDK.

        + + + + + +

        + + +

        +

        + +

        +

        + +

        + + + + + + + + + + + + + diff --git a/docs/html/sdk/index.html b/docs/html/sdk/index.html deleted file mode 100644 index c5468ddc2bf48..0000000000000 --- a/docs/html/sdk/index.html +++ /dev/null @@ -1,6 +0,0 @@ - - -Redirecting... - - - \ No newline at end of file diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd new file mode 100644 index 0000000000000..38db6f80de08c --- /dev/null +++ b/docs/html/sdk/index.jd @@ -0,0 +1,5 @@ +sdk.redirect=1.1_r1 +@jd:body + + + diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs index 11d3086d6ff25..f617302278cc0 100644 --- a/docs/html/sdk/sdk_toc.cs +++ b/docs/html/sdk/sdk_toc.cs @@ -1,37 +1,20 @@ + + diff --git a/docs/html/sdk/terms.jd b/docs/html/sdk/terms.jd index 5599fa691a898..72e5bd0378e9a 100644 --- a/docs/html/sdk/terms.jd +++ b/docs/html/sdk/terms.jd @@ -1,4 +1,5 @@ page.title=Terms and Conditions +sdk.version=1.1_r1 @jd:body

        This is the Android Software Development Kit License Agreement.

        diff --git a/docs/html/sdk/terms_body.html b/docs/html/sdk/terms_body.html new file mode 100644 index 0000000000000..e8fdc3cff7619 --- /dev/null +++ b/docs/html/sdk/terms_body.html @@ -0,0 +1,216 @@ + +

        This is the Android Software Development Kit License Agreement.

        + +

        + 1. Introduction +

        +

        + 1.1 The Android Software Development Kit (referred to in this License Agreement as the "SDK" and specifically including the Android system files and packaged APIs) is licensed to you subject to the terms of this License Agreement. This License Agreement forms a legally binding contract between you and Google in relation to your use of the SDK. +

        +

        + 1.2 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States. +

        +

        + 2. Accepting this License Agreement +

        +

        + 2.1 In order to use the SDK, you must first agree to this License Agreement. You may not use the SDK if you do not accept this License Agreement. +

        +

        + 2.2 You can accept this License Agreement by: +

        +

        + (A) clicking to accept or agree to this License Agreement, where this option is made available to you; or +

        +

        + (B) by actually using the SDK. In this case, you agree that use of the SDK constitutes acceptance of the Licensing Agreement from that point onwards. +

        +

        + 2.3 You may not use the SDK and may not accept the Licensing Agreement if you are a person barred from receiving the SDK under the laws of the United States or other countries including the country in which you are resident or from which you use the SDK. +

        +

        + 2.4 If you are agreeing to be bound by this License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to this License Agreement. If you do not have the requisite authority, you may not accept the Licensing Agreement or use the SDK on behalf of your employer or other entity. +

        +

        + 3. SDK License from Google +

        +

        + 3.1 Subject to the terms of this License Agreement, Google grants you a limited, worldwide, royalty-free, non- assignable and non-exclusive license to use the SDK solely to develop applications to run on the Android platform. +

        +

        + 3.2 You agree that Google or third parties own all legal right, title and interest in and to the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. The Android Open Source Project +

        +

        + 3.3 Except to the extent required by applicable third party licenses, you may not copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the SDK or any part of the SDK. Except to the extent required by applicable third party licenses, you may not load any part of the SDK onto a mobile handset or any other hardware device except a personal computer, combine any part of the SDK with other software, or distribute any software or device incorporating a part of the SDK. +

        +

        + 3.4 Use, reproduction and distribution of components of the SDK licensed under an open source software license are governed solely by the terms of that open source software license and not this License Agreement. +

        +

        + 3.5 You agree that the form and nature of the SDK that Google provides may change without prior notice to you and that future versions of the SDK may be incompatible with applications developed on previous versions of the SDK. You agree that Google may stop (permanently or temporarily) providing the SDK (or any features within the SDK) to you or to users generally at Google's sole discretion, without prior notice to you. +

        +

        + 3.6 Nothing in this License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features. +

        +

        + 3.7 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the SDK. +

        +

        + 4. Use of the SDK by You +

        +

        + 4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under this License Agreement in or to any software applications that you develop using the SDK, including any intellectual property rights which subsist in those applications. +

        +

        + 4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a) this License Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries). +

        +

        + 4.3 You agree that if you use the SDK to develop applications for general public users, you will protect the privacy and legal rights of those users. If the users provide you with user names, passwords, or other login information or personal information, your must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If the user provides your application with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, the user has given you permission to do so. +

        +

        + 4.4 You agree that you will not engage in any activity with the SDK, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of any third party including, but not limited to, Google or any mobile communications carrier. +

        +

        + 4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through the Android platform and/or applications for the Android platform, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so. +

        +

        + 4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under this License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach. +

        +

        + 5. Your Developer Credentials +

        +

        + 5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials. +

        +

        + 6. Privacy and Information +

        +

        + 6.1 In order to continually innovate and improve the SDK, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the SDK are being used and how they are being used. Before any of this information is collected, the SDK will notify you and seek your consent. If you withhold consent, the information will not be collected. +

        +

        + 6.2 The data collected is examined in the aggregate to improve the SDK and is maintained in accordance with Google's Privacy Policy. +

        +

        + 7. Third Party Applications for the Android Platform +

        +

        + 7.1 If you use the SDK to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources. +

        +

        + 7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners. +

        +

        + 7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, this License Agreement does not affect your legal relationship with these third parties. +

        +

        + 8. Using Android APIs +

        +

        + 8.1 Android Maps API +

        +

        + 8.1.1 If you use the Android Maps API (described in the SDK by the Package names "com.google.android.maps" and "com.android.location.Geocoder"), the terms of your binding legal agreement with Google include this License Agreement, the Google Maps API Terms of Service and the Google Maps Terms of Service. You must read and agree to those Terms of Service before you use the Android Maps API. +

        +

        + 8.1.2 If you use the Android Maps API to retrieve map or satellite image data from Google, you must include the following copyright notice in your application or service in a manner that is reasonably available to users: +

        +

        + "Copyright Notice: Data: (c)2007 TeleAtlas, AND, Europa Technologies, Kingway, Map Data Sciences Pty Ltd, PSMA, ZENRIN, Geocentre, MapLink/TeleAtlas; Imagery: (c)2007 DigitalGlobe, EarthSat, Sanborn, NYGIS, Scankort, TerraMetrics, MassGIS Commonwealth of Massachusetts, Digital Earth Technology." +

        +

        + 8.2 Google Data APIs +

        +

        + 8.2.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by those who provide that data (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless you have been specifically given permission to do so by the owners of that data. +

        +

        + 8.2.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so. +

        +

        + 9. Terminating this License Agreement +

        +

        + 9.1 This License Agreement will continue to apply until terminated by either you or Google as set out below. +

        +

        + 9.2 If you want to terminate this License Agreement, you may do so by ceasing your use of the SDK and any relevant developer credentials. +

        +

        + 9.3 Google may at any time, terminate this License Agreement with you if: +

        +

        + (A) you have breached any provision of this License Agreement; or +

        +

        + (B) Google is required to do so by law; or +

        +

        + (C) the partner with whom Google offered certain parts of SDK (such as APIs) to you has terminated its relationship with Google or ceased to offer certain parts of the SDK to you; or +

        +

        + (D) Google decides to no longer providing the SDK or certain parts of the SDK to users in the country in which you are resident or from which you use the service, or the provision of the SDK or certain SDK services to you by Google is, in Google's sole discretion, no longer commercially viable. +

        +

        + 9.4 When this License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst this License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely. +

        +

        + 10. DISCLAIMER OF WARRANTIES +

        +

        + 10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE RISK AND THAT THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE. +

        +

        + 10.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE. +

        +

        + 10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +

        +

        + 11. LIMITATION OF LIABILITY +

        +

        + 11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING. +

        +

        + 12. Indemnification +

        +

        + 12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with this License Agreement. +

        +

        + 13. Changes to the License Agreement +

        +

        + 13.1 Google may make changes to the License Agreement as it distributes new versions of the SDK. When these changes are made, Google will make a new version of the License Agreement available on the website where the SDK is made available and with the SDK downloadable. +

        +

        + 13.2 You agree that your use of a specific version of the SDK is governed by the License Agreement included with that version of the SDK. +

        +

        + 14. General Legal Terms +

        +

        + 14.1 This License Agreement constitute the whole legal agreement between you and Google and govern your use of the SDK (excluding any services which Google may provide to you under a separate written agreement), and completely replace any prior agreements between you and Google in relation to the SDK. +

        +

        + 14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in this License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google. +

        +

        + 14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of this License Agreement is invalid, then that provision will be removed from this License Agreement without affecting the rest of this License Agreement. The remaining provisions of this License Agreement will continue to be valid and enforceable. +

        +

        + 14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to this License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of this License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to this License Agreement. +

        +

        + 14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE. +

        +

        + 14.6 The rights granted in this License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under this License Agreement without the prior written approval of the other party. +

        +

        + 14.7 This License Agreement, and your relationship with Google under this License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from this License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction. +

        +

        + August 18, 2008 +

        diff --git a/docs/html/search.jd b/docs/html/search.jd old mode 100755 new mode 100644 index 9269d2c66ed1a..979964bd94d87 --- a/docs/html/search.jd +++ b/docs/html/search.jd @@ -5,73 +5,53 @@ page.title=Search Results +

        search results

        diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index dc16c39f4d7e2..0b398bc3b9651 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -16,18 +16,25 @@ package android.graphics; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.io.OutputStream; +import android.os.Parcelable; +import android.os.Parcel; + import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ShortBuffer; import java.nio.IntBuffer; - -import android.os.Parcel; -import android.os.Parcelable; +import java.io.OutputStream; public final class Bitmap implements Parcelable { + /** + * Indicates that the bitmap was created for an unknown pixel density. + * + * @see Bitmap#getDensityScale() + * @see Bitmap#setDensityScale(float) + * + * @hide pending API council approval + */ + public static final float DENSITY_SCALE_UNKNOWN = -1.0f; // Note: mNativeBitmap is used by FaceDetector_jni.cpp // Don't change/rename without updating FaceDetector_jni.cpp @@ -41,6 +48,9 @@ public final class Bitmap implements Parcelable { private static volatile Matrix sScaleMatrix; + private float mDensityScale = DENSITY_SCALE_UNKNOWN; + private boolean mAutoScaling; + /** * @noinspection UnusedDeclaration */ @@ -59,6 +69,104 @@ public final class Bitmap implements Parcelable { mIsMutable = isMutable; mNinePatchChunk = ninePatchChunk; } + + /** + *

        Returns the density scale for this bitmap, expressed as a factor of + * the default density (160.) For instance, a bitmap designed for + * displays with a density of 240 will have a density scale of 1.5 whereas a bitmap + * designed for a density of 160 will have a density scale of 1.0.

        + * + *

        The default density scale is {@link #DENSITY_SCALE_UNKNOWN}.

        + * + * @return A scaling factor of the default density (160) or {@link #DENSITY_SCALE_UNKNOWN} + * if the scaling factor is unknown. + * + * @see #setDensityScale(float) + * @see #isAutoScalingEnabled() + * @see #setAutoScalingEnabled(boolean) + * @see android.util.DisplayMetrics#DEFAULT_DENSITY + * @see android.util.DisplayMetrics#density + * @see #DENSITY_SCALE_UNKNOWN + * + * @hide pending API council approval + */ + public float getDensityScale() { + return mDensityScale; + } + + /** + *

        Specifies the density scale for this bitmap, expressed as a factor of + * the default density (160.) For instance, a bitmap designed for + * displays with a density of 240 will have a density scale of 1.5 whereas a bitmap + * designed for a density of 160 will have a density scale of 1.0.

        + * + * @param densityScale The density scaling factor to use with this bitmap or + * {@link #DENSITY_SCALE_UNKNOWN} if the factor is unknown. + * + * @see #getDensityScale() + * @see #isAutoScalingEnabled() + * @see #setAutoScalingEnabled(boolean) + * @see android.util.DisplayMetrics#DEFAULT_DENSITY + * @see android.util.DisplayMetrics#density + * @see #DENSITY_SCALE_UNKNOWN + * + * @hide pending API council approval + */ + public void setDensityScale(float densityScale) { + mDensityScale = densityScale; + } + + /** + *

        Indicates whether this bitmap will be automatically be scaled at the + * target's density at drawing time. If auto scaling is enabled, this bitmap + * will be drawn with the following scale factor:

        + * + *
        scale = (bitmap density scale factor) / (target density scale factor)
        + * + *

        Auto scaling is turned off by default. If auto scaling is enabled but the + * bitmap has an unknown density scale, then the bitmap will never be automatically + * scaled at drawing time.

        + * + * @return True if the bitmap must be scaled at drawing time, false otherwise. + * + * @see #setAutoScalingEnabled(boolean) + * @see #getDensityScale() + * @see #setDensityScale(float) + * + * @hide pending API council approval + */ + public boolean isAutoScalingEnabled() { + return mAutoScaling; + } + + /** + *

        Enables or disables auto scaling for this bitmap. When auto scaling is enabled, + * the bitmap will be scaled at drawing time to accomodate the drawing target's pixel + * density. The final scale factor for this bitmap is thus defined:

        + * + *
        scale = (bitmap density scale factor) / (target density scale factor)
        + * + *

        If auto scaling is enabled but the bitmap has an unknown density scale, then + * the bitmap will never be automatically scaled at drawing time.

        + * + * @param autoScalingEnabled True to scale the bitmap at drawing time, false otherwise. + * + * @hide pending API council approval + */ + public void setAutoScalingEnabled(boolean autoScalingEnabled) { + mAutoScaling = autoScalingEnabled; + } + + /** + * Sets the nine patch chunk. + * + * @param chunk The definition of the nine patch + * + * @hide + */ + public void setNinePatchChunk(byte[] chunk) { + mNinePatchChunk = chunk; + } /** * Free up the memory associated with this bitmap's pixels, and mark the @@ -126,7 +234,7 @@ public final class Bitmap implements Parcelable { throw new IllegalArgumentException("height must be > 0"); } } - + public enum Config { // these native values must match up with the enum in SkBitmap.h ALPHA_8 (2), @@ -232,7 +340,7 @@ public final class Bitmap implements Parcelable { public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) { - Matrix m = null; + Matrix m; synchronized (Bitmap.class) { // small pool of just 1 matrix m = sScaleMatrix; @@ -279,8 +387,7 @@ public final class Bitmap implements Parcelable { * @param width The number of pixels in each row * @param height The number of rows */ - public static Bitmap createBitmap(Bitmap source, int x, int y, - int width, int height) { + public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) { return createBitmap(source, x, y, width, height, null, false); } @@ -301,23 +408,21 @@ public final class Bitmap implements Parcelable { * @throws IllegalArgumentException if the x, y, width, height values are * outside of the dimensions of the source bitmap. */ - public static Bitmap createBitmap(Bitmap source, int x, int y, int width, - int height, Matrix m, boolean filter) { + public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height, + Matrix m, boolean filter) { + checkXYSign(x, y); checkWidthHeight(width, height); if (x + width > source.getWidth()) { - throw new IllegalArgumentException( - "x + width must be <= bitmap.width()"); + throw new IllegalArgumentException("x + width must be <= bitmap.width()"); } if (y + height > source.getHeight()) { - throw new IllegalArgumentException( - "y + height must be <= bitmap.height()"); + throw new IllegalArgumentException("y + height must be <= bitmap.height()"); } // check if we can just return our argument unchanged - if (!source.isMutable() && x == 0 && y == 0 - && width == source.getWidth() && height == source.getHeight() - && (m == null || m.isIdentity())) { + if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() && + height == source.getHeight() && (m == null || m.isIdentity())) { return source; } @@ -331,8 +436,8 @@ public final class Bitmap implements Parcelable { RectF dstR = new RectF(0, 0, width, height); if (m == null || m.isIdentity()) { - bitmap = createBitmap(neww, newh, source.hasAlpha() ? - Config.ARGB_8888 : Config.RGB_565); + bitmap = createBitmap(neww, newh, + source.hasAlpha() ? Config.ARGB_8888 : Config.RGB_565); paint = null; // not needed } else { /* the dst should have alpha if the src does, or if our matrix @@ -343,8 +448,7 @@ public final class Bitmap implements Parcelable { m.mapRect(deviceR, dstR); neww = Math.round(deviceR.width()); newh = Math.round(deviceR.height()); - bitmap = createBitmap(neww, newh, hasAlpha ? - Config.ARGB_8888 : Config.RGB_565); + bitmap = createBitmap(neww, newh, hasAlpha ? Config.ARGB_8888 : Config.RGB_565); if (hasAlpha) { bitmap.eraseColor(0); } @@ -358,7 +462,12 @@ public final class Bitmap implements Parcelable { } canvas.setBitmap(bitmap); canvas.drawBitmap(source, srcR, dstR, paint); - + + // The new bitmap was created from a known bitmap source so assume that + // they use the same density scale + bitmap.setDensityScale(source.getDensityScale()); + bitmap.setAutoScalingEnabled(source.isAutoScalingEnabled()); + return bitmap; } @@ -371,8 +480,7 @@ public final class Bitmap implements Parcelable { * @throws IllegalArgumentException if the width or height are <= 0 */ public static Bitmap createBitmap(int width, int height, Config config) { - Bitmap bm = nativeCreate(null, 0, width, width, height, - config.nativeInt, true); + Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true); bm.eraseColor(0); // start with black/transparent pixels return bm; } @@ -395,16 +503,16 @@ public final class Bitmap implements Parcelable { * the color array's length is less than the number of pixels. */ public static Bitmap createBitmap(int colors[], int offset, int stride, - int width, int height, Config config) { + int width, int height, Config config) { + checkWidthHeight(width, height); if (Math.abs(stride) < width) { throw new IllegalArgumentException("abs(stride) must be >= width"); } int lastScanline = offset + (height - 1) * stride; int length = colors.length; - if (offset < 0 || (offset + width > length) - || lastScanline < 0 - || (lastScanline + width > length)) { + if (offset < 0 || (offset + width > length) || lastScanline < 0 || + (lastScanline + width > length)) { throw new ArrayIndexOutOfBoundsException(); } return nativeCreate(colors, offset, stride, width, height, @@ -425,8 +533,7 @@ public final class Bitmap implements Parcelable { * @throws IllegalArgumentException if the width or height are <= 0, or if * the color array's length is less than the number of pixels. */ - public static Bitmap createBitmap(int colors[], int width, int height, - Config config) { + public static Bitmap createBitmap(int colors[], int width, int height, Config config) { return createBitmap(colors, 0, width, width, height, config); } @@ -474,8 +581,7 @@ public final class Bitmap implements Parcelable { * @param stream The outputstream to write the compressed data. * @return true if successfully compressed to the specified stream. */ - public boolean compress(CompressFormat format, int quality, - OutputStream stream) { + public boolean compress(CompressFormat format, int quality, OutputStream stream) { checkRecycled("Can't compress a recycled bitmap"); // do explicit check before calling the native method if (stream == null) { @@ -505,6 +611,32 @@ public final class Bitmap implements Parcelable { return mHeight == -1 ? mHeight = nativeHeight(mNativeBitmap) : mHeight; } + /** + * Convenience method that returns the width of this bitmap divided + * by the density scale factor. + * + * @return The scaled width of this bitmap, according to the density scale factor. + * + * @hide pending API council approval + */ + public int getScaledWidth() { + final float scale = getDensityScale(); + return scale == DENSITY_SCALE_UNKNOWN ? getWidth() : (int) (getWidth() / scale); + } + + /** + * Convenience method that returns the height of this bitmap divided + * by the density scale factor. + * + * @return The scaled height of this bitmap, according to the density scale factor. + * + * @hide pending API council approval + */ + public int getScaledHeight() { + final float scale = getDensityScale(); + return scale == DENSITY_SCALE_UNKNOWN ? getWidth() : (int) (getHeight() / scale); + } + /** * Return the number of bytes between rows in the bitmap's pixels. Note that * this refers to the pixels as stored natively by the bitmap. If you call @@ -836,7 +968,7 @@ public final class Bitmap implements Parcelable { private static native Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint, int[] offsetXY); - + /* package */ final int ni() { return mNativeBitmap; } diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 2c3f543a90e9d..3813d8f7ca973 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -18,26 +18,20 @@ package android.graphics; import android.content.res.AssetManager; import android.content.res.Resources; -import android.util.Log; +import android.util.TypedValue; +import android.util.DisplayMetrics; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.io.FileDescriptor; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.nio.ShortBuffer; - /** * Creates Bitmap objects from various sources, including files, streams, * and byte-arrays. */ public class BitmapFactory { - private static final String TAG = "BitmapFactory"; - private static final boolean DEBUG_LOAD = false; - public static class Options { /** * Create a default Options object, which if left unchanged will give @@ -45,6 +39,8 @@ public class BitmapFactory { */ public Options() { inDither = true; + inDensity = 0; + inScaled = true; } /** @@ -53,6 +49,7 @@ public class BitmapFactory { * the bitmap without having to allocate the memory for its pixels. */ public boolean inJustDecodeBounds; + /** * If set to a value > 1, requests the decoder to subsample the original * image, returning a smaller image to save memory. The sample size is @@ -80,20 +77,45 @@ public class BitmapFactory { * image. */ public boolean inDither; - + + /** + * The desired pixel density of the bitmap. + * + * @see android.util.DisplayMetrics#DEFAULT_DENSITY + * @see android.util.DisplayMetrics#density + * + * @hide pending API council approval + */ + public int inDensity; + + /** + *

        If the bitmap is loaded from {@link android.content.res.Resources} and + * this flag is turned on, the bitmap will be scaled to match the default + * display's pixel density.

        + * + *

        This flag is turned on by default and should be turned off if you need + * a non-scaled version of the bitmap. In this case, + * {@link android.graphics.Bitmap#setAutoScalingEnabled(boolean)} can be used + * to properly scale the bitmap at drawing time.

        + * + * @hide pending API council approval + */ + public boolean inScaled; + /** * The resulting width of the bitmap, set independent of the state of * inJustDecodeBounds. However, if there is an error trying to decode, * outWidth will be set to -1. */ public int outWidth; + /** * The resulting height of the bitmap, set independent of the state of * inJustDecodeBounds. However, if there is an error trying to decode, * outHeight will be set to -1. */ public int outHeight; - + /** * If known, this string is set to the mimetype of the decoded image. * If not know, or there is an error, it is set to null. @@ -103,7 +125,7 @@ public class BitmapFactory { /** * Temp storage to use for decoding. Suggest 16K or so. */ - public byte [] inTempStorage; + public byte[] inTempStorage; private native void requestCancel(); @@ -174,6 +196,58 @@ public class BitmapFactory { return decodeFile(pathName, null); } + /** + * Decode a new Bitmap from an InputStream. This InputStream was obtained from + * resources, which we pass to be able to scale the bitmap accordingly. + * + * @hide + */ + public static Bitmap decodeStream(Resources res, TypedValue value, InputStream is, + Rect pad, Options opts) { + + if (opts == null) { + opts = new Options(); + } + + Bitmap bm = decodeStream(is, pad, opts); + + if (bm != null && res != null && value != null) { + byte[] np = bm.getNinePatchChunk(); + final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); + + final int density = value.density; + if (opts.inDensity == 0) { + opts.inDensity = density == TypedValue.DENSITY_DEFAULT ? + DisplayMetrics.DEFAULT_DENSITY : density; + } + float scale = opts.inDensity / (float) DisplayMetrics.DEFAULT_DENSITY; + + if (opts.inScaled || isNinePatch) { + bm.setDensityScale(1.0f); + bm.setAutoScalingEnabled(false); + // Assume we are going to prescale for the screen + scale = res.getDisplayMetrics().density / scale; + if (scale != 1.0f) { + // TODO: This is very inefficient and should be done in native by Skia + final Bitmap oldBitmap = bm; + bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f), + (int) (bm.getHeight() * scale + 0.5f), true); + oldBitmap.recycle(); + + if (isNinePatch) { + np = nativeScaleNinePatch(np, scale, pad); + bm.setNinePatchChunk(np); + } + } + } else { + bm.setDensityScale(scale); + bm.setAutoScalingEnabled(true); + } + } + + return bm; + } + /** * Decode an image referenced by a resource ID. * @@ -189,11 +263,12 @@ public class BitmapFactory { Bitmap bm = null; try { - InputStream is = res.openRawResource(id); - bm = decodeStream(is, null, opts); + final TypedValue value = new TypedValue(); + final InputStream is = res.openRawResource(id, value); + + bm = decodeStream(res, value, is, null, opts); is.close(); - } - catch (java.io.IOException e) { + } catch (java.io.IOException e) { /* do nothing. If the exception happened on open, bm will be null. If it happened on close, bm is still valid. @@ -226,8 +301,7 @@ public class BitmapFactory { * decoded, or, if opts is non-null, if opts requested only the * size be returned (in opts.outWidth and opts.outHeight) */ - public static Bitmap decodeByteArray(byte[] data, int offset, int length, - Options opts) { + public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) { if ((offset | length) < 0 || data.length < offset + length) { throw new ArrayIndexOutOfBoundsException(); } @@ -265,8 +339,7 @@ public class BitmapFactory { * decoded, or, if opts is non-null, if opts requested only the * size be returned (in opts.outWidth and opts.outHeight) */ - public static Bitmap decodeStream(InputStream is, Rect outPadding, - Options opts) { + public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) { // we don't throw in this case, thus allowing the caller to only check // the cache, and not force the image to be decoded. if (is == null) { @@ -287,11 +360,9 @@ public class BitmapFactory { Bitmap bm; if (is instanceof AssetManager.AssetInputStream) { - bm = nativeDecodeAsset( - ((AssetManager.AssetInputStream)is).getAssetInt(), outPadding, - opts); - } - else { + bm = nativeDecodeAsset(((AssetManager.AssetInputStream) is).getAssetInt(), + outPadding, opts); + } else { // pass some temp storage down to the native code. 1024 is made up, // but should be large enough to avoid too many small calls back // into is.read(...) This number is not related to the value passed @@ -337,8 +408,7 @@ public class BitmapFactory { * image should be completely decoded, or just is size returned. * @return the decoded bitmap, or null */ - public static Bitmap decodeFileDescriptor(FileDescriptor fd, - Rect outPadding, Options opts) { + public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) { return nativeDecodeFileDescriptor(fd, outPadding, opts); } @@ -354,13 +424,13 @@ public class BitmapFactory { return nativeDecodeFileDescriptor(fd, null, null); } - private static native Bitmap nativeDecodeStream(InputStream is, - byte[] storage, Rect padding, Options opts); + private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage, + Rect padding, Options opts); private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, - Rect padding, Options opts); - private static native Bitmap nativeDecodeAsset(int asset, Rect padding, - Options opts); + Rect padding, Options opts); + private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts); private static native Bitmap nativeDecodeByteArray(byte[] data, int offset, - int length, Options opts); + int length, Options opts); + private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad); } diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index b57f42854a0d9..32ecd9fa49aab 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -50,6 +50,8 @@ public class Canvas { // Used by native code @SuppressWarnings({"UnusedDeclaration"}) private int mSurfaceFormat; + @SuppressWarnings({"UnusedDeclaration"}) + private float mDensityScale = 1.0f; /** * Construct an empty raster canvas. Use setBitmap() to specify a bitmap to @@ -74,6 +76,8 @@ public class Canvas { throwIfRecycled(bitmap); mNativeCanvas = initRaster(bitmap.ni()); mBitmap = bitmap; + mDensityScale = bitmap.getDensityScale(); + if (mDensityScale == Bitmap.DENSITY_SCALE_UNKNOWN) mDensityScale = 1.0f; } /*package*/ Canvas(int nativeCanvas) { @@ -126,6 +130,8 @@ public class Canvas { native_setBitmap(mNativeCanvas, bitmap.ni()); mBitmap = bitmap; + mDensityScale = bitmap.getDensityScale(); + if (mDensityScale == Bitmap.DENSITY_SCALE_UNKNOWN) mDensityScale = 1.0f; } /** @@ -163,6 +169,51 @@ public class Canvas { */ public native int getHeight(); + /** + *

        Returns the density scale for this Canvas' backing bitmap, expressed as a + * factor of the default density (160dpi.) For instance, a bitmap designed for + * 240dpi displays will have a density scale of 1.5 whereas a bitmap + * designed for 160dpi will have a density scale of 1.0.

        + * + *

        The default density scale is {@link Bitmap#DENSITY_SCALE_UNKNOWN}.

        + * + * @return A scaling factor of the default density (160dpi) or + * {@link Bitmap#DENSITY_SCALE_UNKNOWN} if the scaling factor is unknown. + * + * @see #setDensityScale(float) + * @see Bitmap#getDensityScale() + * + * @hide pending API council approval + */ + public float getDensityScale() { + if (mBitmap != null) { + return mBitmap.getDensityScale(); + } + return mDensityScale; + } + + /** + *

        Specifies the density scale for this Canvas' backing bitmap, expressed as a + * factor of the default density (160dpi.) For instance, a bitmap designed for + * 240dpi displays will have a density scale of 1.5 whereas a bitmap + * designed for 160dpi will have a density scale of 1.0.

        + * + * @param densityScale The density scaling factor to use with this bitmap or + * {@link Bitmap#DENSITY_SCALE_UNKNOWN} if the factor is unknown. + * + * @see #getDensityScale() + * @see Bitmap#setDensityScale(float) + * + * @hide pending API council approval + */ + public void setDensityScale(float densityScale) { + if (mBitmap != null) { + mBitmap.setDensityScale(densityScale); + } + mDensityScale = densityScale; + if (mDensityScale == Bitmap.DENSITY_SCALE_UNKNOWN) mDensityScale = 1.0f; + } + // the SAVE_FLAG constants must match their native equivalents /** restore the current matrix when restore() is called */ @@ -910,7 +961,8 @@ public class Canvas { public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { throwIfRecycled(bitmap); native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top, - paint != null ? paint.mNativePaint : 0); + paint != null ? paint.mNativePaint : 0, bitmap.isAutoScalingEnabled(), + bitmap.getDensityScale()); } /** @@ -982,8 +1034,8 @@ public class Canvas { * be 0xFF for every pixel). * @param paint May be null. The paint used to draw the bitmap */ - public void drawBitmap(int[] colors, int offset, int stride, int x, int y, - int width, int height, boolean hasAlpha, + public void drawBitmap(int[] colors, int offset, int stride, float x, + float y, int width, int height, boolean hasAlpha, Paint paint) { // check for valid input if (width < 0) { @@ -1006,11 +1058,20 @@ public class Canvas { return; } // punch down to native for the actual draw - native_drawBitmap(mNativeCanvas, colors, offset, stride, x, y, - width, height, hasAlpha, - paint != null ? paint.mNativePaint : 0); + native_drawBitmap(mNativeCanvas, colors, offset, stride, x, y, width, height, hasAlpha, + paint != null ? paint.mNativePaint : 0); } + /** Legacy version of drawBitmap(int[] colors, ...) that took ints for x,y + */ + public void drawBitmap(int[] colors, int offset, int stride, int x, int y, + int width, int height, boolean hasAlpha, + Paint paint) { + // call through to the common float version + drawBitmap(colors, offset, stride, (float)x, (float)y, width, height, + hasAlpha, paint); + } + /** * Draw the bitmap using the specified matrix. * @@ -1020,7 +1081,7 @@ public class Canvas { */ public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { nativeDrawBitmapMatrix(mNativeCanvas, bitmap.ni(), matrix.ni(), - paint != null ? paint.mNativePaint : 0); + paint != null ? paint.mNativePaint : 0); } private static void checkRange(int length, int offset, int count) { @@ -1416,25 +1477,27 @@ public class Canvas { float ry, int paint); private static native void native_drawPath(int nativeCanvas, int path, int paint); - private static native void native_drawBitmap(int nativeCanvas, int bitmap, + private native void native_drawBitmap(int nativeCanvas, int bitmap, float left, float top, - int nativePaintOrZero); - private static native void native_drawBitmap(int nativeCanvas, int bitmap, + int nativePaintOrZero, boolean autoScale, + float densityScale); + private native void native_drawBitmap(int nativeCanvas, int bitmap, Rect src, RectF dst, int nativePaintOrZero); private static native void native_drawBitmap(int nativeCanvas, int bitmap, Rect src, Rect dst, int nativePaintOrZero); private static native void native_drawBitmap(int nativeCanvas, int[] colors, - int offset, int stride, int x, - int y, int width, int height, + int offset, int stride, float x, + float y, int width, int height, boolean hasAlpha, int nativePaintOrZero); private static native void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, int nMatrix, int nPaint); private static native void nativeDrawBitmapMesh(int nCanvas, int nBitmap, - int meshWidth, int meshHeight, float[] verts, int vertOffset, - int[] colors, int colorOffset, int nPaint); + int meshWidth, int meshHeight, + float[] verts, int vertOffset, + int[] colors, int colorOffset, int nPaint); private static native void nativeDrawVertices(int nCanvas, int mode, int n, float[] verts, int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices, diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java index cabd84805f549..2b24ef227e332 100644 --- a/graphics/java/android/graphics/NinePatch.java +++ b/graphics/java/android/graphics/NinePatch.java @@ -26,10 +26,10 @@ package android.graphics; * way that you define, when content added within the image exceeds the normal * bounds of the graphic. For a thorough explanation of a NinePatch image, * read the discussion in the - * Available - * Resource Types document. + * 2D + * Graphics document. *

        - * The Draw 9-patch + * The Draw 9-Patch * tool offers an extremely handy way to create your NinePatch images, * using a WYSIWYG graphics editor. *

        @@ -50,6 +50,17 @@ public class NinePatch { validateNinePatchChunk(mBitmap.ni(), chunk); } + /** + * @hide + */ + public NinePatch(NinePatch patch) { + mBitmap = patch.mBitmap; + mChunk = patch.mChunk; + mSrcName = patch.mSrcName; + mPaint = new Paint(patch.mPaint); + validateNinePatchChunk(mBitmap.ni(), mChunk); + } + public void setPaint(Paint p) { mPaint = p; } diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java index c6b772e96e470..bab1703fbf242 100644 --- a/graphics/java/android/graphics/drawable/AnimationDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java @@ -74,6 +74,7 @@ import android.util.AttributeSet; public class AnimationDrawable extends DrawableContainer implements Runnable { private final AnimationState mAnimationState; private int mCurFrame = -1; + private boolean mMutated; public AnimationDrawable() { this(null); @@ -283,6 +284,15 @@ public class AnimationDrawable extends DrawableContainer implements Runnable { setFrame(0, true, false); } + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mAnimationState.mDurations = mAnimationState.mDurations.clone(); + mMutated = true; + } + return this; + } + private final static class AnimationState extends DrawableContainerState { private int[] mDurations; private boolean mOneShot; diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 7f3df9afc1579..5b32246c9bb93 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -28,6 +28,7 @@ import android.graphics.Rect; import android.graphics.Shader; import android.graphics.BitmapShader; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.view.Gravity; import org.xmlpull.v1.XmlPullParser; @@ -62,9 +63,12 @@ public class BitmapDrawable extends Drawable { private boolean mApplyGravity; private boolean mRebuildShader; + private int mBitmapWidth; + private int mBitmapHeight; + private boolean mMutated; public BitmapDrawable() { - mBitmapState = new BitmapState(null); + mBitmapState = new BitmapState((Bitmap) null); } public BitmapDrawable(Bitmap bitmap) { @@ -92,7 +96,62 @@ public class BitmapDrawable extends Drawable { public final Bitmap getBitmap() { return mBitmap; } - + + private void setBitmap(Bitmap bitmap) { + mBitmap = bitmap; + if (bitmap != null) { + mBitmapWidth = bitmap.getWidth(); + mBitmapHeight = bitmap.getHeight(); + } else { + mBitmapWidth = mBitmapHeight = -1; + } + } + + /** + * Set the density scale at which this drawable will be rendered. This + * method assumes the drawable will be rendered at the same density as the + * specified canvas. + * + * @param canvas The Canvas from which the density scale must be obtained. + * + * @see android.graphics.Bitmap#setDensityScale(float) + * @see android.graphics.Bitmap#getDensityScale() + * + * @hide pending API council approval + */ + public void setDensityScale(Canvas canvas) { + setDensityScale(canvas.getDensityScale()); + } + + /** + * Set the density scale at which this drawable will be rendered. + * + * @param metrics The DisplayMetrics indicating the density scale for this drawable. + * + * @see android.graphics.Bitmap#setDensityScale(float) + * @see android.graphics.Bitmap#getDensityScale() + * + * @hide pending API council approval + */ + public void setDensityScale(DisplayMetrics metrics) { + setDensityScale(metrics.density); + } + + /** + * Set the density scale at which this drawable will be rendered. + * + * @param density The density scale for this drawable. + * + * @see android.graphics.Bitmap#setDensityScale(float) + * @see android.graphics.Bitmap#getDensityScale() + * + * @hide pending API council approval + */ + public void setDensityScale(float density) { + density = (density == Bitmap.DENSITY_SCALE_UNKNOWN ? 1.0f : density); + mBitmapState.mTargetDensityScale = density; + } + /** Get the gravity used to position/stretch the bitmap within its bounds. See android.view.Gravity * @return the gravity applied to the bitmap @@ -184,7 +243,7 @@ public class BitmapDrawable extends Drawable { Shader shader = state.mPaint.getShader(); if (shader == null) { if (mApplyGravity) { - Gravity.apply(state.mGravity, bitmap.getWidth(), bitmap.getHeight(), + Gravity.apply(state.mGravity, mBitmapWidth, mBitmapHeight, getBounds(), mDstRect); mApplyGravity = false; } @@ -209,6 +268,21 @@ public class BitmapDrawable extends Drawable { mBitmapState.mPaint.setColorFilter(cf); } + /** + * A mutable BitmapDrawable still shares its Bitmap with any other Drawable + * that comes from the same resource. + * + * @return This drawable. + */ + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mBitmapState = new BitmapState(mBitmapState); + mMutated = true; + } + return this; + } + @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { @@ -226,7 +300,9 @@ public class BitmapDrawable extends Drawable { throw new XmlPullParserException(parser.getPositionDescription() + ": requires a valid src attribute"); } - mBitmapState.mBitmap = mBitmap = bitmap; + mBitmapState.mBitmap = bitmap; + setBitmap(bitmap); + setDensityScale(r.getDisplayMetrics()); final Paint paint = mBitmapState.mPaint; paint.setAntiAlias(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_antialias, @@ -256,14 +332,29 @@ public class BitmapDrawable extends Drawable { @Override public int getIntrinsicWidth() { - Bitmap bitmap = mBitmap; - return bitmap != null ? bitmap.getWidth() : -1; + final Bitmap bitmap = mBitmap; + final BitmapState state = mBitmapState; + + if (!state.mAutoScale || state.mBitmapScale == Bitmap.DENSITY_SCALE_UNKNOWN) { + return mBitmapWidth; + } else { + return bitmap != null ? (int) (mBitmapWidth / + (state.mBitmapScale / state.mTargetDensityScale) + 0.5f) : -1; + + } } @Override public int getIntrinsicHeight() { - Bitmap bitmap = mBitmap; - return bitmap != null ? bitmap.getHeight() : -1; + final Bitmap bitmap = mBitmap; + final BitmapState state = mBitmapState; + + if (!state.mAutoScale || state.mBitmapScale == Bitmap.DENSITY_SCALE_UNKNOWN) { + return mBitmapHeight; + } else { + return bitmap != null ? (int) (mBitmapHeight / + (state.mBitmapScale / state.mTargetDensityScale) + 0.5f) : -1; + } } @Override @@ -289,9 +380,29 @@ public class BitmapDrawable extends Drawable { Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS); Shader.TileMode mTileModeX; Shader.TileMode mTileModeY; + boolean mAutoScale; + float mBitmapScale; + float mTargetDensityScale = 1.0f; BitmapState(Bitmap bitmap) { mBitmap = bitmap; + if (bitmap != null) { + mBitmapScale = bitmap.getDensityScale(); + mAutoScale = bitmap.isAutoScalingEnabled(); + } else { + mBitmapScale = 1.0f; + mAutoScale = false; + } + } + + BitmapState(BitmapState bitmapState) { + this(bitmapState.mBitmap); + mChangingConfigurations = bitmapState.mChangingConfigurations; + mGravity = bitmapState.mGravity; + mTileModeX = bitmapState.mTileModeX; + mTileModeY = bitmapState.mTileModeY; + mTargetDensityScale = bitmapState.mTargetDensityScale; + mPaint = new Paint(bitmapState.mPaint); } @Override @@ -307,6 +418,6 @@ public class BitmapDrawable extends Drawable { private BitmapDrawable(BitmapState state) { mBitmapState = state; - mBitmap = state.mBitmap; + setBitmap(state.mBitmap); } } diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java index 60293885e7611..95d4dd096b9cf 100644 --- a/graphics/java/android/graphics/drawable/ClipDrawable.java +++ b/graphics/java/android/graphics/drawable/ClipDrawable.java @@ -24,7 +24,6 @@ import android.content.res.TypedArray; import android.graphics.*; import android.view.Gravity; import android.util.AttributeSet; -import android.util.Log; import java.io.IOException; @@ -100,9 +99,8 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { mClipState.mDrawable = dr; mClipState.mOrientation = orientation; mClipState.mGravity = g; - if (dr != null) { - dr.setCallback(this); - } + + dr.setCallback(this); } // overrides from Drawable.Callback @@ -168,8 +166,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { @Override protected boolean onStateChange(int[] state) { - boolean changed = mClipState.mDrawable.setState(state); - return changed; + return mClipState.mDrawable.setState(state); } @Override @@ -233,7 +230,17 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { return null; } + + final static class ClipState extends ConstantState { + Drawable mDrawable; + int mChangingConfigurations; + int mOrientation; + int mGravity; + + private boolean mCheckedConstantState; + private boolean mCanConstantState; + ClipState(ClipState orig, ClipDrawable owner) { if (orig != null) { mDrawable = orig.mDrawable.getConstantState().newDrawable(); @@ -262,14 +269,6 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { return mCanConstantState; } - - Drawable mDrawable; - int mChangingConfigurations; - int mOrientation; - int mGravity; - - private boolean mCheckedConstantState; - private boolean mCanConstantState; } private ClipDrawable(ClipState state) { diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 00212415aa93b..cf72f3887815a 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -29,6 +29,7 @@ import android.graphics.*; import android.util.AttributeSet; import android.util.StateSet; import android.util.Xml; +import android.util.TypedValue; /** * A Drawable is a general abstraction for "something that can be drawn." Most @@ -91,7 +92,7 @@ import android.util.Xml; * whose overall size is modified based on the current level. *
      *

      For information and examples of creating drawable resources (XML or bitmap files that - * can be loaded in code), see Resources + * can be loaded in code), see Resources * and Internationalization. */ public abstract class Drawable { @@ -102,7 +103,7 @@ public abstract class Drawable { private Rect mBounds = new Rect(); /*package*/ Callback mCallback = null; private boolean mVisible = true; - + /** * Draw in its bounds (set via setBounds) respecting optional effects such * as alpha (set via setAlpha) and color filter (set via setColorFilter). @@ -617,10 +618,37 @@ public abstract class Drawable { return false; } + /** + * Make this drawable mutable. This operation cannot be reversed. A mutable + * drawable is guaranteed to not share its state with any other drawable. + * This is especially useful when you need to modify properties of drawables + * loaded from resources. By default, all drawables instances loaded from + * the same resource share a common state; if you modify the state of one + * instance, all the other instances will receive the same modification. + * + * Calling this method on a mutable Drawable will have no effect. + * + * @return This drawable. + */ + public Drawable mutate() { + return this; + } + /** * Create a drawable from an inputstream */ public static Drawable createFromStream(InputStream is, String srcName) { + return createFromResourceStream(null, null, is, srcName); + } + + /** + * Create a drawable from an inputstream + * + * @hide pending API council approval + */ + public static Drawable createFromResourceStream(Resources res, TypedValue value, + InputStream is, String srcName) { + if (is == null) { return null; } @@ -632,14 +660,14 @@ public abstract class Drawable { Rects only to drop them on the floor. */ Rect pad = new Rect(); - Bitmap bm = BitmapFactory.decodeStream(is, pad, null); + Bitmap bm = BitmapFactory.decodeStream(res, value, is, pad, null); if (bm != null) { byte[] np = bm.getNinePatchChunk(); if (np == null || !NinePatch.isNinePatchChunk(np)) { np = null; pad = null; } - return drawableFromBitmap(bm, np, pad, srcName); + return drawableFromBitmap(res, bm, np, pad, srcName); } return null; } @@ -647,7 +675,7 @@ public abstract class Drawable { /** * Create a drawable from an XML document. For more information on how to * create resources in XML, see - * Resources and + * Resources and * Internationalization. */ public static Drawable createFromXml(Resources r, XmlPullParser parser) @@ -708,6 +736,9 @@ public abstract class Drawable { drawable = new InsetDrawable(); } else if (name.equals("bitmap")) { drawable = new BitmapDrawable(); + if (r != null) { + ((BitmapDrawable) drawable).setDensityScale(r.getDisplayMetrics()); + } } else { throw new XmlPullParserException(parser.getPositionDescription() + ": invalid drawable tag " + name); @@ -728,7 +759,7 @@ public abstract class Drawable { Bitmap bm = BitmapFactory.decodeFile(pathName); if (bm != null) { - return drawableFromBitmap(bm, null, null, pathName); + return drawableFromBitmap(null, bm, null, null, pathName); } return null; @@ -758,11 +789,19 @@ public abstract class Drawable { return null; } - private static Drawable drawableFromBitmap(Bitmap bm, byte[] np, Rect pad, String srcName) { + private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np, + Rect pad, String srcName) { + if (np != null) { return new NinePatchDrawable(bm, np, pad, srcName); } - return new BitmapDrawable(bm); + + final BitmapDrawable drawable = new BitmapDrawable(bm); + if (res != null) { + drawable.setDensityScale(res.getDisplayMetrics()); + } + + return drawable; } } diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index e6c48be9921ad..29f2a0018f17d 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -21,12 +21,13 @@ import android.graphics.*; public class DrawableContainer extends Drawable implements Drawable.Callback { private DrawableContainerState mDrawableContainerState; - private Drawable mCurrDrawable; - private int mAlpha = 0xFF; - private ColorFilter mColorFilter; - private boolean mDither; + private Drawable mCurrDrawable; + private int mAlpha = 0xFF; + private ColorFilter mColorFilter; + private boolean mDither; - private int mCurIndex = -1; + private int mCurIndex = -1; + private boolean mMutated; // overrides from Drawable @@ -229,6 +230,17 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { return null; } + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + for (Drawable child : mDrawableContainerState.mDrawables) { + child.mutate(); + } + mMutated = true; + } + return this; + } + public abstract static class DrawableContainerState extends ConstantState { final DrawableContainer mOwner; @@ -277,7 +289,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mCheckedConstantState = mCanConstantState = true; mVariablePadding = orig.mVariablePadding; - mConstantPadding = orig.mConstantPadding; + if (orig.mConstantPadding != null) { + mConstantPadding = new Rect(orig.mConstantPadding); + } mConstantSize = orig.mConstantSize; mComputedConstantSize = orig.mComputedConstantSize; mConstantWidth = orig.mConstantWidth; diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index cdfca1be8559c..82cb795deccb7 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -106,20 +106,21 @@ public class GradientDrawable extends Drawable { */ public static final int SWEEP_GRADIENT = 2; - private final GradientState mGradientState; + private GradientState mGradientState; private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Rect mPadding; - private Paint mStrokePaint; // optional, set by the caller + private Rect mPadding; + private Paint mStrokePaint; // optional, set by the caller private ColorFilter mColorFilter; // optional, set by the caller - private int mAlpha = 0xFF; // modified by the caller - private boolean mDither; + private int mAlpha = 0xFF; // modified by the caller + private boolean mDither; - private final Path mPath = new Path(); + private final Path mPath = new Path(); private final RectF mRect = new RectF(); - private Paint mLayerPaint; // internal, used if we use saveLayer() - private boolean mRectIsDirty; // internal state + private Paint mLayerPaint; // internal, used if we use saveLayer() + private boolean mRectIsDirty; // internal state + private boolean mMutated; /** * Controls how the gradient is oriented relative to the drawable's bounds @@ -791,31 +792,41 @@ public class GradientDrawable extends Drawable { return mGradientState; } + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mGradientState = new GradientState(mGradientState); + initializeWithState(mGradientState); + mMutated = true; + } + return this; + } + final static class GradientState extends ConstantState { - public int mChangingConfigurations; - public int mShape = RECTANGLE; - public int mGradient = LINEAR_GRADIENT; - public Orientation mOrientation; - public int[] mColors; - public float[] mPositions; - public boolean mHasSolidColor; - public int mSolidColor; - public int mStrokeWidth = -1; // if >= 0 use stroking. - public int mStrokeColor; - public float mStrokeDashWidth; - public float mStrokeDashGap; - public float mRadius; // use this if mRadiusArray is null - public float[] mRadiusArray; - public Rect mPadding; - public int mWidth = -1; - public int mHeight = -1; - public float mInnerRadius; - public float mThickness; - private float mCenterX = 0.5f; - private float mCenterY = 0.5f; - private float mGradientRadius = 0.5f; - private boolean mUseLevel; - private boolean mUseLevelForShape; + public int mChangingConfigurations; + public int mShape = RECTANGLE; + public int mGradient = LINEAR_GRADIENT; + public Orientation mOrientation; + public int[] mColors; + public float[] mPositions; + public boolean mHasSolidColor; + public int mSolidColor; + public int mStrokeWidth = -1; // if >= 0 use stroking. + public int mStrokeColor; + public float mStrokeDashWidth; + public float mStrokeDashGap; + public float mRadius; // use this if mRadiusArray is null + public float[] mRadiusArray; + public Rect mPadding; + public int mWidth = -1; + public int mHeight = -1; + public float mInnerRadius; + public float mThickness; + private float mCenterX = 0.5f; + private float mCenterY = 0.5f; + private float mGradientRadius = 0.5f; + private boolean mUseLevel; + private boolean mUseLevelForShape; GradientState() { @@ -827,6 +838,32 @@ public class GradientDrawable extends Drawable { mColors = colors; } + public GradientState(GradientState state) { + mChangingConfigurations = state.mChangingConfigurations; + mShape = state.mShape; + mGradient = state.mGradient; + mOrientation = state.mOrientation; + mColors = state.mColors.clone(); + mPositions = state.mPositions.clone(); + mHasSolidColor = state.mHasSolidColor; + mStrokeWidth = state.mStrokeWidth; + mStrokeColor = state.mStrokeColor; + mStrokeDashWidth = state.mStrokeDashWidth; + mStrokeDashGap = state.mStrokeDashGap; + mRadius = state.mRadius; + mRadiusArray = state.mRadiusArray.clone(); + mPadding = new Rect(state.mPadding); + mWidth = state.mWidth; + mHeight = state.mHeight; + mInnerRadius = state.mInnerRadius; + mThickness = state.mThickness; + mCenterX = state.mCenterX; + mCenterY = state.mCenterY; + mGradientRadius = state.mGradientRadius; + mUseLevel = state.mUseLevel; + mUseLevelForShape = state.mUseLevelForShape; + } + @Override public Drawable newDrawable() { return new GradientDrawable(this); @@ -895,6 +932,11 @@ public class GradientDrawable extends Drawable { private GradientDrawable(GradientState state) { mGradientState = state; + initializeWithState(state); + mRectIsDirty = true; + } + + private void initializeWithState(GradientState state) { if (state.mHasSolidColor) { mFillPaint.setColor(state.mSolidColor); } @@ -906,12 +948,11 @@ public class GradientDrawable extends Drawable { mStrokePaint.setColor(state.mStrokeColor); if (state.mStrokeDashWidth != 0.0f) { - DashPathEffect e = new DashPathEffect(new float[] { - state.mStrokeDashWidth, state.mStrokeDashGap}, 0); + DashPathEffect e = new DashPathEffect( + new float[] { state.mStrokeDashWidth, state.mStrokeDashGap }, 0); mStrokePaint.setPathEffect(e); } } - mRectIsDirty = true; } } diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java index 80b8e96e46d9a..604772602ae16 100644 --- a/graphics/java/android/graphics/drawable/InsetDrawable.java +++ b/graphics/java/android/graphics/drawable/InsetDrawable.java @@ -44,6 +44,9 @@ import java.io.IOException; public class InsetDrawable extends Drawable implements Drawable.Callback { // Most of this is copied from ScaleDrawable. + private InsetState mInsetState; + private final Rect mTmpRect = new Rect(); + private boolean mMutated; /*package*/ InsetDrawable() { this(null); @@ -239,7 +242,27 @@ public class InsetDrawable extends Drawable implements Drawable.Callback return null; } + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mInsetState.mDrawable.mutate(); + mMutated = true; + } + return this; + } + final static class InsetState extends ConstantState { + Drawable mDrawable; + int mChangingConfigurations; + + int mInsetLeft; + int mInsetTop; + int mInsetRight; + int mInsetBottom; + + boolean mCheckedConstantState; + boolean mCanConstantState; + InsetState(InsetState orig, InsetDrawable owner) { if (orig != null) { mDrawable = orig.mDrawable.getConstantState().newDrawable(); @@ -270,25 +293,10 @@ public class InsetDrawable extends Drawable implements Drawable.Callback return mCanConstantState; } - - Drawable mDrawable; - int mChangingConfigurations; - - int mInsetLeft; - int mInsetTop; - int mInsetRight; - int mInsetBottom; - - boolean mCheckedConstantState; - boolean mCanConstantState; } private InsetDrawable(InsetState state) { - InsetState as = new InsetState(state, this); - mInsetState = as; + mInsetState = new InsetState(state, this); } - - private InsetState mInsetState; - private final Rect mTmpRect = new Rect(); } diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 59dfbd46c7581..c7772053f98cd 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -51,6 +51,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { private int[] mPaddingB; private final Rect mTmpRect = new Rect(); + private boolean mMutated; /** * Create a new layer drawable with the list of specified layers. @@ -71,16 +72,16 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { LayerDrawable(Drawable[] layers, LayerState state) { this(state); int length = layers.length; - Rec[] r = new Rec[length]; + ChildDrawable[] r = new ChildDrawable[length]; for (int i = 0; i < length; i++) { - r[i] = new Rec(); + r[i] = new ChildDrawable(); r[i].mDrawable = layers[i]; layers[i].setCallback(this); mLayerState.mChildrenChangingConfigurations |= layers[i].getChangingConfigurations(); } mLayerState.mNum = length; - mLayerState.mArray = r; + mLayerState.mChildren = r; ensurePadding(); } @@ -171,26 +172,26 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { */ private void addLayer(Drawable layer, int id, int left, int top, int right, int bottom) { final LayerState st = mLayerState; - int N = st.mArray != null ? st.mArray.length : 0; + int N = st.mChildren != null ? st.mChildren.length : 0; int i = st.mNum; if (i >= N) { - Rec[] nu = new Rec[N + 10]; + ChildDrawable[] nu = new ChildDrawable[N + 10]; if (i > 0) { - System.arraycopy(st.mArray, 0, nu, 0, i); + System.arraycopy(st.mChildren, 0, nu, 0, i); } - st.mArray = nu; + st.mChildren = nu; } mLayerState.mChildrenChangingConfigurations |= layer.getChangingConfigurations(); - Rec rec = new Rec(); - st.mArray[i] = rec; - rec.mId = id; - rec.mDrawable = layer; - rec.mInsetL = left; - rec.mInsetT = top; - rec.mInsetR = right; - rec.mInsetB = bottom; + ChildDrawable childDrawable = new ChildDrawable(); + st.mChildren[i] = childDrawable; + childDrawable.mId = id; + childDrawable.mDrawable = layer; + childDrawable.mInsetL = left; + childDrawable.mInsetT = top; + childDrawable.mInsetR = right; + childDrawable.mInsetB = bottom; st.mNum++; layer.setCallback(this); @@ -203,7 +204,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { * @return The {@link Drawable} of the layer that has the given id in the hierarchy or null. */ public Drawable findDrawableByLayerId(int id) { - final Rec[] layers = mLayerState.mArray; + final ChildDrawable[] layers = mLayerState.mChildren; for (int i = mLayerState.mNum - 1; i >= 0; i--) { if (layers[i].mId == id) { @@ -221,7 +222,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { * @param id The ID to assign to the layer. */ public void setId(int index, int id) { - mLayerState.mArray[index].mId = id; + mLayerState.mChildren[index].mId = id; } /** @@ -240,7 +241,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { * @return The {@link android.graphics.drawable.Drawable} at the specified layer index. */ public Drawable getDrawable(int index) { - return mLayerState.mArray[index].mDrawable; + return mLayerState.mChildren[index].mDrawable; } /** @@ -251,7 +252,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { * @return The id of the layer or {@link android.view.View#NO_ID} if the layer has no id. */ public int getId(int index) { - return mLayerState.mArray[index].mId; + return mLayerState.mChildren[index].mId; } /** @@ -263,7 +264,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { * the id was not found). */ public boolean setDrawableByLayerId(int id, Drawable drawable) { - final Rec[] layers = mLayerState.mArray; + final ChildDrawable[] layers = mLayerState.mChildren; for (int i = mLayerState.mNum - 1; i >= 0; i--) { if (layers[i].mId == id) { @@ -282,11 +283,11 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { bottom -= b; */ public void setLayerInset(int index, int l, int t, int r, int b) { - Rec rec = mLayerState.mArray[index]; - rec.mInsetL = l; - rec.mInsetT = t; - rec.mInsetR = r; - rec.mInsetB = b; + ChildDrawable childDrawable = mLayerState.mChildren[index]; + childDrawable.mInsetL = l; + childDrawable.mInsetT = t; + childDrawable.mInsetR = r; + childDrawable.mInsetB = b; } // overrides from Drawable.Callback @@ -313,7 +314,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @Override public void draw(Canvas canvas) { - final Rec[] array = mLayerState.mArray; + final ChildDrawable[] array = mLayerState.mChildren; final int N = mLayerState.mNum; for (int i=0; i width) { @@ -478,11 +479,11 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @Override public int getIntrinsicHeight() { int height = -1; - final Rec[] array = mLayerState.mArray; + final ChildDrawable[] array = mLayerState.mChildren; final int N = mLayerState.mNum; int padT=0, padB=0; for (int i=0; i height) { height = h; @@ -493,7 +494,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { return height; } - private boolean reapplyPadding(int i, Rec r) { + private boolean reapplyPadding(int i, ChildDrawable r) { final Rect rect = mTmpRect; r.mDrawable.getPadding(rect); if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] || @@ -527,7 +528,20 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { return null; } - static class Rec { + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + final ChildDrawable[] array = mLayerState.mChildren; + final int N = mLayerState.mNum; + for (int i = 0; i < N; i++) { + array[i].mDrawable.mutate(); + } + mMutated = true; + } + return this; + } + + static class ChildDrawable { public Drawable mDrawable; public int mInsetL, mInsetT, mInsetR, mInsetB; public int mId; @@ -535,7 +549,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { static class LayerState extends ConstantState { int mNum; - Rec[] mArray; + ChildDrawable[] mChildren; int mChangingConfigurations; int mChildrenChangingConfigurations; @@ -551,18 +565,18 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { LayerState(LayerState orig, LayerDrawable owner) { if (orig != null) { - final Rec[] origRec = orig.mArray; + final ChildDrawable[] origChildDrawable = orig.mChildren; final int N = orig.mNum; mNum = N; - mArray = new Rec[N]; + mChildren = new ChildDrawable[N]; mChangingConfigurations = orig.mChangingConfigurations; mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations; for (int i = 0; i < N; i++) { - final Rec r = mArray[i] = new Rec(); - final Rec or = origRec[i]; + final ChildDrawable r = mChildren[i] = new ChildDrawable(); + final ChildDrawable or = origChildDrawable[i]; r.mDrawable = or.mDrawable.getConstantState().newDrawable(); r.mDrawable.setCallback(owner); r.mInsetL = or.mInsetL; @@ -579,7 +593,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { mCheckedConstantState = mCanConstantState = true; } else { mNum = 0; - mArray = null; + mChildren = null; } } @@ -599,11 +613,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { } final int N = mNum; - int op = N > 0 ? mArray[0].mDrawable.getOpacity() - : PixelFormat.TRANSPARENT; + int op = N > 0 ? mChildren[0].mDrawable.getOpacity() : PixelFormat.TRANSPARENT; for (int i = 1; i < N; i++) { - op = Drawable.resolveOpacity(op, mArray[i].mDrawable - .getOpacity()); + op = Drawable.resolveOpacity(op, mChildren[i].mDrawable.getOpacity()); } mOpacity = op; mHaveOpacity = true; @@ -618,7 +630,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { boolean stateful = false; final int N = mNum; for (int i = 0; i < N; i++) { - if (mArray[i].mDrawable.isStateful()) { + if (mChildren[i].mDrawable.isStateful()) { stateful = true; break; } @@ -630,11 +642,11 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { } public synchronized boolean canConstantState() { - if (!mCheckedConstantState && mArray != null) { + if (!mCheckedConstantState && mChildren != null) { mCanConstantState = true; final int N = mNum; for (int i=0; i * It can be defined in an XML file with the <level-list> element. * Each Drawable level is defined in a nested <item> *

      + * * @attr ref android.R.styleable#LevelListDrawableItem_minLevel * @attr ref android.R.styleable#LevelListDrawableItem_maxLevel * @attr ref android.R.styleable#LevelListDrawableItem_drawable */ public class LevelListDrawable extends DrawableContainer { - public LevelListDrawable() - { + private final LevelListState mLevelListState; + private boolean mMutated; + + public LevelListDrawable() { this(null); } @@ -54,7 +56,7 @@ public class LevelListDrawable extends DrawableContainer { onLevelChange(getLevel()); } } - + // overrides from Drawable @Override @@ -65,21 +67,22 @@ public class LevelListDrawable extends DrawableContainer { } return super.onLevelChange(level); } - - @Override public void inflate(Resources r, XmlPullParser parser, - AttributeSet attrs) - throws XmlPullParserException, IOException { + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs); - + int type; int low = 0; - final int innerDepth = parser.getDepth()+1; + final int innerDepth = parser.getDepth() + 1; int depth; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth=parser.getDepth()) >= innerDepth - || type != XmlPullParser.END_TAG)) { + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { if (type != XmlPullParser.START_TAG) { continue; } @@ -87,50 +90,60 @@ public class LevelListDrawable extends DrawableContainer { if (depth > innerDepth || !parser.getName().equals("item")) { continue; } - + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.LevelListDrawableItem); - + low = a.getInt( com.android.internal.R.styleable.LevelListDrawableItem_minLevel, 0); int high = a.getInt( com.android.internal.R.styleable.LevelListDrawableItem_maxLevel, 0); int drawableRes = a.getResourceId( com.android.internal.R.styleable.LevelListDrawableItem_drawable, 0); - + a.recycle(); - + if (high < 0) { throw new XmlPullParserException(parser.getPositionDescription() - + ": tag requires a 'maxLevel' attribute"); + + ": tag requires a 'maxLevel' attribute"); } - + Drawable dr; if (drawableRes != 0) { dr = r.getDrawable(drawableRes); } else { - while ((type=parser.next()) == XmlPullParser.TEXT) { + while ((type = parser.next()) == XmlPullParser.TEXT) { } if (type != XmlPullParser.START_TAG) { throw new XmlPullParserException( parser.getPositionDescription() - + ": tag requires a 'drawable' attribute or " - + "child tag defining a drawable"); + + ": tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); } dr = Drawable.createFromXmlInner(r, parser, attrs); } mLevelListState.addLevel(low, high, dr); - low = high+1; } onLevelChange(getLevel()); } - private final static class LevelListState extends DrawableContainerState - { - LevelListState(LevelListState orig, LevelListDrawable owner) - { + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mLevelListState.mLows = mLevelListState.mLows.clone(); + mLevelListState.mHighs = mLevelListState.mHighs.clone(); + mMutated = true; + } + return this; + } + + private final static class LevelListState extends DrawableContainerState { + private int[] mLows; + private int[] mHighs; + + LevelListState(LevelListState orig, LevelListDrawable owner) { super(orig, owner); if (orig != null) { @@ -142,19 +155,17 @@ public class LevelListDrawable extends DrawableContainer { } } - public void addLevel(int low, int high, Drawable drawable) - { + public void addLevel(int low, int high, Drawable drawable) { int pos = addChild(drawable); mLows[pos] = low; mHighs[pos] = high; } - public int indexOfLevel(int level) - { + public int indexOfLevel(int level) { final int[] lows = mLows; final int[] highs = mHighs; final int N = getChildCount(); - for (int i=0; i= lows[i] && level <= highs[i]) { return i; } @@ -163,14 +174,12 @@ public class LevelListDrawable extends DrawableContainer { } @Override - public Drawable newDrawable() - { + public Drawable newDrawable() { return new LevelListDrawable(this); } @Override - public void growArray(int oldSize, int newSize) - { + public void growArray(int oldSize, int newSize) { super.growArray(oldSize, newSize); int[] newInts = new int[newSize]; System.arraycopy(mLows, 0, newInts, 0, oldSize); @@ -179,19 +188,13 @@ public class LevelListDrawable extends DrawableContainer { System.arraycopy(mHighs, 0, newInts, 0, oldSize); mHighs = newInts; } - - private int[] mLows; - private int[] mHighs; } - private LevelListDrawable(LevelListState state) - { + private LevelListDrawable(LevelListState state) { LevelListState as = new LevelListState(state, this); mLevelListState = as; setConstantState(as); onLevelChange(getLevel()); } - - private final LevelListState mLevelListState; } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index c98ef5ba75215..3ded37b8a80e3 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -26,9 +26,13 @@ import android.graphics.*; * */ public class NinePatchDrawable extends Drawable { + private NinePatchState mNinePatchState; + private NinePatch mNinePatch; + private Rect mPadding; + private Paint mPaint; + private boolean mMutated; - public NinePatchDrawable(Bitmap bitmap, byte[] chunk, - Rect padding, String srcName) { + public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) { this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding)); } @@ -45,8 +49,7 @@ public class NinePatchDrawable extends Drawable { @Override public int getChangingConfigurations() { - return super.getChangingConfigurations() - | mNinePatchState.mChangingConfigurations; + return super.getChangingConfigurations() | mNinePatchState.mChangingConfigurations; } @Override @@ -104,12 +107,13 @@ public class NinePatchDrawable extends Drawable { } /** - * Returns a {@link android.graphics.PixelFormat graphics.PixelFormat} value of OPAQUE or TRANSLUCENT. + * Returns a {@link android.graphics.PixelFormat graphics.PixelFormat} + * value of OPAQUE or TRANSLUCENT. */ @Override public int getOpacity() { - return mNinePatch.hasAlpha() || (mPaint != null && mPaint.getAlpha() < 255) - ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; + return mNinePatch.hasAlpha() || (mPaint != null && mPaint.getAlpha() < 255) ? + PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; } @Override @@ -123,16 +127,35 @@ public class NinePatchDrawable extends Drawable { return mNinePatchState; } + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mNinePatchState = new NinePatchState(mNinePatchState); + mNinePatch = mNinePatchState.mNinePatch; + mPadding = mNinePatchState.mPadding; + mMutated = true; + } + return this; + } + final static class NinePatchState extends ConstantState { - NinePatchState(NinePatch ninePatch, Rect padding) - { + final NinePatch mNinePatch; + final Rect mPadding; + int mChangingConfigurations; + + NinePatchState(NinePatch ninePatch, Rect padding) { mNinePatch = ninePatch; mPadding = padding; } + NinePatchState(NinePatchState state) { + mNinePatch = new NinePatch(state.mNinePatch); + mPadding = new Rect(state.mPadding); + mChangingConfigurations = state.mChangingConfigurations; + } + @Override - public Drawable newDrawable() - { + public Drawable newDrawable() { return new NinePatchDrawable(this); } @@ -140,10 +163,6 @@ public class NinePatchDrawable extends Drawable { public int getChangingConfigurations() { return mChangingConfigurations; } - - final NinePatch mNinePatch; - final Rect mPadding; - int mChangingConfigurations; } private NinePatchDrawable(NinePatchState state) { @@ -151,10 +170,5 @@ public class NinePatchDrawable extends Drawable { mNinePatch = state.mNinePatch; mPadding = state.mPadding; } - - private final NinePatchState mNinePatchState; - private final NinePatch mNinePatch; - private final Rect mPadding; - private Paint mPaint; } diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java index 3e03ed4c90512..e4b821a905c0c 100644 --- a/graphics/java/android/graphics/drawable/RotateDrawable.java +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -45,6 +45,10 @@ import java.io.IOException; * @attr ref android.R.styleable#RotateDrawable_drawable */ public class RotateDrawable extends Drawable implements Drawable.Callback { + private static final float MAX_LEVEL = 10000.0f; + + private RotateState mState; + private boolean mMutated; /** *

      Create a new rotating drawable with an empty state.

      @@ -248,6 +252,15 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { } } + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mState.mDrawable.mutate(); + mMutated = true; + } + return this; + } + /** *

      Represents the state of a rotation for a given drawable. The same * rotate drawable can be invoked with different states to drive several @@ -268,6 +281,9 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { float mCurrentDegrees; + private boolean mCanConstantState; + private boolean mCheckedConstantState; + public RotateState(RotateState source, RotateDrawable owner) { if (source != null) { mDrawable = source.mDrawable.getConstantState().newDrawable(); @@ -300,12 +316,5 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { return mCanConstantState; } - - private boolean mCanConstantState; - private boolean mCheckedConstantState; } - - private static final float MAX_LEVEL = 10000.0f; - - private RotateState mState; } diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java index c40c8c0681642..b3322c9634bb9 100644 --- a/graphics/java/android/graphics/drawable/ScaleDrawable.java +++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java @@ -24,7 +24,6 @@ import android.content.res.TypedArray; import android.graphics.*; import android.view.Gravity; import android.util.AttributeSet; -import android.util.Log; import java.io.IOException; @@ -44,6 +43,7 @@ import java.io.IOException; */ public class ScaleDrawable extends Drawable implements Drawable.Callback { private ScaleState mScaleState; + private boolean mMutated; private final Rect mTmpRect = new Rect(); ScaleDrawable() { @@ -234,7 +234,25 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { return null; } + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mScaleState.mDrawable.mutate(); + mMutated = true; + } + return this; + } + final static class ScaleState extends ConstantState { + Drawable mDrawable; + int mChangingConfigurations; + float mScaleWidth; + float mScaleHeight; + int mGravity; + + private boolean mCheckedConstantState; + private boolean mCanConstantState; + ScaleState(ScaleState orig, ScaleDrawable owner) { if (orig != null) { mDrawable = orig.mDrawable.getConstantState().newDrawable(); @@ -264,15 +282,6 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { return mCanConstantState; } - - Drawable mDrawable; - int mChangingConfigurations; - float mScaleWidth; - float mScaleHeight; - int mGravity; - - private boolean mCheckedConstantState; - private boolean mCanConstantState; } private ScaleDrawable(ScaleState state) { diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java index 7e1284f7309b1..d24194f545f12 100644 --- a/graphics/java/android/graphics/drawable/ShapeDrawable.java +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -28,15 +28,24 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; /** - * An object that draws primitive shapes. + * A Drawable object that draws primitive shapes. * A ShapeDrawable takes a {@link android.graphics.drawable.shapes.Shape} * object and manages its presence on the screen. If no Shape is given, then * the ShapeDrawable will default to a * {@link android.graphics.drawable.shapes.RectShape}. + * + * @attr ref android.R.styleable#ShapeDrawablePadding_left + * @attr ref android.R.styleable#ShapeDrawablePadding_top + * @attr ref android.R.styleable#ShapeDrawablePadding_right + * @attr ref android.R.styleable#ShapeDrawablePadding_bottom + * @attr ref android.R.styleable#ShapeDrawable_color + * @attr ref android.R.styleable#ShapeDrawable_width + * @attr ref android.R.styleable#ShapeDrawable_height */ public class ShapeDrawable extends Drawable { private ShapeState mShapeState; - + private boolean mMutated; + /** * ShapeDrawable constructor. */ @@ -334,6 +343,21 @@ public class ShapeDrawable extends Drawable { return mShapeState; } + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mShapeState.mPaint = new Paint(mShapeState.mPaint); + mShapeState.mPadding = new Rect(mShapeState.mPadding); + try { + mShapeState.mShape = mShapeState.mShape.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + mMutated = true; + } + return this; + } + /** * Defines the intrinsic properties of this ShapeDrawable's Shape. */ diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java index 1dc162724fad8..475825c28a9a2 100644 --- a/graphics/java/android/graphics/drawable/StateListDrawable.java +++ b/graphics/java/android/graphics/drawable/StateListDrawable.java @@ -27,10 +27,9 @@ import android.util.AttributeSet; import android.util.StateSet; /** - * * Lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string * ID value. - * + *

      *

      It can be defined in an XML file with the <selector> element. * Each state Drawable is defined in a nested <item> element.

      * @@ -51,15 +50,18 @@ import android.util.StateSet; * @attr ref android.R.styleable#DrawableStates_state_pressed */ public class StateListDrawable extends DrawableContainer { - public StateListDrawable() - { + private final StateListState mStateListState; + private boolean mMutated; + + public StateListDrawable() { this(null); } /** * Add a new image/string ID to the set of images. + * * @param stateSet - An array of resource Ids to associate with the image. - * Switch to this image by calling setState(). + * Switch to this image by calling setState(). * @param drawable -The image to show. */ public void addState(int[] stateSet, Drawable drawable) { @@ -74,7 +76,7 @@ public class StateListDrawable extends DrawableContainer { public boolean isStateful() { return true; } - + @Override protected boolean onStateChange(int[] stateSet) { int idx = mStateListState.indexOfStateSet(stateSet); @@ -87,30 +89,31 @@ public class StateListDrawable extends DrawableContainer { return super.onStateChange(stateSet); } - @Override public void inflate(Resources r, XmlPullParser parser, + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) - throws XmlPullParserException, IOException { - + throws XmlPullParserException, IOException { + TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.StateListDrawable); super.inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.StateListDrawable_visible); - + mStateListState.setVariablePadding(a.getBoolean( com.android.internal.R.styleable.StateListDrawable_variablePadding, false)); mStateListState.setConstantSize(a.getBoolean( com.android.internal.R.styleable.StateListDrawable_constantSize, false)); - + a.recycle(); - + int type; - final int innerDepth = parser.getDepth()+1; + final int innerDepth = parser.getDepth() + 1; int depth; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth=parser.getDepth()) >= innerDepth - || type != XmlPullParser.END_TAG)) { + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth + || type != XmlPullParser.END_TAG)) { if (type != XmlPullParser.START_TAG) { continue; } @@ -118,9 +121,9 @@ public class StateListDrawable extends DrawableContainer { if (depth > innerDepth || !parser.getName().equals("item")) { continue; } - + int drawableRes = 0; - + int i; int j = 0; final int numAttrs = attrs.getAttributeCount(); @@ -132,27 +135,27 @@ public class StateListDrawable extends DrawableContainer { drawableRes = attrs.getAttributeResourceValue(i, 0); } else { states[j++] = attrs.getAttributeBooleanValue(i, false) - ? stateResId - : -stateResId; + ? stateResId + : -stateResId; } } states = StateSet.trimStateSet(states, j); - + Drawable dr; if (drawableRes != 0) { dr = r.getDrawable(drawableRes); } else { - while ((type=parser.next()) == XmlPullParser.TEXT) { + while ((type = parser.next()) == XmlPullParser.TEXT) { } if (type != XmlPullParser.START_TAG) { throw new XmlPullParserException( parser.getPositionDescription() - + ": tag requires a 'drawable' attribute or " - + "child tag defining a drawable"); + + ": tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); } dr = Drawable.createFromXmlInner(r, parser, attrs); } - + mStateListState.addStateSet(states, dr); } @@ -162,49 +165,63 @@ public class StateListDrawable extends DrawableContainer { StateListState getStateListState() { return mStateListState; } - + /** * Gets the number of states contained in this drawable. - * + * * @return The number of states contained in this drawable. + * @hide pending API council * @see #getStateSet(int) * @see #getStateDrawable(int) - * @hide pending API council */ public int getStateCount() { - return mStateListState.getChildCount(); + return mStateListState.getChildCount(); } /** * Gets the state set at an index. - * + * * @param index The index of the state set. * @return The state set at the index. + * @hide pending API council * @see #getStateCount() * @see #getStateDrawable(int) - * @hide pending API council */ public int[] getStateSet(int index) { return mStateListState.mStateSets[index]; } - + /** * Gets the drawable at an index. - * + * * @param index The index of the drawable. * @return The drawable at the index. + * @hide pending API council * @see #getStateCount() * @see #getStateSet(int) - * @hide pending API council */ public Drawable getStateDrawable(int index) { return mStateListState.getChildren()[index]; } - - static final class StateListState extends DrawableContainerState - { - StateListState(StateListState orig, StateListDrawable owner) - { + + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + final int[][] sets = mStateListState.mStateSets; + final int count = sets.length; + mStateListState.mStateSets = new int[count][]; + for (int i = 0; i < count; i++) { + mStateListState.mStateSets[i] = sets[i].clone(); + } + mMutated = true; + } + return this; + } + + static final class StateListState extends DrawableContainerState { + private int[][] mStateSets; + + StateListState(StateListState orig, StateListDrawable owner) { super(orig, owner); if (orig != null) { @@ -220,11 +237,10 @@ public class StateListDrawable extends DrawableContainer { return pos; } - private int indexOfStateSet(int[] stateSet) - { + private int indexOfStateSet(int[] stateSet) { final int[][] stateSets = mStateSets; final int N = getChildCount(); - for (int i=0; i mState.mDuration) { - if (mState.mAlpha == 0) { + if (time - mStartTimeMillis > mDuration) { + if (mAlpha == 0) { mFrom = 0; mTo = 255; - mState.mAlpha = 0; + mAlpha = 0; mReverse = false; } else { mFrom = 255; mTo = 0; - mState.mAlpha = 255; + mAlpha = 255; mReverse = true; } - mDuration = mState.mDuration = duration; + mDuration = mOriginalDuration = duration; mTransitionState = TRANSITION_STARTING; invalidateSelf(); return; } mReverse = !mReverse; - mFrom = mState.mAlpha; + mFrom = mAlpha; mTo = mReverse ? 0 : 255; mDuration = (int) (mReverse ? time - mStartTimeMillis : - mState.mDuration - (time - mStartTimeMillis)); + mOriginalDuration - (time - mStartTimeMillis)); mTransitionState = TRANSITION_STARTING; } @Override public void draw(Canvas canvas) { boolean done = true; - final TransitionState state = mState; switch (mTransitionState) { case TRANSITION_STARTING: @@ -177,14 +176,14 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba (SystemClock.uptimeMillis() - mStartTimeMillis) / mDuration; done = normalized >= 1.0f; normalized = Math.min(normalized, 1.0f); - state.mAlpha = (int) (mFrom + (mTo - mFrom) * normalized); + mAlpha = (int) (mFrom + (mTo - mFrom) * normalized); } break; } - final int alpha = state.mAlpha; - final boolean crossFade = state.mCrossFade; - final Rec[] array = mLayerState.mArray; + final int alpha = mAlpha; + final boolean crossFade = mCrossFade; + final ChildDrawable[] array = mLayerState.mChildren; Drawable d; d = array[0].mDrawable; @@ -217,7 +216,7 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba * @param enabled True to enable cross fading, false otherwise. */ public void setCrossFadeEnabled(boolean enabled) { - mState.mCrossFade = enabled; + mCrossFade = enabled; } /** @@ -226,14 +225,10 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba * @return True if cross fading is enabled, false otherwise. */ public boolean isCrossFadeEnabled() { - return mState.mCrossFade; + return mCrossFade; } static class TransitionState extends LayerState { - int mAlpha = 0; - int mDuration; - boolean mCrossFade; - TransitionState(TransitionState orig, TransitionDrawable owner) { super(orig, owner); } diff --git a/graphics/java/android/graphics/drawable/package.html b/graphics/java/android/graphics/drawable/package.html index 17ee865296dcf..da49df763bfaa 100644 --- a/graphics/java/android/graphics/drawable/package.html +++ b/graphics/java/android/graphics/drawable/package.html @@ -4,6 +4,6 @@ Provides classes to manage a variety of visual elements that are intended for display only, such as bitmaps and gradients. These elements are often used by widgets as background images or simply as indicators (for example, a volume level indicator). You can create most of these in XML as described in Resources. +href="{@docRoot}guide/topics/resources/available-resources.html">Availeble Resource Types. diff --git a/graphics/java/android/graphics/drawable/shapes/PathShape.java b/graphics/java/android/graphics/drawable/shapes/PathShape.java index 3656a0b9e676b..30b73479a98cb 100644 --- a/graphics/java/android/graphics/drawable/shapes/PathShape.java +++ b/graphics/java/android/graphics/drawable/shapes/PathShape.java @@ -64,5 +64,12 @@ public class PathShape extends Shape { mScaleX = width / mStdWidth; mScaleY = height / mStdHeight; } + + @Override + public PathShape clone() throws CloneNotSupportedException { + PathShape shape = (PathShape) super.clone(); + shape.mPath = new Path(mPath); + return shape; + } } diff --git a/graphics/java/android/graphics/drawable/shapes/RectShape.java b/graphics/java/android/graphics/drawable/shapes/RectShape.java index 4f038aef143fc..a3d2654b89306 100644 --- a/graphics/java/android/graphics/drawable/shapes/RectShape.java +++ b/graphics/java/android/graphics/drawable/shapes/RectShape.java @@ -50,4 +50,11 @@ public class RectShape extends Shape { protected final RectF rect() { return mRect; } + + @Override + public RectShape clone() throws CloneNotSupportedException { + final RectShape shape = (RectShape) super.clone(); + shape.mRect = new RectF(mRect); + return shape; + } } diff --git a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java index 54ef3f76f248f..f4cf15c746988 100644 --- a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java +++ b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java @@ -105,4 +105,15 @@ public class RoundRectShape extends RectShape { } } } + + @Override + public RoundRectShape clone() throws CloneNotSupportedException { + RoundRectShape shape = (RoundRectShape) super.clone(); + shape.mOuterRadii = mOuterRadii.clone(); + shape.mInnerRadii = mInnerRadii.clone(); + shape.mInset = new RectF(mInset); + shape.mInnerRect = new RectF(mInnerRect); + shape.mPath = new Path(mPath); + return shape; + } } diff --git a/graphics/java/android/graphics/drawable/shapes/Shape.java b/graphics/java/android/graphics/drawable/shapes/Shape.java index fc54bc185b3c8..4e192f95dac97 100644 --- a/graphics/java/android/graphics/drawable/shapes/Shape.java +++ b/graphics/java/android/graphics/drawable/shapes/Shape.java @@ -25,7 +25,7 @@ import android.graphics.Paint; * but more graphical control is available if you instead pass * it to a {@link android.graphics.drawable.ShapeDrawable}. */ -public abstract class Shape { +public abstract class Shape implements Cloneable { private float mWidth; private float mHeight; @@ -92,4 +92,9 @@ public abstract class Shape { * @param height the new height of the Shape */ protected void onResize(float width, float height) {} + + @Override + public Shape clone() throws CloneNotSupportedException { + return (Shape) super.clone(); + } } diff --git a/include/GLES/egl.h b/include/GLES/egl.h deleted file mode 100644 index 08834ab8b6d6a..0000000000000 --- a/include/GLES/egl.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_EGL_H -#define ANDROID_EGL_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define EGL_VERSION_1_0 1 -#define EGL_VERSION_1_1 1 -#define EGL_VERSION_1_2 1 - -#define EGL_FALSE 0 -#define EGL_TRUE 1 - -/* Errors */ -#define EGL_SUCCESS 0x3000 -#define EGL_NOT_INITIALIZED 0x3001 -#define EGL_BAD_ACCESS 0x3002 -#define EGL_BAD_ALLOC 0x3003 -#define EGL_BAD_ATTRIBUTE 0x3004 -#define EGL_BAD_CONFIG 0x3005 -#define EGL_BAD_CONTEXT 0x3006 -#define EGL_BAD_CURRENT_SURFACE 0x3007 -#define EGL_BAD_DISPLAY 0x3008 -#define EGL_BAD_MATCH 0x3009 -#define EGL_BAD_NATIVE_PIXMAP 0x300A -#define EGL_BAD_NATIVE_WINDOW 0x300B -#define EGL_BAD_PARAMETER 0x300C -#define EGL_BAD_SURFACE 0x300D -#define EGL_CONTEXT_LOST 0x300E - -/* Config attributes */ -#define EGL_BUFFER_SIZE 0x3020 -#define EGL_ALPHA_SIZE 0x3021 -#define EGL_BLUE_SIZE 0x3022 -#define EGL_GREEN_SIZE 0x3023 -#define EGL_RED_SIZE 0x3024 -#define EGL_DEPTH_SIZE 0x3025 -#define EGL_STENCIL_SIZE 0x3026 -#define EGL_CONFIG_CAVEAT 0x3027 -#define EGL_CONFIG_ID 0x3028 -#define EGL_LEVEL 0x3029 -#define EGL_MAX_PBUFFER_HEIGHT 0x302A -#define EGL_MAX_PBUFFER_PIXELS 0x302B -#define EGL_MAX_PBUFFER_WIDTH 0x302C -#define EGL_NATIVE_RENDERABLE 0x302D -#define EGL_NATIVE_VISUAL_ID 0x302E -#define EGL_NATIVE_VISUAL_TYPE 0x302F -#define EGL_SAMPLES 0x3031 -#define EGL_SAMPLE_BUFFERS 0x3032 -#define EGL_SURFACE_TYPE 0x3033 -#define EGL_TRANSPARENT_TYPE 0x3034 -#define EGL_TRANSPARENT_BLUE_VALUE 0x3035 -#define EGL_TRANSPARENT_GREEN_VALUE 0x3036 -#define EGL_TRANSPARENT_RED_VALUE 0x3037 -#define EGL_NONE 0x3038 -#define EGL_BIND_TO_TEXTURE_RGB 0x3039 -#define EGL_BIND_TO_TEXTURE_RGBA 0x303A -#define EGL_MIN_SWAP_INTERVAL 0x303B -#define EGL_MAX_SWAP_INTERVAL 0x303C -#define EGL_LUMINANCE_SIZE 0x303D -#define EGL_ALPHA_MASK_SIZE 0x303E -#define EGL_COLOR_BUFFER_TYPE 0x303F -#define EGL_RENDERABLE_TYPE 0x3040 - -/* Config values */ -#define EGL_DONT_CARE ((EGLint)-1) - -#define EGL_SLOW_CONFIG 0x3050 -#define EGL_NON_CONFORMANT_CONFIG 0x3051 -#define EGL_TRANSPARENT_RGB 0x3052 -#define EGL_NO_TEXTURE 0x305C -#define EGL_TEXTURE_RGB 0x305D -#define EGL_TEXTURE_RGBA 0x305E -#define EGL_TEXTURE_2D 0x305F -#define EGL_RGB_BUFFER 0x308E -#define EGL_LUMINANCE_BUFFER 0x308F - -/* Config attribute mask bits */ -#define EGL_PBUFFER_BIT 0x01 -#define EGL_PIXMAP_BIT 0x02 -#define EGL_WINDOW_BIT 0x04 -#define EGL_OPENGL_ES_BIT 0x01 -#define EGL_OPENVG_BIT 0x02 - -/* String names */ -#define EGL_VENDOR 0x3053 -#define EGL_VERSION 0x3054 -#define EGL_EXTENSIONS 0x3055 -#define EGL_CLIENT_APIS 0x308D - -/* Surface attributes */ -#define EGL_HEIGHT 0x3056 -#define EGL_WIDTH 0x3057 -#define EGL_LARGEST_PBUFFER 0x3058 -#define EGL_TEXTURE_FORMAT 0x3080 -#define EGL_TEXTURE_TARGET 0x3081 -#define EGL_MIPMAP_TEXTURE 0x3082 -#define EGL_MIPMAP_LEVEL 0x3083 -#define EGL_RENDER_BUFFER 0x3086 -#define EGL_COLORSPACE 0x3087 -#define EGL_ALPHA_FORMAT 0x3088 -#define EGL_HORIZONTAL_RESOLUTION 0x3090 -#define EGL_VERTICAL_RESOLUTION 0x3091 -#define EGL_PIXEL_ASPECT_RATIO 0x3092 -#define EGL_SWAP_BEHAVIOR 0x3093 - -#define EGL_BACK_BUFFER 0x3084 -#define EGL_SINGLE_BUFFER 0x3085 - -#define EGL_DISPLAY_SCALING 10000 - -#define EGL_UNKNOWN ((EGLint)-1) - -/* Back buffer swap behaviors */ -#define EGL_BUFFER_PRESERVED 0x3094 -#define EGL_BUFFER_DESTROYED 0x3095 - -/* CreatePbufferFromClientBuffer buffer types */ -#define EGL_OPENVG_IMAGE 0x3096 - -/* QueryContext targets */ -#define EGL_CONTEXT_CLIENT_TYPE 0x3097 - -/* BindAPI/QueryAPI targets */ -#define EGL_OPENGL_ES_API 0x30A0 -#define EGL_OPENVG_API 0x30A1 - -/* WaitNative engines */ -#define EGL_CORE_NATIVE_ENGINE 0x305B - -/* Current surfaces */ -#define EGL_DRAW 0x3059 -#define EGL_READ 0x305A - - -EGLDisplay eglGetDisplay(NativeDisplayType display); -EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor); -EGLBoolean eglTerminate(EGLDisplay dpy); - -EGLBoolean eglGetConfigs( EGLDisplay dpy, - EGLConfig *configs, - EGLint config_size, EGLint *num_config); - -EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, - EGLConfig *configs, EGLint config_size, - EGLint *num_config); - -EGLBoolean eglGetConfigAttrib( EGLDisplay dpy, EGLConfig config, - EGLint attribute, EGLint *value); - -EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, - NativeWindowType window, - const EGLint *attrib_list); - -EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, - NativePixmapType pixmap, - const EGLint *attrib_list); - -EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, - const EGLint *attrib_list); - -EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface); - -EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, - EGLint attribute, EGLint *value); - -EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, - EGLContext share_list, const EGLint *attrib_list); - -EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx); - -EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, - EGLSurface read, EGLContext ctx); - -EGLContext eglGetCurrentContext(void); -EGLSurface eglGetCurrentSurface(EGLint readdraw); -EGLDisplay eglGetCurrentDisplay(void); -EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, - EGLint attribute, EGLint *value); - -EGLBoolean eglWaitGL(void); -EGLBoolean eglWaitNative(EGLint engine); -EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw); -EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, - NativePixmapType target); - -EGLint eglGetError(void); -const char* eglQueryString(EGLDisplay dpy, EGLint name); -void (*eglGetProcAddress (const char *procname))(); - -/* ---------------------------------------------------------------------------- - * EGL 1.1 - * ---------------------------------------------------------------------------- - */ - -EGLBoolean eglSurfaceAttrib( - EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value); -EGLBoolean eglBindTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer); -EGLBoolean eglReleaseTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer); - -EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval); - -/* ---------------------------------------------------------------------------- - * EGL 1.2 - * ---------------------------------------------------------------------------- - */ - -EGLBoolean eglBindAPI(EGLenum api); -EGLenum eglQueryAPI(void); -EGLBoolean eglWaitClient(void); -EGLBoolean eglReleaseThread(void); -EGLSurface eglCreatePbufferFromClientBuffer( - EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, - EGLConfig config, const EGLint *attrib_list); - -/* ---------------------------------------------------------------------------- - * Android extentions - * ---------------------------------------------------------------------------- - */ - -EGLBoolean eglSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, - EGLint l, EGLint t, EGLint w, EGLint h); - -EGLBoolean eglCopyFrontToBackANDROID(EGLDisplay dpy, - EGLSurface surface, - EGLint l, EGLint t, EGLint w, EGLint h); - -const char* eglQueryStringConfigANDROID( - EGLDisplay dpy, EGLConfig config, EGLint name); - -void* eglGetRenderBufferAddressANDROID(EGLDisplay dpy, EGLSurface surface); - -EGLBoolean eglCopyBitsANDROID(EGLDisplay dpy, - NativeWindowType draw, EGLint x, EGLint y, - NativeWindowType read, - EGLint crop_x, EGLint crop_y, EGLint crop_w, EGLint crop_h, - EGLint flags); - - -#ifdef __cplusplus -} -#endif - - -#endif /*ANDROID_EGL_H*/ diff --git a/include/GLES/egltypes.h b/include/GLES/egltypes.h deleted file mode 100644 index 698239b210b08..0000000000000 --- a/include/GLES/egltypes.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_EGL_TYPES_H -#define ANDROID_EGL_TYPES_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned int EGLBoolean; -typedef int32_t EGLint; -typedef int EGLenum; -typedef void *EGLDisplay; -typedef void *EGLConfig; -typedef void *EGLSurface; -typedef void *EGLContext; -typedef void *EGLClientBuffer; - -#define EGL_DEFAULT_DISPLAY ((NativeDisplayType)0) - -#define EGL_NO_CONTEXT ((EGLContext)0) -#define EGL_NO_DISPLAY ((EGLDisplay)0) -#define EGL_NO_SURFACE ((EGLSurface)0) - - -#ifdef __cplusplus -} -#endif - - -#endif /* ANDROID_EGL_TYPES_H */ diff --git a/include/GLES/gl.h b/include/GLES/gl.h deleted file mode 100644 index 50b6ac43f88b6..0000000000000 --- a/include/GLES/gl.h +++ /dev/null @@ -1,639 +0,0 @@ -/* - * 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. - */ - -#ifndef __gl_h_ -#define __gl_h_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/*****************************************************************************/ - -typedef int8_t GLbyte; // b -typedef int16_t GLshort; // s -typedef int32_t GLint; // i -typedef ssize_t GLsizei; // i -typedef int32_t GLfixed; // x -typedef int32_t GLclampx; // x -typedef float GLfloat; // f -typedef float GLclampf; // f -typedef uint8_t GLubyte; // ub -typedef uint8_t GLboolean; // ub -typedef uint16_t GLushort; // us -typedef uint32_t GLuint; // ui -typedef unsigned int GLenum; // ui -typedef unsigned int GLbitfield; // ui -typedef void GLvoid; -typedef intptr_t GLintptr; -typedef int GLsizeiptr; -typedef GLintptr GLintptrARB; -typedef GLsizeiptr GLsizeiptrARB; - -/*****************************************************************************/ - -#define GL_VERSION_ES_CM_1_0 1 -#define GL_VERSION_ES_CL_1_0 1 -#define GL_VERSION_ES_CM_1_1 1 -#define GL_VERSION_ES_CL_1_1 1 - -#define GL_OES_byte_coordinates 1 -#define GL_OES_fixed_point 1 -#define GL_OES_single_precision 1 -#define GL_OES_read_format 1 -#define GL_OES_compressed_paletted_texture 1 -#define GL_OES_draw_texture 1 -#define GL_OES_matrix_get 1 -#define GL_OES_query_matrix 1 -#define GL_OES_vertex_buffer_object 1 -#define GL_OES_point_size_array 1 -#define GL_OES_point_sprite 1 -#define GL_ARB_texture_non_power_of_two 1 - -/*****************************************************************************/ -/* OpenGL ES 1.0 names */ - -#define GL_FALSE 0 -#define GL_TRUE 1 - -/* begin mode */ -#define GL_POINTS 0x0000 -#define GL_LINES 0x0001 -#define GL_LINE_LOOP 0x0002 -#define GL_LINE_STRIP 0x0003 -#define GL_TRIANGLES 0x0004 -#define GL_TRIANGLE_STRIP 0x0005 -#define GL_TRIANGLE_FAN 0x0006 - -/* clear mask */ -#define GL_DEPTH_BUFFER_BIT 0x00000100 -#define GL_STENCIL_BUFFER_BIT 0x00000400 -#define GL_COLOR_BUFFER_BIT 0x00004000 - -/* enable/disable */ -#define GL_FOG 0x0B60 -#define GL_LIGHTING 0x0B50 -#define GL_TEXTURE_2D 0x0DE1 -#define GL_CULL_FACE 0x0B44 -#define GL_ALPHA_TEST 0x0BC0 -#define GL_BLEND 0x0BE2 -#define GL_COLOR_LOGIC_OP 0x0BF2 -#define GL_DITHER 0x0BD0 -#define GL_STENCIL_TEST 0x0B90 -#define GL_DEPTH_TEST 0x0B71 -#define GL_POINT_SMOOTH 0x0B10 -#define GL_LINE_SMOOTH 0x0B20 -#define GL_SCISSOR_TEST 0x0C11 -#define GL_COLOR_MATERIAL 0x0B57 -#define GL_NORMALIZE 0x0BA1 -#define GL_RESCALE_NORMAL 0x803A -#define GL_POLYGON_OFFSET_FILL 0x8037 -#define GL_VERTEX_ARRAY 0x8074 -#define GL_NORMAL_ARRAY 0x8075 -#define GL_COLOR_ARRAY 0x8076 -#define GL_TEXTURE_COORD_ARRAY 0x8078 -#define GL_MULTISAMPLE 0x809D -#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E -#define GL_SAMPLE_ALPHA_TO_ONE 0x809F -#define GL_SAMPLE_COVERAGE 0x80A0 - -/* gets */ -#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 -#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 -#define GL_ALIASED_POINT_SIZE_RANGE 0x846D -#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E -#define GL_MAX_LIGHTS 0x0D31 -#define GL_MAX_CLIP_PLANES 0x0D32 -#define GL_MAX_TEXTURE_SIZE 0x0D33 -#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 -#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 -#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 -#define GL_MAX_VIEWPORT_DIMS 0x0D3A -#define GL_MAX_ELEMENTS_VERTICES 0x80E8 -#define GL_MAX_ELEMENTS_INDICES 0x80E9 -#define GL_MAX_TEXTURE_UNITS 0x84E2 -#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 -#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 -#define GL_SUBPIXEL_BITS 0x0D50 -#define GL_RED_BITS 0x0D52 -#define GL_GREEN_BITS 0x0D53 -#define GL_BLUE_BITS 0x0D54 -#define GL_ALPHA_BITS 0x0D55 -#define GL_DEPTH_BITS 0x0D56 -#define GL_STENCIL_BITS 0x0D57 - -/* clip planes */ -#define GL_CLIP_PLANE0 0x3000 -#define GL_CLIP_PLANE1 0x3001 -#define GL_CLIP_PLANE2 0x3002 -#define GL_CLIP_PLANE3 0x3003 -#define GL_CLIP_PLANE4 0x3004 -#define GL_CLIP_PLANE5 0x3005 - -/* errors */ -#define GL_NO_ERROR 0 -#define GL_INVALID_ENUM 0x0500 -#define GL_INVALID_VALUE 0x0501 -#define GL_INVALID_OPERATION 0x0502 -#define GL_STACK_OVERFLOW 0x0503 -#define GL_STACK_UNDERFLOW 0x0504 -#define GL_OUT_OF_MEMORY 0x0505 - -/* fog */ -#define GL_EXP 0x0800 -#define GL_EXP2 0x0801 -#define GL_FOG_DENSITY 0x0B62 -#define GL_FOG_START 0x0B63 -#define GL_FOG_END 0x0B64 -#define GL_FOG_MODE 0x0B65 -#define GL_FOG_COLOR 0x0B66 - -/* culling */ -#define GL_CW 0x0900 -#define GL_CCW 0x0901 - -#define GL_FRONT 0x0404 -#define GL_BACK 0x0405 -#define GL_FRONT_AND_BACK 0x0408 - -/* hints */ -#define GL_DONT_CARE 0x1100 -#define GL_FASTEST 0x1101 -#define GL_NICEST 0x1102 - -#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 -#define GL_POINT_SMOOTH_HINT 0x0C51 -#define GL_LINE_SMOOTH_HINT 0x0C52 -#define GL_POLYGON_SMOOTH_HINT 0x0C53 -#define GL_FOG_HINT 0x0C54 -#define GL_GENERATE_MIPMAP_HINT 0x8192 - -/* lights */ -#define GL_LIGHT_MODEL_AMBIENT 0x0B53 -#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 - -#define GL_AMBIENT 0x1200 -#define GL_DIFFUSE 0x1201 -#define GL_SPECULAR 0x1202 -#define GL_POSITION 0x1203 -#define GL_SPOT_DIRECTION 0x1204 -#define GL_SPOT_EXPONENT 0x1205 -#define GL_SPOT_CUTOFF 0x1206 -#define GL_CONSTANT_ATTENUATION 0x1207 -#define GL_LINEAR_ATTENUATION 0x1208 -#define GL_QUADRATIC_ATTENUATION 0x1209 - -#define GL_LIGHT0 0x4000 -#define GL_LIGHT1 0x4001 -#define GL_LIGHT2 0x4002 -#define GL_LIGHT3 0x4003 -#define GL_LIGHT4 0x4004 -#define GL_LIGHT5 0x4005 -#define GL_LIGHT6 0x4006 -#define GL_LIGHT7 0x4007 - -/* material */ -#define GL_EMISSION 0x1600 -#define GL_SHININESS 0x1601 -#define GL_AMBIENT_AND_DIFFUSE 0x1602 - -/* matrix */ -#define GL_MODELVIEW 0x1700 -#define GL_PROJECTION 0x1701 -#define GL_TEXTURE 0x1702 - -/* types */ -#define GL_BYTE 0x1400 -#define GL_UNSIGNED_BYTE 0x1401 -#define GL_SHORT 0x1402 -#define GL_UNSIGNED_SHORT 0x1403 -#define GL_FLOAT 0x1406 -#define GL_FIXED 0x140C - -/* pixel formats */ -#define GL_ALPHA 0x1906 -#define GL_RGB 0x1907 -#define GL_RGBA 0x1908 -#define GL_LUMINANCE 0x1909 -#define GL_LUMINANCE_ALPHA 0x190A - -/* pixel store */ -#define GL_UNPACK_ALIGNMENT 0x0CF5 -#define GL_PACK_ALIGNMENT 0x0D05 - -/* pixel types */ -#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 -#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 -#define GL_UNSIGNED_SHORT_5_6_5 0x8363 - -/* logic op */ -#define GL_CLEAR 0x1500 // 0 -#define GL_AND 0x1501 // s & d -#define GL_AND_REVERSE 0x1502 // s & ~d -#define GL_COPY 0x1503 // s -#define GL_AND_INVERTED 0x1504 // ~s & d -#define GL_NOOP 0x1505 // d -#define GL_XOR 0x1506 // s ^ d -#define GL_OR 0x1507 // s | d -#define GL_NOR 0x1508 // ~(s | d) -#define GL_EQUIV 0x1509 // ~(s ^ d) -#define GL_INVERT 0x150A // ~d -#define GL_OR_REVERSE 0x150B // s | ~d -#define GL_COPY_INVERTED 0x150C // ~s -#define GL_OR_INVERTED 0x150D // ~s | d -#define GL_NAND 0x150E // ~(s & d) -#define GL_SET 0x150F // 1 - -/* shade model */ -#define GL_FLAT 0x1D00 -#define GL_SMOOTH 0x1D01 - -/* strings */ -#define GL_VENDOR 0x1F00 -#define GL_RENDERER 0x1F01 -#define GL_VERSION 0x1F02 -#define GL_EXTENSIONS 0x1F03 - -/* stencil */ -#define GL_KEEP 0x1E00 -#define GL_REPLACE 0x1E01 -#define GL_INCR 0x1E02 -#define GL_DECR 0x1E03 - -/* alpha & stencil */ -#define GL_NEVER 0x0200 -#define GL_LESS 0x0201 -#define GL_EQUAL 0x0202 -#define GL_LEQUAL 0x0203 -#define GL_GREATER 0x0204 -#define GL_NOTEQUAL 0x0205 -#define GL_GEQUAL 0x0206 -#define GL_ALWAYS 0x0207 - -/* blending equation & function */ -#define GL_ZERO 0 // SD -#define GL_ONE 1 // SD -#define GL_SRC_COLOR 0x0300 // D -#define GL_ONE_MINUS_SRC_COLOR 0x0301 // D -#define GL_SRC_ALPHA 0x0302 // SD -#define GL_ONE_MINUS_SRC_ALPHA 0x0303 // SD -#define GL_DST_ALPHA 0x0304 // SD -#define GL_ONE_MINUS_DST_ALPHA 0x0305 // SD -#define GL_DST_COLOR 0x0306 // S -#define GL_ONE_MINUS_DST_COLOR 0x0307 // S -#define GL_SRC_ALPHA_SATURATE 0x0308 // S - -/* Texture parameter name */ -#define GL_TEXTURE_MIN_FILTER 0x2801 -#define GL_TEXTURE_MAG_FILTER 0x2800 -#define GL_TEXTURE_WRAP_S 0x2802 -#define GL_TEXTURE_WRAP_T 0x2803 -#define GL_GENERATE_MIPMAP 0x8191 -#define GL_TEXTURE_CROP_RECT_OES 0x8B9D - -/* Texture Filter */ -#define GL_NEAREST 0x2600 -#define GL_LINEAR 0x2601 -#define GL_NEAREST_MIPMAP_NEAREST 0x2700 -#define GL_LINEAR_MIPMAP_NEAREST 0x2701 -#define GL_NEAREST_MIPMAP_LINEAR 0x2702 -#define GL_LINEAR_MIPMAP_LINEAR 0x2703 - -/* Texture Wrap Mode */ -#define GL_CLAMP 0x2900 -#define GL_REPEAT 0x2901 -#define GL_CLAMP_TO_EDGE 0x812F - -/* Texture Env Mode */ -#define GL_REPLACE 0x1E01 -#define GL_MODULATE 0x2100 -#define GL_DECAL 0x2101 -#define GL_ADD 0x0104 - -/* Texture Env Parameter */ -#define GL_TEXTURE_ENV_MODE 0x2200 -#define GL_TEXTURE_ENV_COLOR 0x2201 - -/* Texture Env Target */ -#define GL_TEXTURE_ENV 0x2300 - -/* TMUs */ -#define GL_TEXTURE0 0x84C0 -#define GL_TEXTURE1 0x84C1 -#define GL_TEXTURE2 0x84C2 -#define GL_TEXTURE3 0x84C3 -#define GL_TEXTURE4 0x84C4 -#define GL_TEXTURE5 0x84C5 -#define GL_TEXTURE6 0x84C6 -#define GL_TEXTURE7 0x84C7 -#define GL_TEXTURE8 0x84C8 -#define GL_TEXTURE9 0x84C9 -#define GL_TEXTURE10 0x84CA -#define GL_TEXTURE11 0x84CB -#define GL_TEXTURE12 0x84CC -#define GL_TEXTURE13 0x84CD -#define GL_TEXTURE14 0x84CE -#define GL_TEXTURE15 0x84CF -#define GL_TEXTURE16 0x84D0 -#define GL_TEXTURE17 0x84D1 -#define GL_TEXTURE18 0x84D2 -#define GL_TEXTURE19 0x84D3 -#define GL_TEXTURE20 0x84D4 -#define GL_TEXTURE21 0x84D5 -#define GL_TEXTURE22 0x84D6 -#define GL_TEXTURE23 0x84D7 -#define GL_TEXTURE24 0x84D8 -#define GL_TEXTURE25 0x84D9 -#define GL_TEXTURE26 0x84DA -#define GL_TEXTURE27 0x84DB -#define GL_TEXTURE28 0x84DC -#define GL_TEXTURE29 0x84DD -#define GL_TEXTURE30 0x84DE -#define GL_TEXTURE31 0x84DF - -/*****************************************************************************/ -/* OpenGL ES 1.1 additions */ - -#define GL_ARRAY_BUFFER 0x8892 -#define GL_ELEMENT_ARRAY_BUFFER 0x8893 - -#define GL_STATIC_DRAW 0x88E4 -#define GL_DYNAMIC_DRAW 0x88E8 - -#define GL_BUFFER_SIZE 0x8764 -#define GL_BUFFER_USAGE 0x8765 - -#define GL_ARRAY_BUFFER_BINDING 0x8894 -#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 -#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 -#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 -#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 -#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A - -/*****************************************************************************/ -/* Required extensions */ - -#define GL_PALETTE4_RGB8_OES 0x8B90 -#define GL_PALETTE4_RGBA8_OES 0x8B91 -#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 -#define GL_PALETTE4_RGBA4_OES 0x8B93 -#define GL_PALETTE4_RGB5_A1_OES 0x8B94 -#define GL_PALETTE8_RGB8_OES 0x8B95 -#define GL_PALETTE8_RGBA8_OES 0x8B96 -#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 -#define GL_PALETTE8_RGBA4_OES 0x8B98 -#define GL_PALETTE8_RGB5_A1_OES 0x8B99 - -#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A -#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B - -#define GL_POINT_SPRITE_OES 0x8861 -#define GL_COORD_REPLACE_OES 0x8862 - -#define GL_POINT_SIZE_ARRAY_OES 0x8B9C -#define GL_POINT_SIZE_ARRAY_TYPE_OES 0x898A -#define GL_POINT_SIZE_ARRAY_STRIDE_OES 0x898B -#define GL_POINT_SIZE_ARRAY_POINTER_OES 0x898C -#define GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES 0x8B9F - -/*****************************************************************************/ -/* Extensions */ - -#define GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES 0x898D -#define GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES 0x898E -#define GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES 0x898F - -#define GL_DIRECT_TEXTURE_2D_QUALCOMM 0x7E80 - - - - -/*****************************************************************************/ -/* OpenGL ES 1.0 functions */ - -void glActiveTexture(GLenum texture); -void glAlphaFunc(GLenum func, GLclampf ref); -void glAlphaFuncx(GLenum func, GLclampx ref); -void glBindTexture(GLenum target, GLuint texture); -void glBlendFunc(GLenum sfactor, GLenum dfactor); -void glClear(GLbitfield mask); -void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); -void glClearColorx(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); -void glClearDepthf(GLclampf depth); -void glClearDepthx(GLclampx depth); -void glClearStencil(GLint s); -void glClientActiveTexture(GLenum texture); -void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -void glColor4x(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); -void glColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a); -void glColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr); -void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, - GLsizei width, GLsizei height, GLint border, - GLsizei imageSize, const GLvoid *data); -void glCompressedTexSubImage2D( GLenum target, GLint level, GLint xoffset, - GLint yoffset, GLsizei width, GLsizei height, - GLenum format, GLsizei imageSize, - const GLvoid *data); -void glCopyTexImage2D( GLenum target, GLint level, GLenum internalformat, - GLint x, GLint y, GLsizei width, GLsizei height, - GLint border); -void glCopyTexSubImage2D( GLenum target, GLint level, GLint xoffset, - GLint yoffset, GLint x, GLint y, GLsizei width, - GLsizei height); -void glCullFace(GLenum mode); -void glDeleteTextures(GLsizei n, const GLuint *textures); -void glDepthFunc(GLenum func); -void glDepthMask(GLboolean flag); -void glDepthRangef(GLclampf zNear, GLclampf zFar); -void glDepthRangex(GLclampx zNear, GLclampx zFar); -void glDisable(GLenum cap); -void glDisableClientState(GLenum array); -void glDrawArrays(GLenum mode, GLint first, GLsizei count); -void glDrawElements(GLenum mode, GLsizei count, - GLenum type, const GLvoid *indices); -void glEnable(GLenum cap); -void glEnableClientState(GLenum array); -void glFinish(void); -void glFlush(void); -void glFogf(GLenum pname, GLfloat param); -void glFogfv(GLenum pname, const GLfloat *params); -void glFogx(GLenum pname, GLfixed param); -void glFogxv(GLenum pname, const GLfixed *params); -void glFrontFace(GLenum mode); -void glFrustumf(GLfloat left, GLfloat right, - GLfloat bottom, GLfloat top, - GLfloat zNear, GLfloat zFar); -void glFrustumx(GLfixed left, GLfixed right, - GLfixed bottom, GLfixed top, - GLfixed zNear, GLfixed zFar); -void glGenTextures(GLsizei n, GLuint *textures); -GLenum glGetError(void); -void glGetIntegerv(GLenum pname, GLint *params); -const GLubyte * glGetString(GLenum name); -void glHint(GLenum target, GLenum mode); -void glLightModelf(GLenum pname, GLfloat param); -void glLightModelfv(GLenum pname, const GLfloat *params); -void glLightModelx(GLenum pname, GLfixed param); -void glLightModelxv(GLenum pname, const GLfixed *params); -void glLightf(GLenum light, GLenum pname, GLfloat param); -void glLightfv(GLenum light, GLenum pname, const GLfloat *params); -void glLightx(GLenum light, GLenum pname, GLfixed param); -void glLightxv(GLenum light, GLenum pname, const GLfixed *params); -void glLineWidth(GLfloat width); -void glLineWidthx(GLfixed width); -void glLoadIdentity(void); -void glLoadMatrixf(const GLfloat *m); -void glLoadMatrixx(const GLfixed *m); -void glLogicOp(GLenum opcode); -void glMaterialf(GLenum face, GLenum pname, GLfloat param); -void glMaterialfv(GLenum face, GLenum pname, const GLfloat *params); -void glMaterialx(GLenum face, GLenum pname, GLfixed param); -void glMaterialxv(GLenum face, GLenum pname, const GLfixed *params); -void glMatrixMode(GLenum mode); -void glMultMatrixf(const GLfloat *m); -void glMultMatrixx(const GLfixed *m); -void glMultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); -void glMultiTexCoord4x(GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); -void glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz); -void glNormal3x(GLfixed nx, GLfixed ny, GLfixed nz); -void glNormalPointer(GLenum type, GLsizei stride, const GLvoid *pointer); -void glOrthof( GLfloat left, GLfloat right, - GLfloat bottom, GLfloat top, - GLfloat zNear, GLfloat zFar); -void glOrthox( GLfixed left, GLfixed right, - GLfixed bottom, GLfixed top, - GLfixed zNear, GLfixed zFar); -void glPixelStorei(GLenum pname, GLint param); -void glPointSize(GLfloat size); -void glPointSizex(GLfixed size); -void glPolygonOffset(GLfloat factor, GLfloat units); -void glPolygonOffsetx(GLfixed factor, GLfixed units); -void glPopMatrix(void); -void glPushMatrix(void); -void glReadPixels( GLint x, GLint y, GLsizei width, GLsizei height, - GLenum format, GLenum type, GLvoid *pixels); -void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); -void glRotatex(GLfixed angle, GLfixed x, GLfixed y, GLfixed z); -void glSampleCoverage(GLclampf value, GLboolean invert); -void glSampleCoveragex(GLclampx value, GLboolean invert); -void glScalef(GLfloat x, GLfloat y, GLfloat z); -void glScalex(GLfixed x, GLfixed y, GLfixed z); -void glScissor(GLint x, GLint y, GLsizei width, GLsizei height); -void glShadeModel(GLenum mode); -void glStencilFunc(GLenum func, GLint ref, GLuint mask); -void glStencilMask(GLuint mask); -void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass); -void glTexCoordPointer( GLint size, GLenum type, - GLsizei stride, const GLvoid *pointer); -void glTexEnvf(GLenum target, GLenum pname, GLfloat param); -void glTexEnvfv(GLenum target, GLenum pname, const GLfloat *params); -void glTexEnvx(GLenum target, GLenum pname, GLfixed param); -void glTexEnvxv(GLenum target, GLenum pname, const GLfixed *params); -void glTexImage2D( GLenum target, GLint level, GLenum internalformat, - GLsizei width, GLsizei height, GLint border, GLenum format, - GLenum type, const GLvoid *pixels); -void glTexParameterf(GLenum target, GLenum pname, GLfloat param); -void glTexParameterx(GLenum target, GLenum pname, GLfixed param); -void glTexSubImage2D( GLenum target, GLint level, GLint xoffset, - GLint yoffset, GLsizei width, GLsizei height, - GLenum format, GLenum type, const GLvoid *pixels); -void glTranslatef(GLfloat x, GLfloat y, GLfloat z); -void glTranslatex(GLfixed x, GLfixed y, GLfixed z); -void glVertexPointer( GLint size, GLenum type, - GLsizei stride, const GLvoid *pointer); -void glViewport(GLint x, GLint y, GLsizei width, GLsizei height); - -/*****************************************************************************/ -/* OpenGL ES 1.1 functions */ - -void glClipPlanef(GLenum plane, const GLfloat* equation); -void glClipPlanex(GLenum plane, const GLfixed* equation); - -void glBindBuffer(GLenum target, GLuint buffer); -void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage); -void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data); -void glDeleteBuffers(GLsizei n, const GLuint* buffers); -void glGenBuffers(GLsizei n, GLuint* buffers); - -void glGetBooleanv(GLenum pname, GLboolean *params); -void glGetFixedv(GLenum pname, GLfixed *params); -void glGetFloatv(GLenum pname, GLfloat *params); -void glGetPointerv(GLenum pname, void **params); -void glGetBufferParameteriv(GLenum target, GLenum pname, GLint *params); -void glGetClipPlanef(GLenum pname, GLfloat eqn[4]); -void glGetClipPlanex(GLenum pname, GLfixed eqn[4]); -void glGetLightxv(GLenum light, GLenum pname, GLfixed *params); -void glGetLightfv(GLenum light, GLenum pname, GLfloat *params); -void glGetMaterialxv(GLenum face, GLenum pname, GLfixed *params); -void glGetMaterialfv(GLenum face, GLenum pname, GLfloat *params); -void glGetTexEnvfv(GLenum env, GLenum pname, GLfloat *params); -void glGetTexEnviv(GLenum env, GLenum pname, GLint *params); -void glGetTexEnvxv(GLenum env, GLenum pname, GLfixed *params); -void glGetTexParameterfv(GLenum target, GLenum pname, GLfloat *params); -void glGetTexParameteriv(GLenum target, GLenum pname, GLint *params); -void glGetTexParameterxv(GLenum target, GLenum pname, GLfixed *params); -GLboolean glIsBuffer(GLuint buffer); -GLboolean glIsEnabled(GLenum cap); -GLboolean glIsTexture(GLuint texture); -void glPointParameterf(GLenum pname, GLfloat param); -void glPointParameterfv(GLenum pname, const GLfloat *params); -void glPointParameterx(GLenum pname, GLfixed param); -void glPointParameterxv(GLenum pname, const GLfixed *params); -void glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); -void glTexEnvi(GLenum target, GLenum pname, GLint param); -void glTexEnviv(GLenum target, GLenum pname, const GLint *params); -void glTexParameterfv(GLenum target, GLenum pname, const GLfloat *params); -void glTexParameteriv(GLenum target, GLenum pname, const GLint *params); -void glTexParameteri(GLenum target, GLenum pname, GLint param); -void glTexParameterxv(GLenum target, GLenum pname, const GLfixed *params); - -/*****************************************************************************/ -/* Required extensions functions */ - -void glPointSizePointerOES(GLenum type, GLsizei stride, const GLvoid *pointer); - - -/*****************************************************************************/ -/* Extensions functions */ - -void glDrawTexsOES(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h); -void glDrawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h); -void glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h); -void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h); -void glDrawTexsvOES(const GLshort* coords); -void glDrawTexivOES(const GLint* coords); -void glDrawTexfvOES(const GLfloat* coords); -void glDrawTexxvOES(const GLfixed* coords); -GLbitfield glQueryMatrixxOES(GLfixed* mantissa, GLint* exponent); - -/* called by dalvik */ -void glColorPointerBounds(GLint size, GLenum type, GLsizei stride, - const GLvoid *ptr, GLsizei count); -void glNormalPointerBounds(GLenum type, GLsizei stride, - const GLvoid *pointer, GLsizei count); -void glTexCoordPointerBounds(GLint size, GLenum type, - GLsizei stride, const GLvoid *pointer, GLsizei count); -void glVertexPointerBounds(GLint size, GLenum type, - GLsizei stride, const GLvoid *pointer, GLsizei count); - -#ifdef __cplusplus -} -#endif - -#endif /* __gl_h_ */ diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index 77676bf464cff..6bd54ba6611ea 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -99,23 +99,31 @@ public: static status_t getOutputSamplingRate(int* samplingRate); static status_t getOutputFrameCount(int* frameCount); static status_t getOutputLatency(uint32_t* latency); + + static status_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount, + size_t* buffSize); // ---------------------------------------------------------------------------- private: - class DeathNotifier: public IBinder::DeathRecipient + class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient { public: - DeathNotifier() { + AudioFlingerClient() { } + // DeathRecipient virtual void binderDied(const wp& who); + + // IAudioFlingerClient + virtual void audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency); + }; - static sp gDeathNotifier; + static sp gAudioFlingerClient; - friend class DeathNotifier; + friend class AudioFlingerClient; static Mutex gLock; static sp gAudioFlinger; @@ -123,6 +131,13 @@ private: static int gOutSamplingRate; static int gOutFrameCount; static uint32_t gOutLatency; + + static size_t gInBuffSize; + // previous parameters for recording buffer size queries + static uint32_t gPrevInSamplingRate; + static int gPrevInFormat; + static int gPrevInChannelCount; + }; }; // namespace android diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index fd62daad8fc2a..5b2bab98bb27c 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -51,6 +51,7 @@ public: MUSIC = 3, ALARM = 4, NOTIFICATION = 5, + BLUETOOTH_SCO = 6, NUM_STREAM_TYPES }; diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index 69703b2b003af..df601d7ca03fe 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace android { @@ -107,6 +108,15 @@ public: // Temporary interface, do not use // TODO: Replace with a more generic key:value get/set mechanism virtual status_t setParameter(const char* key, const char* value) = 0; + + // register a current process for audio output change notifications + virtual void registerClient(const sp& client) = 0; + + // retrieve the audio recording buffer size + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount) = 0; + + // force AudioFlinger thread out of standby + virtual void wakeUp() = 0; }; diff --git a/include/media/IAudioFlingerClient.h b/include/media/IAudioFlingerClient.h new file mode 100644 index 0000000000000..10c3e0fd9f384 --- /dev/null +++ b/include/media/IAudioFlingerClient.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009 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. + */ + +#ifndef ANDROID_IAUDIOFLINGERCLIENT_H +#define ANDROID_IAUDIOFLINGERCLIENT_H + + +#include +#include + + +namespace android { + +// ---------------------------------------------------------------------------- + +class IAudioFlingerClient : public IInterface +{ +public: + DECLARE_META_INTERFACE(AudioFlingerClient); + + // Notifies a change of audio output from/to hardware to/from A2DP. + virtual void audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency) = 0; + +}; + + +// ---------------------------------------------------------------------------- + +class BnAudioFlingerClient : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IAUDIOFLINGERCLIENT_H diff --git a/include/media/JetPlayer.h b/include/media/JetPlayer.h index 426817048d0d1..16764a9192bdc 100644 --- a/include/media/JetPlayer.h +++ b/include/media/JetPlayer.h @@ -33,9 +33,12 @@ class JetPlayer { public: - static const int JET_USERID_UPDATE = 1; - static const int JET_NUMQUEUEDSEGMENT_UPDATE = 2; - static const int JET_PAUSE_UPDATE = 3; + // to keep in sync with the JetPlayer class constants + // defined in frameworks/base/media/java/android/media/JetPlayer.java + static const int JET_EVENT = 1; + static const int JET_USERID_UPDATE = 2; + static const int JET_NUMQUEUEDSEGMENT_UPDATE = 3; + static const int JET_PAUSE_UPDATE = 4; JetPlayer(jobject javaJetPlayer, int maxTracks = 32, @@ -44,7 +47,8 @@ public: int init(); int release(); - int openFile(const char* url); + int loadFromFile(const char* url); + int loadFromFD(const int fd, const long long offset, const long long length); int closeFile(); int play(); int pause(); @@ -53,6 +57,7 @@ public: int setMuteFlags(EAS_U32 muteFlags, bool sync); int setMuteFlag(int trackNum, bool muteFlag, bool sync); int triggerClip(int clipId); + int clearQueue(); void setEventCallback(jetevent_callback callback); @@ -62,7 +67,8 @@ public: private: static int renderThread(void*); int render(); - void fireEventOnStatusChange(); + void fireUpdateOnStatusChange(); + void fireEventsFromJetQueue(); JetPlayer() {} // no default constructor void dump(); diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 30e4578c42124..7f0e7b3a473ed 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -17,9 +17,6 @@ #ifndef ANDROID_MEDIAPLAYERINTERFACE_H #define ANDROID_MEDIAPLAYERINTERFACE_H -#include -#include - #ifdef __cplusplus #include @@ -74,7 +71,6 @@ public: virtual ~MediaPlayerBase() {} virtual status_t initCheck() = 0; virtual bool hardwareOutput() = 0; - virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key) { return 0; } virtual status_t setDataSource(const char *url) = 0; virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0; virtual status_t setVideoSurface(const sp& surface) = 0; @@ -125,34 +121,6 @@ public: #endif // __cplusplus -// A thread can set the thread local variable identified by the pthread_key_t -// that was passed to the player using the setSigBusHandlerStructTLSKey() -// method to the address of the following structure. -// If 'handlesigbus' is non-NULL, the function it points to will be called, -// and if it returns 0, the signal will be assumed to have been handled, -// and no other action will be taken. If it returns non-zero, the old SIGBUS -// handler will be called. -// If 'handlesigbus is NULL, then sigbusvar must be non NULL. The system's -// SIGBUS handler will map an accessible page filled with zeroes at the -// location that caused the original fault, set the variable pointed to by -// sigbusvar to a non-zero value, and exit (which causes the operation to -// be retried, which should now succeed). -// If base and len are non zero, which is strongly recommended, they will -// be used as additional constraints on the signal handler. That is, when -// specified, the fault address must be in the range specified by base and -// len in order for handlesigbus() to be called or sigbusvar to be set. -// If the fault address is outside of the range, the old SIGBUS handler -// will be called. -struct mediasigbushandler { - int (*handlesigbus)(siginfo_t *, struct mediasigbushandler *); - int *sigbusvar; - char *base; - int len; - // these next two are free for application use - struct mediasigbushandler *next; - void *data; -}; - #endif // ANDROID_MEDIAPLAYERINTERFACE_H diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h index 5f302ed8c4cdf..6d98852eb4052 100644 --- a/include/media/PVPlayer.h +++ b/include/media/PVPlayer.h @@ -20,6 +20,12 @@ #include #include +#define MAX_OPENCORE_INSTANCES 25 + +#ifdef MAX_OPENCORE_INSTANCES +#include +#endif + class PlayerDriver; namespace android { @@ -31,7 +37,6 @@ public: virtual ~PVPlayer(); virtual status_t initCheck(); - virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key); virtual status_t setDataSource(const char *url); virtual status_t setDataSource(int fd, int64_t offset, int64_t length); virtual status_t setVideoSurface(const sp& surface); @@ -62,10 +67,13 @@ private: char * mDataSourcePath; bool mIsDataSourceSet; sp mSurface; - void * mMemBase; - off_t mMemSize; + int mSharedFd; status_t mInit; int mDuration; + +#ifdef MAX_OPENCORE_INSTANCES + static volatile int32_t sNumInstances; +#endif }; }; // namespace android diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index 0cfdeec7309a8..ec64e4d704347 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -85,8 +85,6 @@ private: TONE_RESTARTING // }; - static const unsigned int NUM_PCM_BUFFERS = 2; // Number of AudioTrack pcm buffers - static const unsigned int TONEGEN_MAX_WAVES = 3; static const unsigned int TONEGEN_MAX_SEGMENTS = 4; // Maximun number of elenemts in static const unsigned int TONEGEN_INF = 0xFFFFFFFF; // Represents infinite time duration @@ -127,7 +125,6 @@ private: const ToneDescriptor *mpNewToneDesc; // pointer to next active tone descriptor int mSamplingRate; // AudioFlinger Sampling rate - int mBufferSize; // PCM buffer size in frames AudioTrack *mpAudioTrack; // Pointer to audio track used for playback Mutex mLock; // Mutex to control concurent access to ToneGenerator object from audio callback and application API Mutex mCbkCondLock; // Mutex associated to mWaitCbkCond diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h index 3056139bef917..0c7ad462fb2ac 100644 --- a/include/private/opengles/gl_context.h +++ b/include/private/opengles/gl_context.h @@ -28,6 +28,7 @@ #include #include +#include namespace android { diff --git a/include/ui/Camera.h b/include/ui/Camera.h index 44acce5dd0b6b..e593feab7899a 100644 --- a/include/ui/Camera.h +++ b/include/ui/Camera.h @@ -23,8 +23,8 @@ namespace android { /* - * A set of bit masks for specifying how the received frames from preview are - * handled before the frame callback call. + * A set of bit masks for specifying how the received preview frames are + * handled before the previewCallback() call. * * The least significant 3 bits of an "int" value are used for this purpose: * @@ -34,10 +34,18 @@ namespace android { * | |-----------> determine whether the callback is one-shot or not * |-------------> determine whether the frame is copied out or not * + * WARNING: + * When a frame is sent directly without copying, it is the frame receiver's + * responsiblity to make sure that the frame data won't get corrupted by + * subsequent preview frames filled by the camera. This flag is recommended + * only when copying out data brings significant performance price and the + * handling/processing of the received frame data is always faster than + * the preview frame rate so that data corruption won't occur. + * * For instance, * 1. 0x00 disables the callback. In this case, copy out and one shot bits * are ignored. - * 2. 0x01 enables a callback without copying out the recievied frames. A + * 2. 0x01 enables a callback without copying out the received frames. A * typical use case is the Camcorder application to avoid making costly * frame copies. * 3. 0x05 is enabling a callback with frame copied out repeatedly. A typical @@ -96,6 +104,18 @@ public: // get preview state bool previewEnabled(); + // start recording mode, must call setPreviewDisplay first + status_t startRecording(); + + // stop recording mode + void stopRecording(); + + // get recording state + bool recordingEnabled(); + + // release a recording frame + void releaseRecordingFrame(const sp& mem); + // autoFocus - status returned from callback status_t autoFocus(); @@ -111,20 +131,19 @@ public: void setShutterCallback(shutter_callback cb, void *cookie); void setRawCallback(frame_callback cb, void *cookie); void setJpegCallback(frame_callback cb, void *cookie); - - void setFrameCallback(frame_callback cb, - void *cookie, - int frame_callback_flag = FRAME_CALLBACK_FLAG_NOOP); - + void setRecordingCallback(frame_callback cb, void *cookie); + void setPreviewCallback(frame_callback cb, void *cookie, int preview_callback_flag = FRAME_CALLBACK_FLAG_NOOP); void setErrorCallback(error_callback cb, void *cookie); void setAutoFocusCallback(autofocus_callback cb, void *cookie); + // ICameraClient interface virtual void shutterCallback(); virtual void rawCallback(const sp& picture); virtual void jpegCallback(const sp& picture); - virtual void frameCallback(const sp& frame); + virtual void previewCallback(const sp& frame); virtual void errorCallback(status_t error); virtual void autoFocusCallback(bool focused); + virtual void recordingCallback(const sp& frame); sp remote(); @@ -155,8 +174,10 @@ private: void *mRawCallbackCookie; frame_callback mJpegCallback; void *mJpegCallbackCookie; - frame_callback mFrameCallback; - void *mFrameCallbackCookie; + frame_callback mPreviewCallback; + void *mPreviewCallbackCookie; + frame_callback mRecordingCallback; + void *mRecordingCallbackCookie; error_callback mErrorCallback; void *mErrorCallbackCookie; autofocus_callback mAutoFocusCallback; diff --git a/include/ui/CameraHardwareInterface.h b/include/ui/CameraHardwareInterface.h index 2bd53dde32e09..b068c52f542c9 100644 --- a/include/ui/CameraHardwareInterface.h +++ b/include/ui/CameraHardwareInterface.h @@ -20,12 +20,16 @@ #include #include #include +#include namespace android { /** Callback for startPreview() */ typedef void (*preview_callback)(const sp& mem, void* user); +/** Callback for startRecord() */ +typedef void (*recording_callback)(const sp& mem, void* user); + /** Callback for takePicture() */ typedef void (*shutter_callback)(void* user); @@ -89,6 +93,11 @@ public: * call back parameter may be null. */ virtual status_t startPreview(preview_callback cb, void* user) = 0; + /** + * Only used if overlays are used for camera preview. + */ + virtual bool useOverlay() {return false;} + virtual status_t setOverlay(const sp &overlay) {return BAD_VALUE;} /** * Stop a previously started preview. @@ -100,6 +109,29 @@ public: */ virtual bool previewEnabled() = 0; + /** + * Start record mode. When a record image is available recording_callback() + * is called with the user parameter. Every record frame must be released + * by calling releaseRecordingFrame(). + */ + virtual status_t startRecording(recording_callback cb, void* user) = 0; + + /** + * Stop a previously started recording. + */ + virtual void stopRecording() = 0; + + /** + * Returns true if recording is enabled. + */ + virtual bool recordingEnabled() = 0; + + /** + * Release a record frame previously returned by the recording_callback() + * passed to startRecord(). + */ + virtual void releaseRecordingFrame(const sp& mem) = 0; + /** * Start auto focus, the callback routine is called * once when focusing is complete. autoFocus() will diff --git a/include/ui/CameraParameters.h b/include/ui/CameraParameters.h index e35a0546ba09a..9ca18068ef590 100644 --- a/include/ui/CameraParameters.h +++ b/include/ui/CameraParameters.h @@ -29,6 +29,12 @@ public: CameraParameters(const String8 ¶ms) { unflatten(params); } ~CameraParameters(); + enum { + CAMERA_ORIENTATION_UNKNOWN = 0, + CAMERA_ORIENTATION_PORTRAIT = 1, + CAMERA_ORIENTATION_LANDSCAPE = 2, + }; + String8 flatten() const; void unflatten(const String8 ¶ms); @@ -57,6 +63,9 @@ public: void setPictureFormat(const char *format); const char *getPictureFormat() const; + int getOrientation() const; + void setOrientation(int orientation); + void dump() const; status_t dump(int fd, const Vector& args) const; diff --git a/include/ui/EGLDisplaySurface.h b/include/ui/EGLDisplaySurface.h index 0190e099792cc..a8b58539d192d 100644 --- a/include/ui/EGLDisplaySurface.h +++ b/include/ui/EGLDisplaySurface.h @@ -27,7 +27,10 @@ #include #include +#include + struct copybit_device_t; +struct copybit_image_t; // --------------------------------------------------------------------------- namespace android { @@ -44,17 +47,17 @@ public: int32_t getPageFlipCount() const; void copyFrontToBack(const Region& copyback); + void copyFrontToImage(const copybit_image_t& dst); + void copyBackToImage(const copybit_image_t& dst); + void setSwapRectangle(int l, int t, int w, int h); + private: static void hook_incRef(NativeWindowType window); static void hook_decRef(NativeWindowType window); static uint32_t hook_swapBuffers(NativeWindowType window); - static void hook_setSwapRectangle(NativeWindowType window, int l, int t, int w, int h); - static uint32_t hook_nextBuffer(NativeWindowType window); uint32_t swapBuffers(); - uint32_t nextBuffer(); - void setSwapRectangle(int l, int t, int w, int h); status_t mapFrameBuffer(); diff --git a/include/ui/EGLNativeSurface.h b/include/ui/EGLNativeSurface.h index c303cd8d4ac87..7964e7c6bac99 100644 --- a/include/ui/EGLNativeSurface.h +++ b/include/ui/EGLNativeSurface.h @@ -23,7 +23,7 @@ #include #include -#include +#include // --------------------------------------------------------------------------- namespace android { diff --git a/include/ui/EGLNativeWindowSurface.h b/include/ui/EGLNativeWindowSurface.h index 058479a2a6e4b..3494234636042 100644 --- a/include/ui/EGLNativeWindowSurface.h +++ b/include/ui/EGLNativeWindowSurface.h @@ -20,6 +20,7 @@ #include #include #include +#include // --------------------------------------------------------------------------- namespace android { @@ -33,18 +34,16 @@ public: EGLNativeWindowSurface(const sp& surface); ~EGLNativeWindowSurface(); + void setSwapRectangle(int l, int t, int w, int h); + private: static void hook_incRef(NativeWindowType window); static void hook_decRef(NativeWindowType window); static uint32_t hook_swapBuffers(NativeWindowType window); - static uint32_t hook_nextBuffer(NativeWindowType window); - static void hook_setSwapRectangle(NativeWindowType window, int l, int t, int w, int h); static void hook_connect(NativeWindowType window); static void hook_disconnect(NativeWindowType window); uint32_t swapBuffers(); - uint32_t nextBuffer(); - void setSwapRectangle(int l, int t, int w, int h); void connect(); void disconnect(); diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h index 017c145bfb4c6..3848d8c3bdc03 100644 --- a/include/ui/EventHub.h +++ b/include/ui/EventHub.h @@ -76,6 +76,9 @@ public: DEVICE_REMOVED = 0x20000000 }; + // examine key input devices for specific framework keycode support + bool hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags); + virtual bool getEvent(int32_t* outDeviceId, int32_t* outType, int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, int32_t* outValue, nsecs_t* outWhen); @@ -100,6 +103,7 @@ private: const String8 path; String8 name; uint32_t classes; + uint8_t* keyBitmask; KeyLayoutMap* layoutMap; String8 keylayoutFilename; device_t* next; @@ -134,8 +138,6 @@ private: #ifdef EV_SW int32_t mSwitches[SW_MAX+1]; #endif - - KeyLayoutMap * mLayoutMap; }; }; // namespace android diff --git a/include/ui/ICamera.h b/include/ui/ICamera.h index ea2fcee2ae5ee..241fb632616fa 100644 --- a/include/ui/ICamera.h +++ b/include/ui/ICamera.h @@ -48,9 +48,9 @@ public: // pass the buffered ISurface to the camera service virtual status_t setPreviewDisplay(const sp& surface) = 0; - // set the frame callback flag to affect how the received frames from + // set the preview callback flag to affect how the received frames from // preview are handled. - virtual void setFrameCallbackFlag(int frame_callback_flag) = 0; + virtual void setPreviewCallbackFlag(int flag) = 0; // start preview mode, must call setPreviewDisplay first virtual status_t startPreview() = 0; @@ -61,6 +61,18 @@ public: // get preview state virtual bool previewEnabled() = 0; + // start recording mode + virtual status_t startRecording() = 0; + + // stop recording mode + virtual void stopRecording() = 0; + + // get recording state + virtual bool recordingEnabled() = 0; + + // release a recording frame + virtual void releaseRecordingFrame(const sp& mem) = 0; + // auto focus virtual status_t autoFocus() = 0; diff --git a/include/ui/ICameraClient.h b/include/ui/ICameraClient.h index a286b8e0a5a17..73b951cf3cc0e 100644 --- a/include/ui/ICameraClient.h +++ b/include/ui/ICameraClient.h @@ -32,9 +32,10 @@ public: virtual void shutterCallback() = 0; virtual void rawCallback(const sp& picture) = 0; virtual void jpegCallback(const sp& picture) = 0; - virtual void frameCallback(const sp& frame) = 0; + virtual void previewCallback(const sp& frame) = 0; virtual void errorCallback(status_t error) = 0; virtual void autoFocusCallback(bool focused) = 0; + virtual void recordingCallback(const sp& frame) = 0; }; diff --git a/include/ui/ISurface.h b/include/ui/ISurface.h index 9a7383cc242d6..1c8043dbf048d 100644 --- a/include/ui/ISurface.h +++ b/include/ui/ISurface.h @@ -34,11 +34,56 @@ class OverlayRef; class ISurface : public IInterface { +protected: + enum { + REGISTER_BUFFERS = IBinder::FIRST_CALL_TRANSACTION, + UNREGISTER_BUFFERS, + POST_BUFFER, // one-way transaction + CREATE_OVERLAY, + }; + public: DECLARE_META_INTERFACE(Surface); - virtual status_t registerBuffers(int w, int h, int hstride, int vstride, - PixelFormat format, const sp& heap) = 0; + + class BufferHeap { + public: + enum { + /* flip source image horizontally */ + FLIP_H = 0x01, + /* flip source image vertically */ + FLIP_V = 0x02, + /* rotate source image 90 degrees */ + ROT_90 = 0x04, + /* rotate source image 180 degrees */ + ROT_180 = 0x03, + /* rotate source image 270 degrees */ + ROT_270 = 0x07, + }; + BufferHeap(); + + BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, const sp& heap); + + BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, uint32_t transform, uint32_t flags, + const sp& heap); + + ~BufferHeap(); + + uint32_t w; + uint32_t h; + int32_t hor_stride; + int32_t ver_stride; + PixelFormat format; + uint32_t transform; + uint32_t flags; + sp heap; + }; + + virtual status_t registerBuffers(const BufferHeap& buffers) = 0; virtual void postBuffer(ssize_t offset) = 0; // one-way diff --git a/include/ui/Overlay.h b/include/ui/Overlay.h index f8454fd73a078..66514b44ab9ff 100644 --- a/include/ui/Overlay.h +++ b/include/ui/Overlay.h @@ -91,6 +91,7 @@ public: int32_t getFormat() const; int32_t getWidthStride() const; int32_t getHeightStride() const; + int32_t getBufferCount() const; status_t getStatus() const; private: diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h index b65c959261e73..14af8232bb15c 100644 --- a/include/ui/PixelFormat.h +++ b/include/ui/PixelFormat.h @@ -71,6 +71,10 @@ enum { PIXEL_FORMAT_YCbCr_422_SP= GGL_PIXEL_FORMAT_YCbCr_422_SP, PIXEL_FORMAT_YCbCr_420_SP= GGL_PIXEL_FORMAT_YCbCr_420_SP, + PIXEL_FORMAT_YCbCr_422_P = GGL_PIXEL_FORMAT_YCbCr_422_P, + PIXEL_FORMAT_YCbCr_420_P = GGL_PIXEL_FORMAT_YCbCr_420_P, + PIXEL_FORMAT_YCbCr_422_I = GGL_PIXEL_FORMAT_YCbCr_422_I, + PIXEL_FORMAT_YCbCr_420_I = GGL_PIXEL_FORMAT_YCbCr_420_I, // New formats can be added if they're also defined in // pixelflinger/format.h @@ -80,7 +84,19 @@ typedef int32_t PixelFormat; struct PixelFormatInfo { + enum { // components + ALPHA = 1, + RGB = 2, + RGBA = 3, + LUMINANCE = 4, + LUMINANCE_ALPHA = 5, + Y_CB_CR_SP = 6, + Y_CB_CR_P = 7, + Y_CB_CR_I = 8, + }; + inline PixelFormatInfo() : version(sizeof(PixelFormatInfo)) { } + size_t getScanlineSize(unsigned int width) const; size_t version; PixelFormat format; size_t bytesPerPixel; @@ -93,7 +109,9 @@ struct PixelFormatInfo uint8_t l_green; uint8_t h_blue; uint8_t l_blue; - uint32_t reserved[2]; + uint8_t components; + uint8_t reserved0[3]; + uint32_t reserved1; }; // Consider caching the results of these functions are they're not diff --git a/include/utils/Asset.h b/include/utils/Asset.h index d8351f57cbbf5..453a2049ad3c6 100644 --- a/include/utils/Asset.h +++ b/include/utils/Asset.h @@ -62,7 +62,11 @@ public: enum { /* data larger than this does not get uncompressed into a buffer */ +#ifdef HAVE_ANDROID_OS UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 +#else + UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024 +#endif }; /* diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 2d56e3e263c15..d83a33cef5678 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -223,7 +223,7 @@ struct Res_value { // Number of bytes in this structure. uint16_t size; - + // Always set to 0. uint8_t res0; @@ -1131,10 +1131,8 @@ struct ResTable_config && orientation != settings.orientation) { return false; } - if (settings.density != 0 && density != 0 - && density != settings.density) { - return false; - } + // Density not taken into account, always match, no matter what + // density is specified for the resource if (settings.touchscreen != 0 && touchscreen != 0 && touchscreen != settings.touchscreen) { return false; @@ -1464,11 +1462,11 @@ public: * @return ssize_t Either a >= 0 table index or a negative error code. */ ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag=false, - uint32_t* outSpecFlags=NULL) const; + uint32_t* outSpecFlags=NULL, ResTable_config* outConfig=NULL) const; inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue, uint32_t* outSpecFlags=NULL) const { - return getResource(res.ident, outValue, outSpecFlags); + return getResource(res.ident, outValue, false, outSpecFlags, NULL); } ssize_t resolveReference(Res_value* inOutValue, diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp index c8c8431096d7b..3c1803633cb0b 100644 --- a/libs/audioflinger/A2dpAudioInterface.cpp +++ b/libs/audioflinger/A2dpAudioInterface.cpp @@ -99,6 +99,10 @@ status_t A2dpAudioInterface::setParameter(const char *key, const char *value) if (strcmp(key, "a2dp_sink_address") == 0) { return mOutput->setAddress(value); } + if (strcmp(key, "bluetooth_enabled") == 0 && + strcmp(value, "false") == 0) { + return mOutput->close(); + } return 0; } @@ -154,8 +158,7 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::set( A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut() { - if (mData) - a2dp_cleanup(mData); + close(); } ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) @@ -186,7 +189,8 @@ ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t return bytes; -Error: +Error: + close(); // Simulate audio output timing in case of error usleep(bytes * 1000000 / frameSize() / sampleRate()); @@ -213,17 +217,22 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address) if (strcmp(address, mA2dpAddress)) { strcpy(mA2dpAddress, address); - - if (mInitialized) { - a2dp_cleanup(mData); - mData = NULL; - mInitialized = false; - } + close(); } return NO_ERROR; } +status_t A2dpAudioInterface::A2dpAudioStreamOut::close() +{ + if (mData) { + a2dp_cleanup(mData); + mData = NULL; + mInitialized = false; + } + return NO_ERROR; +} + status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector& args) { return NO_ERROR; diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h index 2197d0eb5b4c1..38ba684694199 100644 --- a/libs/audioflinger/A2dpAudioInterface.h +++ b/libs/audioflinger/A2dpAudioInterface.h @@ -77,10 +77,11 @@ private: virtual size_t bufferSize() const { return 512 * 20; } virtual int channelCount() const { return 2; } virtual int format() const { return AudioSystem::PCM_16_BIT; } - virtual uint32_t latency() const { return ((1000*channelCount()*bufferSize())/frameSize())/sampleRate() + 200; } + virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; } virtual status_t setVolume(float volume) { return INVALID_OPERATION; } virtual ssize_t write(const void* buffer, size_t bytes); status_t standby(); + status_t close(); virtual status_t dump(int fd, const Vector& args); private: diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index c330bc8faba14..017a298c69274 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -183,6 +183,7 @@ AudioFlinger::~AudioFlinger() void AudioFlinger::setOutput(AudioStreamOut* output) { mRequestedOutput = output; + mWaitWorkCV.broadcast(); } void AudioFlinger::doSetOutput(AudioStreamOut* output) @@ -198,6 +199,7 @@ void AudioFlinger::doSetOutput(AudioStreamOut* output) mFrameCount = getOutputFrameCount(output); mAudioMixer = (output == mA2dpOutput ? mA2dpAudioMixer : mHardwareAudioMixer); mOutput = output; + notifyOutputChange_l(); } size_t AudioFlinger::getOutputFrameCount(AudioStreamOut* output) @@ -211,6 +213,8 @@ bool AudioFlinger::streamDisablesA2dp(int streamType) return (streamType == AudioTrack::SYSTEM || streamType == AudioTrack::RING || streamType == AudioTrack::ALARM || + streamType == AudioTrack::VOICE_CALL || + streamType == AudioTrack::BLUETOOTH_SCO || streamType == AudioTrack::NOTIFICATION); } @@ -344,7 +348,8 @@ bool AudioFlinger::threadLoop() int16_t* curBuf = mMixBuffer; Vector< sp > tracksToRemove; size_t enabledTracks = 0; - nsecs_t standbyTime = systemTime(); + nsecs_t standbyTime = systemTime(); + nsecs_t outputSwitchStandbyTime = 0; do { enabledTracks = 0; @@ -362,6 +367,11 @@ bool AudioFlinger::threadLoop() mOutput->standby(); mStandby = true; } + if (outputSwitchStandbyTime) { + AudioStreamOut *output = (mOutput == mHardwareOutput) ? mA2dpOutput : mHardwareOutput; + output->standby(); + outputSwitchStandbyTime = 0; + } mHardwareStatus = AUDIO_HW_IDLE; // we're about to wait, flush the binder command buffer IPCThreadState::self()->flushCommands(); @@ -375,11 +385,18 @@ bool AudioFlinger::threadLoop() if (mRequestedOutput != mOutput) { // put current output into standby mode - if (mOutput) mOutput->standby(); + if (mOutput) { + outputSwitchStandbyTime = systemTime() + milliseconds(mOutput->latency()); + } // change output doSetOutput(mRequestedOutput); } + if (outputSwitchStandbyTime && systemTime() > outputSwitchStandbyTime) { + AudioStreamOut *output = (mOutput == mHardwareOutput) ? mA2dpOutput : mHardwareOutput; + output->standby(); + outputSwitchStandbyTime = 0; + } // find out which tracks need to be processed size_t count = activeTracks.size(); @@ -481,7 +498,7 @@ bool AudioFlinger::threadLoop() removeActiveTrack(track); if (track->isTerminated()) { mTracks.remove(track); - mAudioMixer->deleteTrackName(track->mName); + deleteTrackName(track->mName); } } } @@ -512,7 +529,7 @@ bool AudioFlinger::threadLoop() // active tracks were late. Sleep a little bit to give // them another chance. If we're too late, the audio // hardware will zero-fill for us. - LOGV("no buffers - usleep(%lu)", sleepTime); +// LOGV("no buffers - usleep(%lu)", sleepTime); usleep(sleepTime); if (sleepTime < kMaxBufferRecoveryInUsecs) { sleepTime += kBufferRecoveryInUsecs; @@ -802,12 +819,14 @@ status_t AudioFlinger::setStreamVolume(int stream, float value) mStreamTypes[stream].volume = value; status_t ret = NO_ERROR; - if (stream == AudioTrack::VOICE_CALL) { + if (stream == AudioTrack::VOICE_CALL || + stream == AudioTrack::BLUETOOTH_SCO) { AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_SET_VOICE_VOLUME; ret = mAudioHardware->setVoiceVolume(value); mHardwareStatus = AUDIO_HW_IDLE; } + return ret; } @@ -821,7 +840,20 @@ status_t AudioFlinger::setStreamMute(int stream, bool muted) if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { return BAD_VALUE; } +#ifdef WITH_A2DP + if (stream == AudioTrack::MUSIC) + { + AutoMutex lock(&mLock); + if (mA2dpDisableCount > 0) + mMusicMuteSaved = muted; + else + mStreamTypes[stream].mute = muted; + } else { + mStreamTypes[stream].mute = muted; + } +#else mStreamTypes[stream].mute = muted; +#endif return NO_ERROR; } @@ -838,6 +870,12 @@ bool AudioFlinger::streamMute(int stream) const if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { return true; } +#ifdef WITH_A2DP + if (stream == AudioTrack::MUSIC && mA2dpDisableCount > 0) + { + return mMusicMuteSaved; + } +#endif return mStreamTypes[stream].mute; } @@ -869,6 +907,59 @@ status_t AudioFlinger::setParameter(const char* key, const char* value) return result; } + +void AudioFlinger::registerClient(const sp& client) +{ + Mutex::Autolock _l(mLock); + + sp binder = client->asBinder(); + if (mNotificationClients.indexOf(binder) < 0) { + LOGV("Adding notification client %p", binder.get()); + binder->linkToDeath(this); + mNotificationClients.add(binder); + } +} + + +size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); +} + +void AudioFlinger::wakeUp() +{ + mWaitWorkCV.broadcast(); +} + +void AudioFlinger::binderDied(const wp& who) { + Mutex::Autolock _l(mLock); + + IBinder *binder = who.unsafe_get(); + + if (binder != NULL) { + int index = mNotificationClients.indexOf(binder); + if (index >= 0) { + LOGV("Removing notification client %p", binder); + mNotificationClients.removeAt(index); + } + } +} + +// must be called with mLock held +void AudioFlinger::notifyOutputChange_l() +{ + size_t size = mNotificationClients.size(); + uint32_t latency = mOutput->latency(); + for (size_t i = 0; i < size; i++) { + sp binder = mNotificationClients.itemAt(i).promote(); + if (binder != NULL) { + LOGV("Notifying output change to client %p", binder.get()); + sp client = interface_cast (binder); + client->audioOutputChanged(mFrameCount, mSampleRate, latency); + } + } +} + void AudioFlinger::removeClient(pid_t pid) { Mutex::Autolock _l(mLock); @@ -920,7 +1011,7 @@ void AudioFlinger::remove_track_l(wp track, int name) if (t!=NULL) { t->reset(); } - audioMixer()->deleteTrackName(name); + deleteTrackName(name); removeActiveTrack(track); mWaitWorkCV.broadcast(); } @@ -938,7 +1029,7 @@ void AudioFlinger::destroyTrack(const sp& track) if (mActiveTracks.indexOf(track) < 0) { LOGV("remove track (%d) and delete from mixer", track->name()); mTracks.remove(track); - audioMixer()->deleteTrackName(keep->name()); + deleteTrackName(keep->name()); } } @@ -953,9 +1044,11 @@ void AudioFlinger::addActiveTrack(const wp& t) if (mA2dpDisableCount++ == 0 && isA2dpEnabled()) { setA2dpEnabled(false); mA2dpSuppressed = true; - LOGD("mA2dpSuppressed = true\n"); + mMusicMuteSaved = mStreamTypes[AudioTrack::MUSIC].mute; + mStreamTypes[AudioTrack::MUSIC].mute = true; + LOGV("mA2dpSuppressed = true, track %d\n", track->name()); } - LOGD("mA2dpDisableCount incremented to %d\n", mA2dpDisableCount); + LOGV("mA2dpDisableCount incremented to %d, track %d\n", mA2dpDisableCount, track->name()); } #endif } @@ -969,17 +1062,45 @@ void AudioFlinger::removeActiveTrack(const wp& t) if (streamDisablesA2dp(track->type())) { if (mA2dpDisableCount > 0) { mA2dpDisableCount--; + LOGV("mA2dpDisableCount decremented to %d, track %d\n", mA2dpDisableCount, track->name()); if (mA2dpDisableCount == 0 && mA2dpSuppressed) { setA2dpEnabled(true); mA2dpSuppressed = false; - } - LOGD("mA2dpDisableCount decremented to %d\n", mA2dpDisableCount); + mStreamTypes[AudioTrack::MUSIC].mute = mMusicMuteSaved; + LOGV("mA2dpSuppressed = false, track %d\n", track->name()); + } } else LOGE("mA2dpDisableCount is already zero"); } #endif } +int AudioFlinger::getTrackName() +{ + // Both mixers must have the same set of track used to avoid mismatches when + // switching from A2DP output to hardware output + int a2DpName; + int hwName; +#ifdef WITH_A2DP + a2DpName = mA2dpAudioMixer->getTrackName(); +#endif + hwName = mHardwareAudioMixer->getTrackName(); + + LOGW_IF((a2DpName != hwName), "getTrackName track name mismatch! A2DP %d, HW %d", a2DpName, hwName); + + return hwName; +} + +void AudioFlinger::deleteTrackName(int name) +{ + // Both mixers must have the same set of track used to avoid mismatches when + // switching from A2DP output to hardware output + mHardwareAudioMixer->deleteTrackName(name); +#ifdef WITH_A2DP + mA2dpAudioMixer->deleteTrackName(name); +#endif +} + // ---------------------------------------------------------------------------- AudioFlinger::Client::Client(const sp& audioFlinger, pid_t pid) @@ -1022,7 +1143,8 @@ AudioFlinger::TrackBase::TrackBase( mFormat(format), mFlags(0) { - mName = audioFlinger->audioMixer()->getTrackName(); + mName = audioFlinger->getTrackName(); + LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); if (mName < 0) { LOGE("no more track names availlable"); return; @@ -1237,14 +1359,14 @@ bool AudioFlinger::Track::isReady() const { status_t AudioFlinger::Track::start() { - LOGV("start(%d)", mName); + LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); mAudioFlinger->addTrack(this); return NO_ERROR; } void AudioFlinger::Track::stop() { - LOGV("stop(%d)", mName); + LOGV("stop(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); Mutex::Autolock _l(mAudioFlinger->mLock); if (mState > STOPPED) { mState = STOPPED; @@ -1258,7 +1380,7 @@ void AudioFlinger::Track::stop() void AudioFlinger::Track::pause() { - LOGV("pause(%d)", mName); + LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); Mutex::Autolock _l(mAudioFlinger->mLock); if (mState == ACTIVE || mState == RESUMING) { mState = PAUSING; @@ -1485,7 +1607,7 @@ AudioFlinger::RecordTrack::RecordTrack( AudioFlinger::RecordTrack::~RecordTrack() { - mAudioFlinger->audioMixer()->deleteTrackName(mName); + mAudioFlinger->deleteTrackName(mName); } status_t AudioFlinger::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 9ab362a39ddd1..38fa0017e7d1f 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -54,7 +55,7 @@ class AudioBuffer; static const nsecs_t kStandbyTimeInNsecs = seconds(3); -class AudioFlinger : public BnAudioFlinger, protected Thread +class AudioFlinger : public BnAudioFlinger, protected Thread, public IBinder::DeathRecipient { public: static void instantiate(); @@ -109,6 +110,15 @@ public: virtual status_t setParameter(const char* key, const char* value); + virtual void registerClient(const sp& client); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + + virtual void wakeUp(); + + // IBinder::DeathRecipient + virtual void binderDied(const wp& who); + enum hardware_call_state { AUDIO_HW_IDLE = 0, AUDIO_HW_INIT, @@ -314,7 +324,7 @@ private: virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); bool isMuted() const { - return mMute; + return (mMute || mAudioFlinger->mStreamTypes[mStreamType].mute); } bool isPausing() const { @@ -382,6 +392,8 @@ private: void destroyTrack(const sp& track); void addActiveTrack(const wp& track); void removeActiveTrack(const wp& track); + int getTrackName(); + void deleteTrackName(int name); AudioMixer* audioMixer() { return mAudioMixer; @@ -460,6 +472,8 @@ private: status_t startRecord(RecordTrack* recordTrack); void stopRecord(RecordTrack* recordTrack); + void notifyOutputChange_l(); + mutable Mutex mHardwareLock; mutable Mutex mLock; mutable Condition mWaitWorkCV; @@ -494,6 +508,8 @@ private: bool mInWrite; int mA2dpDisableCount; bool mA2dpSuppressed; + bool mMusicMuteSaved; + SortedVector< wp > mNotificationClients; }; // ---------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index d14cebf5dbe36..53ba3bc6e4e0b 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -15,7 +15,9 @@ LOCAL_SRC_FILES:= \ LayerBlur.cpp \ LayerBitmap.cpp \ LayerDim.cpp \ + LayerOrientationAnim.cpp \ LayerScreenshot.cpp \ + OrientationAnimation.cpp \ RFBServer.cpp \ SurfaceFlinger.cpp \ Tokenizer.cpp \ @@ -38,7 +40,8 @@ LOCAL_SHARED_LIBRARIES := \ libcorecg \ libsgl \ libpixelflinger \ - libGLES_CM + libEGL \ + libGLESv1_CM LOCAL_C_INCLUDES := \ $(call include-path-for, corecg graphics) diff --git a/libs/surfaceflinger/BootAnimation.cpp b/libs/surfaceflinger/BootAnimation.cpp index d18f59abac52d..2b30336b78b13 100644 --- a/libs/surfaceflinger/BootAnimation.cpp +++ b/libs/surfaceflinger/BootAnimation.cpp @@ -39,7 +39,9 @@ #include #include -#include +#include +#include +#include #include "BootAnimation.h" @@ -47,32 +49,28 @@ namespace android { // --------------------------------------------------------------------------- -BootAnimation::BootAnimation(const sp& composer) -: Thread(false) -{ +BootAnimation::BootAnimation(const sp& composer) : + Thread(false) { mSession = SurfaceComposerClient::clientForConnection( composer->createConnection()->asBinder()); } -BootAnimation::~BootAnimation() -{ +BootAnimation::~BootAnimation() { } -void BootAnimation::onFirstRef() -{ +void BootAnimation::onFirstRef() { run("BootAnimation", PRIORITY_DISPLAY); } -const sp& BootAnimation::session() const -{ +const sp& BootAnimation::session() const { return mSession; } -status_t BootAnimation::initTexture( - Texture* texture, AssetManager& assets, const char* name) -{ +status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, + const char* name) { Asset* asset = assets.open(name, Asset::ACCESS_BUFFER); - if (!asset) return NO_INIT; + if (!asset) + return NO_INIT; SkBitmap bitmap; SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode); @@ -84,32 +82,32 @@ status_t BootAnimation::initTexture( bitmap.lockPixels(); const int w = bitmap.width(); - const int h = bitmap.height(); + const int h = bitmap.height(); const void* p = bitmap.getPixels(); - + GLint crop[4] = { 0, h, w, -h }; texture->w = w; texture->h = h; glGenTextures(1, &texture->name); glBindTexture(GL_TEXTURE_2D, texture->name); - - switch(bitmap.getConfig()) { + + switch (bitmap.getConfig()) { case SkBitmap::kA8_Config: - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, - GL_ALPHA, GL_UNSIGNED_BYTE, p); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, + GL_UNSIGNED_BYTE, p); break; case SkBitmap::kARGB_4444_Config: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, p); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_SHORT_4_4_4_4, p); break; case SkBitmap::kARGB_8888_Config: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, - GL_RGBA, GL_UNSIGNED_BYTE, p); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, p); break; case SkBitmap::kRGB_565_Config: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, p); break; default: break; @@ -123,8 +121,7 @@ status_t BootAnimation::initTexture( return NO_ERROR; } -status_t BootAnimation::readyToRun() -{ +status_t BootAnimation::readyToRun() { mAssets.addDefaultAssets(); DisplayInfo dinfo; @@ -133,32 +130,27 @@ status_t BootAnimation::readyToRun() return -1; // create the native surface - sp s = session()->createSurface(getpid(), 0, - dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); + sp s = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h, + PIXEL_FORMAT_RGB_565); session()->openTransaction(); s->setLayer(0x40000000); session()->closeTransaction(); // initialize opengl and egl - const EGLint attribs[] = { - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, - EGL_DEPTH_SIZE, 0, - EGL_NONE - }; + const EGLint attribs[] = { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 0, EGL_NONE }; EGLint w, h, dummy; EGLint numConfigs; EGLConfig config; EGLSurface surface; EGLContext context; EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(display, NULL, NULL); eglChooseConfig(display, attribs, &config, 1, &numConfigs); - surface = eglCreateWindowSurface( - display, config, new EGLNativeWindowSurface(s), NULL); - + mNativeWindowSurface = new EGLNativeWindowSurface(s); + surface = eglCreateWindowSurface(display, config, + mNativeWindowSurface.get(), NULL); + context = eglCreateContext(display, config, NULL, NULL); eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); @@ -167,7 +159,7 @@ status_t BootAnimation::readyToRun() mContext = context; mSurface = surface; mWidth = w; - mHeight= h; + mHeight = h; mFlingerSurface = s; // initialize GL @@ -180,25 +172,21 @@ status_t BootAnimation::readyToRun() return NO_ERROR; } -void BootAnimation::requestExit() -{ +void BootAnimation::requestExit() { mBarrier.open(); Thread::requestExit(); } -bool BootAnimation::threadLoop() -{ +bool BootAnimation::threadLoop() { bool r = android(); - eglMakeCurrent(mDisplay, 0, 0, 0); - eglDestroyContext(mDisplay, mContext); + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(mDisplay, mContext); eglDestroySurface(mDisplay, mSurface); - eglTerminate(mDisplay); + mNativeWindowSurface.clear(); return r; } - -bool BootAnimation::android() -{ +bool BootAnimation::android() { initTexture(&mAndroid[0], mAssets, "images/android_320x480.png"); initTexture(&mAndroid[1], mAssets, "images/boot_robot.png"); initTexture(&mAndroid[2], mAssets, "images/boot_robot_glow.png"); @@ -219,9 +207,9 @@ bool BootAnimation::android() glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); const int steps = 8; - for (int i=1 ; isetSwapRectangle(updateRect.left, + updateRect.top, updateRect.width(), updateRect.height()); glEnable(GL_SCISSOR_TEST); - glScissor(updateRect.left, mHeight-updateRect.bottom, - updateRect.width(), updateRect.height()); + glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), + updateRect.height()); const nsecs_t startTime = systemTime(); - do - { + do { // glow speed and shape nsecs_t time = systemTime() - startTime; - float t = ((4.0f/(360.0f*us2ns(16667))) * time); + float t = ((4.0f / (360.0f * us2ns(16667))) * time); t = t - floorf(t); - const float fade = 0.5f + 0.5f*sinf(t * 2*M_PI); + const float fade = 0.5f + 0.5f * sinf(t * 2 * M_PI); // fade the glow in and out glDisable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[2].name); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(fade, fade, fade, fade); - glDrawTexiOES(updateRect.left, mHeight-updateRect.bottom, 0, + glDrawTexiOES(updateRect.left, mHeight - updateRect.bottom, 0, updateRect.width(), updateRect.height()); // draw the robot glEnable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[1].name); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glDrawTexiOES(updateRect.left, mHeight-updateRect.bottom, 0, + glDrawTexiOES(updateRect.left, mHeight - updateRect.bottom, 0, updateRect.width(), updateRect.height()); // make sure sleep a lot to not take too much CPU away from // the boot process. With this "glow" animation there is no // visible difference. - usleep(16667*4); + usleep(16667 * 4); eglSwapBuffers(mDisplay, mSurface); } while (!exitPending()); - - + glDeleteTextures(1, &mAndroid[0].name); glDeleteTextures(1, &mAndroid[1].name); glDeleteTextures(1, &mAndroid[2].name); return false; } - -bool BootAnimation::cylon() -{ +bool BootAnimation::cylon() { // initialize the textures... - initTexture(&mLeftTrail, mAssets, "images/cylon_left.png"); + initTexture(&mLeftTrail, mAssets, "images/cylon_left.png"); initTexture(&mRightTrail, mAssets, "images/cylon_right.png"); initTexture(&mBrightSpot, mAssets, "images/cylon_dot.png"); int w = mWidth; int h = mHeight; - const Point c(w/2 , h/2); + const Point c(w / 2, h / 2); const GLint amplitude = 60; - const int scx = c.x - amplitude - mBrightSpot.w/2; - const int scy = c.y - mBrightSpot.h/2; - const int scw = amplitude*2 + mBrightSpot.w; + const int scx = c.x - amplitude - mBrightSpot.w / 2; + const int scy = c.y - mBrightSpot.h / 2; + const int scw = amplitude * 2 + mBrightSpot.w; const int sch = mBrightSpot.h; - const Rect updateRect(scx, h-scy-sch, scx+scw, h-scy); + const Rect updateRect(scx, h - scy - sch, scx + scw, h - scy); // erase screen glDisable(GL_SCISSOR_TEST); @@ -314,33 +296,29 @@ bool BootAnimation::cylon() glClear(GL_COLOR_BUFFER_BIT); - eglSwapRectangleANDROID(mDisplay, mSurface, - updateRect.left, updateRect.top, - updateRect.width(), updateRect.height()); + mNativeWindowSurface->setSwapRectangle(updateRect.left, + updateRect.top, updateRect.width(), updateRect.height()); glEnable(GL_SCISSOR_TEST); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - // clear the screen to white Point p; float t = 0; float alpha = 1.0f; const nsecs_t startTime = systemTime(); nsecs_t fadeTime = 0; - - do - { + + do { // Set scissor in interesting area - glScissor(scx, scy, scw, sch); + glScissor(scx, scy, scw, sch); // erase screen glClear(GL_COLOR_BUFFER_BIT); - // compute wave - const float a = (t * 2*M_PI) - M_PI/2; + const float a = (t * 2 * M_PI) - M_PI / 2; const float sn = sinf(a); const float cs = cosf(a); GLint x = GLint(amplitude * sn); @@ -350,50 +328,50 @@ bool BootAnimation::cylon() if (derivative > 0) { // vanishing trail... - p.x = (-amplitude + c.x) - mBrightSpot.w/2; - p.y = c.y-mLeftTrail.h/2; - float fade = 2.0f*(0.5f-t); + p.x = (-amplitude + c.x) - mBrightSpot.w / 2; + p.y = c.y - mLeftTrail.h / 2; + float fade = 2.0f * (0.5f - t); //fade *= fade; glColor4f(fade, fade, fade, fade); glBindTexture(GL_TEXTURE_2D, mLeftTrail.name); glDrawTexiOES(p.x, p.y, 0, mLeftTrail.w, mLeftTrail.h); // trail... - p.x = (x + c.x) - (mRightTrail.w + mBrightSpot.w/2) + 16; - p.y = c.y-mRightTrail.h/2; - fade = t<0.25f ? t*4.0f : 1.0f; + p.x = (x + c.x) - (mRightTrail.w + mBrightSpot.w / 2) + 16; + p.y = c.y - mRightTrail.h / 2; + fade = t < 0.25f ? t * 4.0f : 1.0f; fade *= fade; glColor4f(fade, fade, fade, fade); glBindTexture(GL_TEXTURE_2D, mRightTrail.name); glDrawTexiOES(p.x, p.y, 0, mRightTrail.w, mRightTrail.h); - } else { + } else { // vanishing trail.. - p.x = (amplitude + c.x) - (mRightTrail.w + mBrightSpot.w/2) + 16; - p.y = c.y-mRightTrail.h/2; - float fade = 2.0f*(0.5f-(t-0.5f)); + p.x = (amplitude + c.x) - (mRightTrail.w + mBrightSpot.w / 2) + 16; + p.y = c.y - mRightTrail.h / 2; + float fade = 2.0f * (0.5f - (t - 0.5f)); //fade *= fade; glColor4f(fade, fade, fade, fade); glBindTexture(GL_TEXTURE_2D, mRightTrail.name); glDrawTexiOES(p.x, p.y, 0, mRightTrail.w, mRightTrail.h); // trail... - p.x = (x + c.x) - mBrightSpot.w/2; - p.y = c.y-mLeftTrail.h/2; - fade = t<0.5f+0.25f ? (t-0.5f)*4.0f : 1.0f; + p.x = (x + c.x) - mBrightSpot.w / 2; + p.y = c.y - mLeftTrail.h / 2; + fade = t < 0.5f + 0.25f ? (t - 0.5f) * 4.0f : 1.0f; fade *= fade; glColor4f(fade, fade, fade, fade); glBindTexture(GL_TEXTURE_2D, mLeftTrail.name); glDrawTexiOES(p.x, p.y, 0, mLeftTrail.w, mLeftTrail.h); } - const Point p( x + c.x-mBrightSpot.w/2, c.y-mBrightSpot.h/2 ); + const Point p(x + c.x - mBrightSpot.w / 2, c.y - mBrightSpot.h / 2); glBindTexture(GL_TEXTURE_2D, mBrightSpot.name); - glColor4f(1,0.5,0.5,1); + glColor4f(1, 0.5, 0.5, 1); glDrawTexiOES(p.x, p.y, 0, mBrightSpot.w, mBrightSpot.h); // update animation nsecs_t time = systemTime() - startTime; - t = ((4.0f/(360.0f*us2ns(16667))) * time); + t = ((4.0f / (360.0f * us2ns(16667))) * time); t = t - floorf(t); eglSwapBuffers(mDisplay, mSurface); @@ -406,7 +384,7 @@ bool BootAnimation::cylon() alpha = 1.0f - ((float(time) * 6.0f) / float(s2ns(1))); session()->openTransaction(); - mFlingerSurface->setAlpha(alpha*alpha); + mFlingerSurface->setAlpha(alpha * alpha); session()->closeTransaction(); } } while (alpha > 0); @@ -421,4 +399,5 @@ bool BootAnimation::cylon() // --------------------------------------------------------------------------- -}; // namespace android +} +; // namespace android diff --git a/libs/surfaceflinger/BootAnimation.h b/libs/surfaceflinger/BootAnimation.h index a4a6d49ded7a2..b20cea09a4911 100644 --- a/libs/surfaceflinger/BootAnimation.h +++ b/libs/surfaceflinger/BootAnimation.h @@ -26,7 +26,8 @@ #include #include -#include +#include +#include #include "Barrier.h" @@ -35,6 +36,7 @@ class SkBitmap; namespace android { class AssetManager; +class EGLNativeWindowSurface; // --------------------------------------------------------------------------- @@ -74,6 +76,7 @@ private: EGLDisplay mContext; EGLDisplay mSurface; sp mFlingerSurface; + sp mNativeWindowSurface; Barrier mBarrier; }; diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 92588fa0d134c..f14d7e99c2d3b 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -21,14 +21,16 @@ #include #include -#include - #include #include #include +#include +#include + + #include "DisplayHardware/DisplayHardware.h" #include @@ -136,26 +138,19 @@ void DisplayHardware::init(uint32_t dpy) const char* const egl_extensions = eglQueryString( display, EGL_EXTENSIONS); - const char* egl_extensions_config = egl_extensions; - - if (strstr(egl_extensions, "EGL_ANDROID_query_string_config")) { - egl_extensions_config = eglQueryStringConfigANDROID( - display, config, EGL_EXTENSIONS); - } - LOGI("EGL informations:"); LOGI("# of configs : %d", numConfigs); LOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); LOGI("version : %s", eglQueryString(display, EGL_VERSION)); LOGI("extensions: %s", egl_extensions); - LOGI("ext/config: %s", egl_extensions_config); LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); - if (strstr(egl_extensions_config, "EGL_ANDROID_swap_rectangle")) { - mFlags |= SWAP_RECTANGLE_EXTENSION; - // TODO: get the real "update_on_demand" behavior - mFlags |= UPDATE_ON_DEMAND; - } + // TODO: get this from the devfb driver (probably should be HAL module) + mFlags |= SWAP_RECTANGLE_EXTENSION; + + // TODO: get the real "update_on_demand" behavior (probably should be HAL module) + mFlags |= UPDATE_ON_DEMAND; + if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) { if (dummy == EGL_SLOW_CONFIG) mFlags |= SLOW_CONFIG; @@ -173,9 +168,6 @@ void DisplayHardware::init(uint32_t dpy) if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) { if (dummy == EGL_BUFFER_PRESERVED) { mFlags |= BUFFER_PRESERVED; - if (strstr(egl_extensions_config, "EGL_ANDROID_copy_front_to_back")) { - mFlags |= COPY_BACK_EXTENSION; - } } } @@ -330,8 +322,7 @@ void DisplayHardware::flip(const Region& dirty) const if (mFlags & SWAP_RECTANGLE_EXTENSION) { const Rect& b(newDirty.bounds()); - eglSwapRectangleANDROID( - dpy, surface, + mDisplaySurface->setSwapRectangle( b.left, b.top, b.width(), b.height()); } @@ -352,3 +343,11 @@ void DisplayHardware::makeCurrent() const { eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); } + +void DisplayHardware::copyFrontToImage(const copybit_image_t& front) const { + mDisplaySurface->copyFrontToImage(front); +} + +void DisplayHardware::copyBackToImage(const copybit_image_t& front) const { + mDisplaySurface->copyBackToImage(front); +} diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h index df97b60d98d06..550a4d12702c9 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -22,7 +22,7 @@ #include #include -#include +#include #include "DisplayHardware/DisplayHardwareBase.h" @@ -39,7 +39,6 @@ class DisplayHardware : public DisplayHardwareBase { public: enum { - COPY_BACK_EXTENSION = 0x00000001, DIRECT_TEXTURE = 0x00000002, SWAP_RECTANGLE_EXTENSION= 0x00000004, COPY_BITS_EXTENSION = 0x00000008, @@ -80,6 +79,9 @@ public: copybit_device_t* getBlitEngine() const { return mBlitEngine; } overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; } + void copyFrontToImage(const copybit_image_t& front) const; + void copyBackToImage(const copybit_image_t& front) const; + Rect bounds() const { return Rect(mWidth, mHeight); } diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index f65d66984d7f5..31e63ef2c6b16 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -186,9 +186,7 @@ void Layer::onDraw(const Region& clip) const copybit_device_t* copybit = mFlinger->getBlitEngine(); copybit->set_parameter(copybit, COPYBIT_TRANSFORM, getOrientation()); copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, - s.flags & ISurfaceComposer::eLayerDither ? - COPYBIT_ENABLE : COPYBIT_DISABLE); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); region_iterator it(clip); err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp index bdefba34e2337..9277a64daa4c4 100644 --- a/libs/surfaceflinger/LayerBase.cpp +++ b/libs/surfaceflinger/LayerBase.cpp @@ -23,6 +23,9 @@ #include #include +#include +#include + #include "clz.h" #include "LayerBase.h" #include "LayerBlur.h" @@ -111,6 +114,12 @@ void LayerBase::commitTransaction(bool skipSize) { mDrawingState.h = h; } } +void LayerBase::forceVisibilityTransaction() { + // this can be called without SurfaceFlinger.mStateLock, but if we + // can atomically increment the sequence number, it doesn't matter. + android_atomic_inc(&mCurrentState.sequence); + requestTransaction(); +} bool LayerBase::requestTransaction() { int32_t old = setTransactionFlags(eTransactionNeeded); return ((old & eTransactionNeeded) == 0); @@ -220,10 +229,15 @@ Point LayerBase::getPhysicalSize() const return Point(front.w, front.h); } +Transform LayerBase::getDrawingStateTransform() const +{ + return drawingState().transform; +} + void LayerBase::validateVisibility(const Transform& planeTransform) { const Layer::State& s(drawingState()); - const Transform tr(planeTransform * s.transform); + const Transform tr(planeTransform * getDrawingStateTransform()); const bool transformed = tr.transformed(); const Point size(getPhysicalSize()); @@ -350,6 +364,10 @@ void LayerBase::draw(const Region& inClip) const return; } } + + // reset GL state + glEnable(GL_SCISSOR_TEST); + onDraw(clip); /* @@ -391,6 +409,7 @@ void LayerBase::clearWithOpenGL(const Region& clip) const Rect r; Region::iterator iterator(clip); if (iterator) { + glEnable(GL_SCISSOR_TEST); glVertexPointer(2, GL_FIXED, 0, mVertices); while (iterator.iterate(&r)) { const GLint sy = fbHeight - (r.top + r.height()); diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h index 5e14dc85c7acc..2377a148650e7 100644 --- a/libs/surfaceflinger/LayerBase.h +++ b/libs/surfaceflinger/LayerBase.h @@ -86,8 +86,8 @@ public: uint32_t z; uint8_t alpha; uint8_t flags; - uint8_t sequence; // changes when visible regions can change - uint8_t reserved; + uint8_t reserved[2]; + int32_t sequence; // changes when visible regions can change uint32_t tint; Transform transform; Region transparentRegion; @@ -104,11 +104,11 @@ public: void commitTransaction(bool skipSize); bool requestTransaction(); - + void forceVisibilityTransaction(); + uint32_t getTransactionFlags(uint32_t flags); uint32_t setTransactionFlags(uint32_t flags); - void validateVisibility(const Transform& globalTransform); Rect visibleBounds() const; void drawRegion(const Region& reg) const; @@ -162,7 +162,19 @@ public: * the bitmap (as opposed to the size of the drawing state). */ virtual Point getPhysicalSize() const; - + + /** + * validateVisibility - cache a bunch of things + */ + virtual void validateVisibility(const Transform& globalTransform); + + /** + * getDrawingStateTransform - returns the drawing state's transform. + * This is used in validateVisibility() and can be use to override or + * modify the transform (if so make sure to trigger a transaction). + */ + virtual Transform getDrawingStateTransform() const; + /** * lockPageFlip - called each time the screen is redrawn and returns whether * the visible regions need to be recomputed (this is a fairly heavy @@ -320,8 +332,7 @@ public: *params = mParams; } - virtual status_t registerBuffers(int w, int h, int hstride, int vstride, - PixelFormat format, const sp& heap) + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers) { return INVALID_OPERATION; } virtual void postBuffer(ssize_t offset) { } virtual void unregisterBuffers() { }; diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp index 7c98857fc2836..e84435088a7c4 100644 --- a/libs/surfaceflinger/LayerBitmap.cpp +++ b/libs/surfaceflinger/LayerBitmap.cpp @@ -69,18 +69,15 @@ status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment, return NO_ERROR; } + PixelFormatInfo info; + getPixelFormatInfo(format, &info); + uint32_t allocFlags = MemoryDealer::PAGE_ALIGNED; const uint32_t align = 4; // must match GL_UNPACK_ALIGNMENT - const uint32_t Bpp = bytesPerPixel(format); + const uint32_t Bpp = info.bytesPerPixel; uint32_t stride = (w + (alignment-1)) & ~(alignment-1); stride = ((stride * Bpp + (align-1)) & ~(align-1)) / Bpp; - size_t size = stride * h * Bpp; - if (format == PIXEL_FORMAT_YCbCr_422_SP || - format == PIXEL_FORMAT_YCbCr_420_SP) { - // in YUV planar, bitsPerPixel is for the Y plane - size = (size * bitsPerPixel(format)) / 8; - } - + size_t size = info.getScanlineSize(stride) * h; if (allocFlags & MemoryDealer::PAGE_ALIGNED) { size_t pagesize = getpagesize(); size = (size + (pagesize-1)) & ~(pagesize-1); diff --git a/libs/surfaceflinger/LayerBitmap.h b/libs/surfaceflinger/LayerBitmap.h index 4c2eb50df5f58..9ad64c40becf9 100644 --- a/libs/surfaceflinger/LayerBitmap.h +++ b/libs/surfaceflinger/LayerBitmap.h @@ -70,9 +70,6 @@ public: void getBitmapSurface(copybit_image_t* img) const; private: - LayerBitmap(const LayerBitmap& rhs); - LayerBitmap& operator = (const LayerBitmap& rhs); - sp mAllocator; sp mBitsMemory; uint32_t mAllocFlags; diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp index efadbcf4c645b..d3e456f1bf8f5 100644 --- a/libs/surfaceflinger/LayerBlur.cpp +++ b/libs/surfaceflinger/LayerBlur.cpp @@ -23,6 +23,9 @@ #include #include +#include +#include + #include "BlurFilter.h" #include "LayerBlur.h" #include "SurfaceFlinger.h" diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index c9cebf4c9164a..fc0a603f7f507 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -25,6 +25,9 @@ #include #include +#include +#include + #include #include @@ -100,6 +103,15 @@ void LayerBuffer::unregisterBuffers() source->unregisterBuffers(); } +Transform LayerBuffer::getDrawingStateTransform() const +{ + Transform tr(LayerBaseClient::getDrawingStateTransform()); + sp source(getSource()); + if (source != 0) + source->updateTransform(&tr); + return tr; +} + uint32_t LayerBuffer::doTransaction(uint32_t flags) { sp source(getSource()); @@ -132,15 +144,13 @@ void LayerBuffer::onDraw(const Region& clip) const /** * This creates a "buffer" source for this surface */ -status_t LayerBuffer::registerBuffers(int w, int h, int hstride, int vstride, - PixelFormat format, const sp& memoryHeap) +status_t LayerBuffer::registerBuffers(const ISurface::BufferHeap& buffers) { Mutex::Autolock _l(mLock); if (mSource != 0) return INVALID_OPERATION; - sp source = new BufferSource(*this, w, h, - hstride, vstride, format, memoryHeap); + sp source = new BufferSource(*this, buffers); status_t result = source->getStatus(); if (result == NO_ERROR) { @@ -194,13 +204,39 @@ LayerBuffer::SurfaceBuffer::~SurfaceBuffer() mOwner = 0; } -status_t LayerBuffer::SurfaceBuffer::registerBuffers( - int w, int h, int hs, int vs, - PixelFormat format, const sp& heap) +status_t LayerBuffer::SurfaceBuffer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case REGISTER_BUFFERS: + case UNREGISTER_BUFFERS: + case CREATE_OVERLAY: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int self_pid = getpid(); + if (LIKELY(pid != self_pid)) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.ACCESS_SURFACE_FLINGER"))) + { + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + } + } + return LayerBaseClient::Surface::onTransact(code, data, reply, flags); +} + +status_t LayerBuffer::SurfaceBuffer::registerBuffers(const ISurface::BufferHeap& buffers) { LayerBuffer* owner(getOwner()); if (owner) - return owner->registerBuffers(w, h, hs, vs, format, heap); + return owner->registerBuffers(buffers); return NO_INIT; } @@ -237,23 +273,20 @@ void LayerBuffer::SurfaceBuffer::disown() // LayerBuffer::Buffer // ============================================================================ -LayerBuffer::Buffer::Buffer(const sp& heap, ssize_t offset, - int w, int h, int hs, int vs, int f) -: mHeap(heap) +LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset) + : mBufferHeap(buffers) { NativeBuffer& src(mNativeBuffer); src.crop.l = 0; src.crop.t = 0; - src.crop.r = w; - src.crop.b = h; - src.img.w = hs ?: w; - src.img.h = vs ?: h; - src.img.format = f; + src.crop.r = buffers.w; + src.crop.b = buffers.h; + src.img.w = buffers.hor_stride ?: buffers.w; + src.img.h = buffers.ver_stride ?: buffers.h; + src.img.format = buffers.format; src.img.offset = offset; - src.img.base = heap->base(); - src.img.fd = heap->heapID(); - // FIXME: make sure this buffer lies within the heap, in which case, set - // mHeap to null + src.img.base = buffers.heap->base(); + src.img.fd = buffers.heap->heapID(); } LayerBuffer::Buffer::~Buffer() @@ -283,41 +316,53 @@ void LayerBuffer::Source::postBuffer(ssize_t offset) { } void LayerBuffer::Source::unregisterBuffers() { } +void LayerBuffer::Source::updateTransform(Transform* tr) const { +} // --------------------------------------------------------------------------- LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, - int w, int h, int hstride, int vstride, - PixelFormat format, const sp& memoryHeap) - : Source(layer), mStatus(NO_ERROR), mTextureName(-1U) + const ISurface::BufferHeap& buffers) + : Source(layer), mStatus(NO_ERROR), + mBufferSize(0), mTextureName(-1U) { - if (memoryHeap == NULL) { + if (buffers.heap == NULL) { // this is allowed, but in this case, it is illegal to receive // postBuffer(). The surface just erases the framebuffer with // fully transparent pixels. - mHeap.clear(); - mWidth = w; - mHeight = h; + mBufferHeap = buffers; mLayer.setNeedsBlending(false); return; } - status_t err = (memoryHeap->heapID() >= 0) ? NO_ERROR : NO_INIT; + status_t err = (buffers.heap->heapID() >= 0) ? NO_ERROR : NO_INIT; if (err != NO_ERROR) { + LOGE("LayerBuffer::BufferSource: invalid heap (%s)", strerror(err)); + mStatus = err; + return; + } + + PixelFormatInfo info; + err = getPixelFormatInfo(buffers.format, &info); + if (err != NO_ERROR) { + LOGE("LayerBuffer::BufferSource: invalid format %d (%s)", + buffers.format, strerror(err)); mStatus = err; return; } - // TODO: validate format/parameters - mHeap = memoryHeap; - mWidth = w; - mHeight = h; - mHStride = hstride; - mVStride = vstride; - mFormat = format; - PixelFormatInfo info; - getPixelFormatInfo(format, &info); - mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0); + if (buffers.hor_stride<0 || buffers.ver_stride<0) { + LOGE("LayerBuffer::BufferSource: invalid parameters " + "(w=%d, h=%d, xs=%d, ys=%d)", + buffers.w, buffers.h, buffers.hor_stride, buffers.ver_stride); + mStatus = BAD_VALUE; + return; + } + + mBufferHeap = buffers; + mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0); + mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride; + mLayer.forceVisibilityTransaction(); } LayerBuffer::BufferSource::~BufferSource() @@ -329,21 +374,24 @@ LayerBuffer::BufferSource::~BufferSource() void LayerBuffer::BufferSource::postBuffer(ssize_t offset) { - sp heap; - int w, h, hs, vs, f; + ISurface::BufferHeap buffers; { // scope for the lock Mutex::Autolock _l(mLock); - w = mWidth; - h = mHeight; - hs= mHStride; - vs= mVStride; - f = mFormat; - heap = mHeap; + buffers = mBufferHeap; + if (buffers.heap != 0) { + const size_t memorySize = buffers.heap->getSize(); + if ((size_t(offset) + mBufferSize) > memorySize) { + LOGE("LayerBuffer::BufferSource::postBuffer() " + "invalid buffer (offset=%d, size=%d, heap-size=%d", + int(offset), int(mBufferSize), int(memorySize)); + return; + } + } } sp buffer; - if (heap != 0) { - buffer = new LayerBuffer::Buffer(heap, offset, w, h, hs, vs, f); + if (buffers.heap != 0) { + buffer = new LayerBuffer::Buffer(buffers, offset); if (buffer->getStatus() != NO_ERROR) buffer.clear(); setBuffer(buffer); @@ -354,7 +402,7 @@ void LayerBuffer::BufferSource::postBuffer(ssize_t offset) void LayerBuffer::BufferSource::unregisterBuffers() { Mutex::Autolock _l(mLock); - mHeap.clear(); + mBufferHeap.heap.clear(); mBuffer.clear(); mLayer.invalidate(); } @@ -371,6 +419,17 @@ void LayerBuffer::BufferSource::setBuffer(const sp& buffer) mBuffer = buffer; } +void LayerBuffer::BufferSource::updateTransform(Transform* tr) const +{ + uint32_t bufTransform = mBufferHeap.transform; + // TODO: handle all transforms + if (bufTransform == ISurface::BufferHeap::ROT_90) { + Transform rot90; + rot90.set(0, -1, 1, 0); + *tr = (*tr) * rot90; + } +} + void LayerBuffer::BufferSource::onDraw(const Region& clip) const { sp buffer(getBuffer()); @@ -452,11 +511,13 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const region_iterator it(clip); copybit->set_parameter(copybit, COPYBIT_TRANSFORM, mLayer.getOrientation()); copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, - s.flags & ISurfaceComposer::eLayerDither ? - COPYBIT_ENABLE : COPYBIT_DISABLE); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + err = copybit->stretch(copybit, &dst, &src.img, &drect, &src.crop, &it); + if (err != NO_ERROR) { + LOGE("copybit failed (%s)", strerror(err)); + } } if (!can_use_copybit || err) { diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index 6e3d49f05373a..5532532f044f5 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -22,7 +22,7 @@ #include #include -#include +#include #include "LayerBase.h" #include "LayerBitmap.h" @@ -46,6 +46,7 @@ class LayerBuffer : public LayerBaseClient virtual void onVisibilityResolved(const Transform& planeTransform); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); + virtual void updateTransform(Transform* tr) const; protected: LayerBuffer& mLayer; }; @@ -67,9 +68,9 @@ public: virtual void onDraw(const Region& clip) const; virtual uint32_t doTransaction(uint32_t flags); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + virtual Transform getDrawingStateTransform() const; - status_t registerBuffers(int w, int h, int hstride, int vstride, - PixelFormat format, const sp& heap); + status_t registerBuffers(const ISurface::BufferHeap& buffers); void postBuffer(ssize_t offset); void unregisterBuffers(); sp createOverlay(uint32_t w, uint32_t h, int32_t format); @@ -89,10 +90,9 @@ private: class Buffer : public LightRefBase { public: - Buffer(const sp& heap, ssize_t offset, - int w, int h, int hs, int vs, int f); + Buffer(const ISurface::BufferHeap& buffers, ssize_t offset); inline status_t getStatus() const { - return mHeap!=0 ? NO_ERROR : NO_INIT; + return mBufferHeap.heap!=0 ? NO_ERROR : NO_INIT; } inline const NativeBuffer& getBuffer() const { return mNativeBuffer; @@ -103,34 +103,29 @@ private: Buffer(const Buffer& rhs); ~Buffer(); private: - sp mHeap; - NativeBuffer mNativeBuffer; + ISurface::BufferHeap mBufferHeap; + NativeBuffer mNativeBuffer; }; class BufferSource : public Source { public: - BufferSource(LayerBuffer& layer, - int w, int h, int hstride, int vstride, - PixelFormat format, const sp& heap); + BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers); virtual ~BufferSource(); status_t getStatus() const { return mStatus; } sp getBuffer() const; void setBuffer(const sp& buffer); + virtual void updateTransform(Transform* tr) const; virtual void onDraw(const Region& clip) const; virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); private: mutable Mutex mLock; - sp mHeap; sp mBuffer; status_t mStatus; - int mWidth; - int mHeight; - int mHStride; - int mVStride; - int mFormat; + ISurface::BufferHeap mBufferHeap; + size_t mBufferSize; mutable sp mTemporaryDealer; mutable LayerBitmap mTempBitmap; mutable GLuint mTextureName; @@ -186,8 +181,9 @@ private: public: SurfaceBuffer(SurfaceID id, LayerBuffer* owner); virtual ~SurfaceBuffer(); - virtual status_t registerBuffers(int w, int h, int hstride, int vstride, - PixelFormat format, const sp& heap); + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); virtual sp createOverlay( diff --git a/libs/surfaceflinger/LayerOrientationAnim.cpp b/libs/surfaceflinger/LayerOrientationAnim.cpp new file mode 100644 index 0000000000000..46b3b196c72e8 --- /dev/null +++ b/libs/surfaceflinger/LayerOrientationAnim.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include +#include + +#include + +#include + +#include "LayerBase.h" +#include "LayerOrientationAnim.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" +#include "OrientationAnimation.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerOrientationAnim::typeInfo = LayerBase::typeInfo | 0x80; +const char* const LayerOrientationAnim::typeID = "LayerOrientationAnim"; + +// --------------------------------------------------------------------------- + +LayerOrientationAnim::LayerOrientationAnim( + SurfaceFlinger* flinger, DisplayID display, + OrientationAnimation* anim, + const LayerBitmap& bitmap, + const LayerBitmap& bitmapIn) + : LayerBase(flinger, display), mAnim(anim), + mBitmap(bitmap), mBitmapIn(bitmapIn), + mTextureName(-1), mTextureNameIn(-1) +{ + mStartTime = systemTime(); + mFinishTime = 0; + mOrientationCompleted = false; + mFirstRedraw = false; + mLastNormalizedTime = 0; + mLastScale = 0; + mNeedsBlending = false; +} + +LayerOrientationAnim::~LayerOrientationAnim() +{ + if (mTextureName != -1U) { + LayerBase::deletedTextures.add(mTextureName); + } + if (mTextureNameIn != -1U) { + LayerBase::deletedTextures.add(mTextureNameIn); + } +} + +bool LayerOrientationAnim::needsBlending() const +{ + return mNeedsBlending; +} + +Point LayerOrientationAnim::getPhysicalSize() const +{ + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + return Point(hw.getWidth(), hw.getHeight()); +} + +void LayerOrientationAnim::validateVisibility(const Transform&) +{ + const Layer::State& s(drawingState()); + const Transform tr(getDrawingStateTransform()); + const Point size(getPhysicalSize()); + uint32_t w = size.x; + uint32_t h = size.y; + mTransformedBounds = tr.makeBounds(w, h); + mLeft = tr.tx(); + mTop = tr.ty(); + transparentRegionScreen.clear(); + mTransformed = true; + mCanUseCopyBit = false; + copybit_device_t* copybit = mFlinger->getBlitEngine(); + if (copybit) { + mCanUseCopyBit = true; + } +} + +void LayerOrientationAnim::onOrientationCompleted() +{ + mFinishTime = systemTime(); + mOrientationCompleted = true; + mFirstRedraw = true; + mNeedsBlending = true; + mFlinger->invalidateLayerVisibility(this); +} + +void LayerOrientationAnim::onDraw(const Region& clip) const +{ + // Animation... + const float MIN_SCALE = 0.5f; + const float DURATION = ms2ns(200); + const float BOUNCES_PER_SECOND = 1.618f; + const float BOUNCES_AMPLITUDE = 1.0f/32.0f; + + const nsecs_t now = systemTime(); + float scale, alpha; + + if (mOrientationCompleted) { + if (mFirstRedraw) { + mFirstRedraw = false; + + // make a copy of what's on screen + copybit_image_t image; + mBitmapIn.getBitmapSurface(&image); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + hw.copyBackToImage(image); + + // and erase the screen for this round + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + + // FIXME: code below is gross + mNeedsBlending = false; + LayerOrientationAnim* self(const_cast(this)); + mFlinger->invalidateLayerVisibility(self); + } + + // make sure pick-up where we left off + const float duration = DURATION * mLastNormalizedTime; + const float normalizedTime = (float(now - mFinishTime) / duration); + if (normalizedTime <= 1.0f) { + const float squaredTime = normalizedTime*normalizedTime; + scale = (1.0f - mLastScale)*squaredTime + mLastScale; + alpha = (1.0f - normalizedTime); + alpha *= alpha; + alpha *= alpha; + } else { + mAnim->onAnimationFinished(); + scale = 1.0f; + alpha = 0.0f; + } + } else { + const float normalizedTime = float(now - mStartTime) / DURATION; + if (normalizedTime <= 1.0f) { + mLastNormalizedTime = normalizedTime; + const float squaredTime = normalizedTime*normalizedTime; + scale = (MIN_SCALE-1.0f)*squaredTime + 1.0f; + alpha = 1.0f; + } else { + mLastNormalizedTime = 1.0f; + const float to_seconds = DURATION / seconds(1); + const float phi = BOUNCES_PER_SECOND * + (((normalizedTime - 1.0f) * to_seconds)*M_PI*2); + scale = MIN_SCALE + BOUNCES_AMPLITUDE * (1.0f - cosf(phi)); + alpha = 1.0f; + } + mLastScale = scale; + } + drawScaled(scale, alpha); +} + +void LayerOrientationAnim::drawScaled(float f, float alpha) const +{ + copybit_image_t dst; + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + hw.getDisplaySurface(&dst); + + // clear screen + // TODO: with update on demand, we may be able + // to not erase the screen at all during the animation + if (!mOrientationCompleted) { + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + } + + const int w = dst.w*f; + const int h = dst.h*f; + const int xc = uint32_t(dst.w-w)/2; + const int yc = uint32_t(dst.h-h)/2; + const copybit_rect_t drect = { xc, yc, xc+w, yc+h }; + + copybit_image_t src; + mBitmap.getBitmapSurface(&src); + const copybit_rect_t srect = { 0, 0, src.w, src.h }; + + int err = NO_ERROR; + const int can_use_copybit = canUseCopybit(); + if (can_use_copybit) { + copybit_device_t* copybit = mFlinger->getBlitEngine(); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + + if (alpha < 1.0f) { + copybit_image_t srcIn; + mBitmapIn.getBitmapSurface(&srcIn); + region_iterator it(Region(Rect( drect.l, drect.t, drect.r, drect.b ))); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + err = copybit->stretch(copybit, &dst, &srcIn, &drect, &srect, &it); + } + + if (!err && alpha > 0.0f) { + region_iterator it(Region(Rect( drect.l, drect.t, drect.r, drect.b ))); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, int(alpha*255)); + err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + } + LOGE_IF(err != NO_ERROR, "copybit failed (%s)", strerror(err)); + } + if (!can_use_copybit || err) { + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = src.w; + t.height = src.h; + t.stride = src.w; + t.vstride= src.h; + t.format = src.format; + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + + Transform tr; + tr.set(f,0,0,f); + tr.set(xc, yc); + + // FIXME: we should not access mVertices and mDrawingState like that, + // but since we control the animation, we know it's going to work okay. + // eventually we'd need a more formal way of doing things like this. + LayerOrientationAnim& self(const_cast(*this)); + tr.transform(self.mVertices[0], 0, 0); + tr.transform(self.mVertices[1], 0, src.h); + tr.transform(self.mVertices[2], src.w, src.h); + tr.transform(self.mVertices[3], src.w, 0); + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // Too slow to do this in software + self.mDrawingState.flags |= ISurfaceComposer::eLayerFilter; + } + + if (alpha < 1.0f) { + copybit_image_t src; + mBitmapIn.getBitmapSurface(&src); + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + if (UNLIKELY(mTextureNameIn == -1LU)) { + mTextureNameIn = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureNameIn, t, w, h); + } + self.mDrawingState.alpha = 255; + const Region clip(Rect( drect.l, drect.t, drect.r, drect.b )); + drawWithOpenGL(clip, mTextureName, t); + } + + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + if (UNLIKELY(mTextureName == -1LU)) { + mTextureName = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureName, t, w, h); + } + self.mDrawingState.alpha = int(alpha*255); + const Region clip(Rect( drect.l, drect.t, drect.r, drect.b )); + drawWithOpenGL(clip, mTextureName, t); + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerOrientationAnim.h b/libs/surfaceflinger/LayerOrientationAnim.h new file mode 100644 index 0000000000000..73676859bc035 --- /dev/null +++ b/libs/surfaceflinger/LayerOrientationAnim.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_LAYER_ORIENTATION_ANIM_H +#define ANDROID_LAYER_ORIENTATION_ANIM_H + +#include +#include +#include +#include + +#include "LayerBase.h" +#include "LayerBitmap.h" + +namespace android { + +// --------------------------------------------------------------------------- +class OrientationAnimation; + +class LayerOrientationAnim : public LayerBase +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerOrientationAnim(SurfaceFlinger* flinger, DisplayID display, + OrientationAnimation* anim, + const LayerBitmap& zoomOut, + const LayerBitmap& zoomIn); + virtual ~LayerOrientationAnim(); + + void onOrientationCompleted(); + + virtual void onDraw(const Region& clip) const; + virtual Point getPhysicalSize() const; + virtual void validateVisibility(const Transform& globalTransform); + virtual bool needsBlending() const; + virtual bool isSecure() const { return false; } +private: + void drawScaled(float scale, float alpha) const; + + OrientationAnimation* mAnim; + LayerBitmap mBitmap; + LayerBitmap mBitmapIn; + nsecs_t mStartTime; + nsecs_t mFinishTime; + bool mOrientationCompleted; + mutable bool mFirstRedraw; + mutable float mLastNormalizedTime; + mutable float mLastScale; + mutable GLuint mTextureName; + mutable GLuint mTextureNameIn; + mutable bool mNeedsBlending; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_ORIENTATION_ANIM_H diff --git a/libs/surfaceflinger/LayerScreenshot.cpp b/libs/surfaceflinger/LayerScreenshot.cpp index 3e7132b731953..40c47b0b7d87f 100644 --- a/libs/surfaceflinger/LayerScreenshot.cpp +++ b/libs/surfaceflinger/LayerScreenshot.cpp @@ -35,7 +35,7 @@ namespace android { // --------------------------------------------------------------------------- -const uint32_t LayerScreenshot::typeInfo = LayerBase::typeInfo | 0x20; +const uint32_t LayerScreenshot::typeInfo = LayerBase::typeInfo | 0x40; const char* const LayerScreenshot::typeID = "LayerScreenshot"; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/OrientationAnimation.cpp b/libs/surfaceflinger/OrientationAnimation.cpp new file mode 100644 index 0000000000000..f6f1326b1003b --- /dev/null +++ b/libs/surfaceflinger/OrientationAnimation.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include "LayerOrientationAnim.h" +#include "OrientationAnimation.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" + +#include "DisplayHardware/DisplayHardware.h" + +namespace android { + +// --------------------------------------------------------------------------- + +OrientationAnimation::OrientationAnimation(const sp& flinger) + : mFlinger(flinger), mLayerOrientationAnim(NULL), mState(DONE) +{ + // allocate a memory-dealer for this the first time + mTemporaryDealer = mFlinger->getSurfaceHeapManager()->createHeap( + ISurfaceComposer::eHardware); +} + +OrientationAnimation::~OrientationAnimation() +{ +} + +void OrientationAnimation::onOrientationChanged() +{ + if (mState == DONE) + mState = PREPARE; +} + +void OrientationAnimation::onAnimationFinished() +{ + if (mState != DONE) + mState = FINISH; +} + +bool OrientationAnimation::run_impl() +{ + bool skip_frame; + switch (mState) { + default: + case DONE: + skip_frame = done(); + break; + case PREPARE: + skip_frame = prepare(); + break; + case PHASE1: + skip_frame = phase1(); + break; + case PHASE2: + skip_frame = phase2(); + break; + case FINISH: + skip_frame = finished(); + break; + } + return skip_frame; +} + +bool OrientationAnimation::done() +{ + if (mFlinger->isFrozen()) { + // we are not allowed to draw, but pause a bit to make sure + // apps don't end up using the whole CPU, if they depend on + // surfaceflinger for synchronization. + usleep(8333); // 8.3ms ~ 120fps + return true; + } + return false; +} + +bool OrientationAnimation::prepare() +{ + mState = PHASE1; + + const GraphicPlane& plane(mFlinger->graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + const uint32_t w = hw.getWidth(); + const uint32_t h = hw.getHeight(); + + LayerBitmap bitmap; + bitmap.init(mTemporaryDealer); + bitmap.setBits(w, h, 1, hw.getFormat()); + + LayerBitmap bitmapIn; + bitmapIn.init(mTemporaryDealer); + bitmapIn.setBits(w, h, 1, hw.getFormat()); + + copybit_image_t front; + bitmap.getBitmapSurface(&front); + hw.copyFrontToImage(front); + + LayerOrientationAnim* l = new LayerOrientationAnim( + mFlinger.get(), 0, this, bitmap, bitmapIn); + l->initStates(w, h, 0); + l->setLayer(INT_MAX-1); + mFlinger->addLayer(l); + mLayerOrientationAnim = l; + return true; +} + +bool OrientationAnimation::phase1() +{ + if (mFlinger->isFrozen() == false) { + // start phase 2 + mState = PHASE2; + mLayerOrientationAnim->onOrientationCompleted(); + mLayerOrientationAnim->invalidate(); + return true; + + } + mLayerOrientationAnim->invalidate(); + return false; +} + +bool OrientationAnimation::phase2() +{ + // do the 2nd phase of the animation + mLayerOrientationAnim->invalidate(); + return false; +} + +bool OrientationAnimation::finished() +{ + mState = DONE; + mFlinger->removeLayer(mLayerOrientationAnim); + mLayerOrientationAnim = NULL; + return true; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/OrientationAnimation.h b/libs/surfaceflinger/OrientationAnimation.h new file mode 100644 index 0000000000000..ba33fcedd4055 --- /dev/null +++ b/libs/surfaceflinger/OrientationAnimation.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_ORIENTATION_ANIMATION_H +#define ANDROID_ORIENTATION_ANIMATION_H + +#include +#include + +#include "SurfaceFlinger.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class SurfaceFlinger; +class MemoryDealer; +class LayerOrientationAnim; + +class OrientationAnimation +{ +public: + OrientationAnimation(const sp& flinger); + virtual ~OrientationAnimation(); + + void onOrientationChanged(); + void onAnimationFinished(); + inline bool run() { + if (LIKELY(mState == DONE)) + return false; + return run_impl(); + } + +private: + enum { + DONE = 0, + PREPARE, + PHASE1, + PHASE2, + FINISH + }; + + bool run_impl(); + bool done(); + bool prepare(); + bool phase1(); + bool phase2(); + bool finished(); + + sp mFlinger; + sp mTemporaryDealer; + LayerOrientationAnim* mLayerOrientationAnim; + int mState; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ORIENTATION_ANIMATION_H diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 4c719e8d9b9a1..4e457c9c258f5 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -52,7 +52,9 @@ #include "LayerBuffer.h" #include "LayerDim.h" #include "LayerBitmap.h" +#include "LayerOrientationAnim.h" #include "LayerScreenshot.h" +#include "OrientationAnimation.h" #include "SurfaceFlinger.h" #include "RFBServer.h" #include "VRamHeap.h" @@ -224,6 +226,7 @@ void SurfaceFlinger::init() SurfaceFlinger::~SurfaceFlinger() { glDeleteTextures(1, &mWormholeTexName); + delete mOrientationAnimation; } copybit_device_t* SurfaceFlinger::getBlitEngine() const @@ -447,6 +450,8 @@ status_t SurfaceFlinger::readyToRun() * We're now ready to accept clients... */ + mOrientationAnimation = new OrientationAnimation(this); + // start CPU gauge display if (mDebugCpu) mCpuGauge = new CPUGauge(this, ms2ns(500)); @@ -471,8 +476,8 @@ void SurfaceFlinger::waitForEvent() { // wait for something to do if (UNLIKELY(isFrozen())) { - // wait 2 seconds - int err = mSyncObject.wait(ms2ns(3000)); + // wait 5 seconds + int err = mSyncObject.wait(ms2ns(5000)); if (err != NO_ERROR) { if (isFrozen()) { // we timed out and are still frozen @@ -555,11 +560,8 @@ bool SurfaceFlinger::threadLoop() void SurfaceFlinger::postFramebuffer() { - if (UNLIKELY(isFrozen())) { - // we are not allowed to draw, but pause a bit to make sure - // apps don't end up using the whole CPU, if they depend on - // surfaceflinger for synchronization. - usleep(8333); // 8.3ms ~ 120fps + const bool skip = mOrientationAnimation->run(); + if (UNLIKELY(skip)) { return; } @@ -684,6 +686,8 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) mVisibleRegionsDirty = true; mDirtyRegion.set(hw.bounds()); + + mOrientationAnimation->onOrientationChanged(); } if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) { @@ -874,19 +878,9 @@ void SurfaceFlinger::handleRepaint() uint32_t flags = hw.getFlags(); if (flags & DisplayHardware::BUFFER_PRESERVED) { - if (flags & DisplayHardware::COPY_BACK_EXTENSION) { - // yay. nothing to do here. - } else { - if (flags & DisplayHardware::UPDATE_ON_DEMAND) { - // we need to fully redraw the part that will be updated - mDirtyRegion.set(mInvalidRegion.bounds()); - } else { - // TODO: we only need te redraw the part that had been drawn - // the round before and is not drawn now - } - } + // here we assume DisplayHardware::flip()'s implementation + // performs the copy-back optimization. } else { - // COPY_BACK_EXTENSION makes no sense here if (flags & DisplayHardware::UPDATE_ON_DEMAND) { // we need to fully redraw the part that will be updated mDirtyRegion.set(mInvalidRegion.bounds()); @@ -1076,6 +1070,29 @@ void SurfaceFlinger::debugShowFPS() const // XXX: mFPS has the value we want } +status_t SurfaceFlinger::addLayer(LayerBase* layer) +{ + Mutex::Autolock _l(mStateLock); + addLayer_l(layer); + setTransactionFlags(eTransactionNeeded|eTraversalNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::removeLayer(LayerBase* layer) +{ + Mutex::Autolock _l(mStateLock); + removeLayer_l(layer); + setTransactionFlags(eTransactionNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::invalidateLayerVisibility(LayerBase* layer) +{ + layer->forceVisibilityTransaction(); + setTransactionFlags(eTraversalNeeded); + return NO_ERROR; +} + status_t SurfaceFlinger::addLayer_l(LayerBase* layer) { ssize_t i = mCurrentState.layersSortedByZ.add( diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index a242f1aa01862..8e5fd885d5a5a 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -35,11 +35,11 @@ #include #include +#include "Barrier.h" +#include "BootAnimation.h" +#include "CPUGauge.h" #include "Layer.h" #include "Tokenizer.h" -#include "CPUGauge.h" -#include "BootAnimation.h" -#include "Barrier.h" struct copybit_device_t; struct overlay_device_t; @@ -48,16 +48,18 @@ namespace android { // --------------------------------------------------------------------------- -class BClient; class Client; +class BClient; class DisplayHardware; +class FreezeLock; class GPUHardwareInterface; class IGPUCallback; class Layer; class LayerBuffer; +class LayerOrientationAnim; +class OrientationAnimation; class RFBServer; class SurfaceHeapManager; -class FreezeLock; typedef int32_t ClientID; @@ -181,7 +183,12 @@ public: copybit_device_t* getBlitEngine() const; overlay_control_device_t* getOverlayEngine() const; + + status_t removeLayer(LayerBase* layer); + status_t addLayer(LayerBase* layer); + status_t invalidateLayerVisibility(LayerBase* layer); + private: friend class BClient; friend class LayerBase; @@ -352,6 +359,8 @@ private: bool mFreezeDisplay; int32_t mFreezeCount; nsecs_t mFreezeDisplayTime; + friend class OrientationAnimation; + OrientationAnimation* mOrientationAnimation; // access protected by mDebugLock mutable Mutex mDebugLock; diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/VRamHeap.cpp index 77bc5766be0a3..0ccd71f0b8446 100644 --- a/libs/surfaceflinger/VRamHeap.cpp +++ b/libs/surfaceflinger/VRamHeap.cpp @@ -35,8 +35,6 @@ #include #include -#include - #include "GPUHardware/GPUHardware.h" #include "SurfaceFlinger.h" #include "VRamHeap.h" diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp index 50c60084adf7f..6c60b853eac65 100644 --- a/libs/ui/Camera.cpp +++ b/libs/ui/Camera.cpp @@ -84,8 +84,10 @@ void Camera::init() mRawCallbackCookie = 0; mJpegCallback = 0; mJpegCallbackCookie = 0; - mFrameCallback = 0; - mFrameCallbackCookie = 0; + mPreviewCallback = 0; + mPreviewCallbackCookie = 0; + mRecordingCallback = 0; + mRecordingCallbackCookie = 0; mErrorCallback = 0; mErrorCallbackCookie = 0; mAutoFocusCallback = 0; @@ -184,6 +186,15 @@ status_t Camera::startPreview() return c->startPreview(); } +// start recording mode, must call setPreviewDisplay first +status_t Camera::startRecording() +{ + LOGV("startRecording"); + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->startRecording(); +} + // stop preview mode void Camera::stopPreview() { @@ -193,6 +204,24 @@ void Camera::stopPreview() c->stopPreview(); } +// stop recording mode +void Camera::stopRecording() +{ + LOGV("stopRecording"); + sp c = mCamera; + if (c == 0) return; + c->stopRecording(); +} + +// release a recording frame +void Camera::releaseRecordingFrame(const sp& mem) +{ + LOGV("releaseRecordingFrame"); + sp c = mCamera; + if (c == 0) return; + c->releaseRecordingFrame(mem); +} + // get preview state bool Camera::previewEnabled() { @@ -202,6 +231,15 @@ bool Camera::previewEnabled() return c->previewEnabled(); } +// get recording state +bool Camera::recordingEnabled() +{ + LOGV("recordingEnabled"); + sp c = mCamera; + if (c == 0) return false; + return c->recordingEnabled(); +} + status_t Camera::autoFocus() { LOGV("autoFocus"); @@ -266,14 +304,21 @@ void Camera::setJpegCallback(frame_callback cb, void *cookie) mJpegCallbackCookie = cookie; } -void Camera::setFrameCallback(frame_callback cb, void *cookie, int frame_callback_flag) +void Camera::setPreviewCallback(frame_callback cb, void *cookie, int flag) { - LOGV("setFrameCallback"); - mFrameCallback = cb; - mFrameCallbackCookie = cookie; + LOGV("setPreviewCallback"); + mPreviewCallback = cb; + mPreviewCallbackCookie = cookie; sp c = mCamera; if (c == 0) return; - mCamera->setFrameCallbackFlag(frame_callback_flag); + mCamera->setPreviewCallbackFlag(flag); +} + +void Camera::setRecordingCallback(frame_callback cb, void *cookie) +{ + LOGV("setRecordingCallback"); + mRecordingCallback = cb; + mRecordingCallbackCookie = cookie; } void Camera::setErrorCallback(error_callback cb, void *cookie) @@ -316,12 +361,21 @@ void Camera::jpegCallback(const sp& picture) } } -// callback from camera service when video frame is ready -void Camera::frameCallback(const sp& frame) +// callback from camera service when preview frame is ready +void Camera::previewCallback(const sp& frame) { LOGV("frameCallback"); - if (mFrameCallback) { - mFrameCallback(frame, mFrameCallbackCookie); + if (mPreviewCallback) { + mPreviewCallback(frame, mPreviewCallbackCookie); + } +} + +// callback from camera service when a recording frame is ready +void Camera::recordingCallback(const sp& frame) +{ + LOGV("recordingCallback"); + if (mRecordingCallback) { + mRecordingCallback(frame, mRecordingCallbackCookie); } } diff --git a/libs/ui/CameraParameters.cpp b/libs/ui/CameraParameters.cpp index 7ca77bbc536b0..6c258366fdd42 100644 --- a/libs/ui/CameraParameters.cpp +++ b/libs/ui/CameraParameters.cpp @@ -24,6 +24,9 @@ namespace android { +static const char* portrait = "portrait"; +static const char* landscape = "landscape"; + CameraParameters::CameraParameters() : mMap() { @@ -182,6 +185,23 @@ void CameraParameters::setPreviewFormat(const char *format) set("preview-format", format); } +int CameraParameters::getOrientation() const +{ + const char* orientation = get("orientation"); + if (orientation && !strcmp(orientation, portrait)) + return CAMERA_ORIENTATION_PORTRAIT; + return CAMERA_ORIENTATION_LANDSCAPE; +} + +void CameraParameters::setOrientation(int orientation) +{ + if (orientation == CAMERA_ORIENTATION_PORTRAIT) { + set("preview-format", portrait); + } else { + set("preview-format", landscape); + } +} + const char *CameraParameters::getPreviewFormat() const { return get("preview-format"); diff --git a/libs/ui/EGLDisplaySurface.cpp b/libs/ui/EGLDisplaySurface.cpp index 44258a8d2c72e..d06c98b9bb4db 100644 --- a/libs/ui/EGLDisplaySurface.cpp +++ b/libs/ui/EGLDisplaySurface.cpp @@ -42,7 +42,7 @@ #include #endif -#include +#include #include @@ -71,8 +71,6 @@ EGLDisplaySurface::EGLDisplaySurface() egl_native_window_t::incRef = &EGLDisplaySurface::hook_incRef; egl_native_window_t::decRef = &EGLDisplaySurface::hook_decRef; egl_native_window_t::swapBuffers = &EGLDisplaySurface::hook_swapBuffers; - egl_native_window_t::setSwapRectangle = &EGLDisplaySurface::hook_setSwapRectangle; - egl_native_window_t::nextBuffer = &EGLDisplaySurface::hook_nextBuffer; egl_native_window_t::connect = 0; egl_native_window_t::disconnect = 0; @@ -136,15 +134,6 @@ uint32_t EGLDisplaySurface::hook_swapBuffers(NativeWindowType window) { EGLDisplaySurface* that = static_cast(window); return that->swapBuffers(); } -uint32_t EGLDisplaySurface::hook_nextBuffer(NativeWindowType window) { - EGLDisplaySurface* that = static_cast(window); - return that->nextBuffer(); -} -void EGLDisplaySurface::hook_setSwapRectangle(NativeWindowType window, - int l, int t, int w, int h) { - EGLDisplaySurface* that = static_cast(window); - that->setSwapRectangle(l, t, w, h); -} void EGLDisplaySurface::setSwapRectangle(int l, int t, int w, int h) { @@ -249,15 +238,6 @@ int32_t EGLDisplaySurface::getPageFlipCount() const return mPageFlipCount; } -uint32_t EGLDisplaySurface::nextBuffer() -{ - // update the address of the buffer to draw to next - const GGLSurface& buffer = mFb[mIndex]; - egl_native_window_t::offset = - intptr_t(buffer.data) - egl_native_window_t::base; - return 0; -} - void EGLDisplaySurface::copyFrontToBack(const Region& copyback) { #if HAVE_ANDROID_OS @@ -318,6 +298,59 @@ void EGLDisplaySurface::copyFrontToBack(const Region& copyback) } } +void EGLDisplaySurface::copyFrontToImage(const copybit_image_t& dst) +{ +#if HAVE_ANDROID_OS + if (mBlitEngine) { + copybit_image_t src = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + region_iterator it(Region(Rect( + egl_native_window_t::width, egl_native_window_t::height))); + mBlitEngine->blit(mBlitEngine, &dst, &src, &it); + } else +#endif + { + uint8_t* const screen_src = mFb[ mIndex].data; + const size_t bpp = bytesPerPixel(egl_native_window_t::format); + const size_t bpr = egl_native_window_t::stride * bpp; + memcpy((char*)dst.base + dst.offset, screen_src, + bpr*egl_native_window_t::height); + } +} + +void EGLDisplaySurface::copyBackToImage(const copybit_image_t& dst) +{ +#if HAVE_ANDROID_OS + if (mBlitEngine) { + copybit_image_t src = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[1-mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + region_iterator it(Region(Rect( + egl_native_window_t::width, egl_native_window_t::height))); + mBlitEngine->blit(mBlitEngine, &dst, &src, &it); + } else +#endif + { + uint8_t* const screen_src = mFb[1-mIndex].data; + const size_t bpp = bytesPerPixel(egl_native_window_t::format); + const size_t bpr = egl_native_window_t::stride * bpp; + memcpy((char*)dst.base + dst.offset, screen_src, + bpr*egl_native_window_t::height); + } +} + + status_t EGLDisplaySurface::mapFrameBuffer() { char const * const device_template[] = { diff --git a/libs/ui/EGLNativeWindowSurface.cpp b/libs/ui/EGLNativeWindowSurface.cpp index d55fb7090ff61..f1071cf9097b4 100644 --- a/libs/ui/EGLNativeWindowSurface.cpp +++ b/libs/ui/EGLNativeWindowSurface.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include @@ -48,8 +48,6 @@ EGLNativeWindowSurface::EGLNativeWindowSurface(const sp& surface) egl_native_window_t::incRef = &EGLNativeWindowSurface::hook_incRef; egl_native_window_t::decRef = &EGLNativeWindowSurface::hook_decRef; egl_native_window_t::swapBuffers = &EGLNativeWindowSurface::hook_swapBuffers; - egl_native_window_t::nextBuffer = &EGLNativeWindowSurface::hook_nextBuffer; - egl_native_window_t::setSwapRectangle = &EGLNativeWindowSurface::hook_setSwapRectangle; egl_native_window_t::connect = &EGLNativeWindowSurface::hook_connect; egl_native_window_t::disconnect = &EGLNativeWindowSurface::hook_disconnect; @@ -98,18 +96,6 @@ uint32_t EGLNativeWindowSurface::hook_swapBuffers(NativeWindowType window) return that->swapBuffers(); } -uint32_t EGLNativeWindowSurface::hook_nextBuffer(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast(window); - return that->nextBuffer(); -} - -void EGLNativeWindowSurface::hook_setSwapRectangle(NativeWindowType window, int l, int t, int w, int h) -{ - EGLNativeWindowSurface* that = static_cast(window); - that->setSwapRectangle(l, t, w, h); -} - void EGLNativeWindowSurface::setSwapRectangle(int l, int t, int w, int h) { mSurface->setSwapRectangle(Rect(l, t, l+w, t+h)); @@ -138,17 +124,6 @@ uint32_t EGLNativeWindowSurface::swapBuffers() return 0; } -uint32_t EGLNativeWindowSurface::nextBuffer() -{ - const sp& surface(mSurface); - Surface::SurfaceInfo info; - surface->nextBuffer(&info); - // update the address of the buffer to draw to next - egl_native_window_t::base = intptr_t(info.base); - egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); - return 0; -} - void EGLNativeWindowSurface::connect() { if (!mConnected) { diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index 700aa3a8ad755..3b29b094db66f 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -71,10 +71,11 @@ static inline int max(int v1, int v2) EventHub::device_t::device_t(int32_t _id, const char* _path) : id(_id), path(_path), classes(0) - , layoutMap(new KeyLayoutMap()), next(NULL) { + , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) { } EventHub::device_t::~device_t() { + delete [] keyBitmask; delete layoutMap; } @@ -403,6 +404,36 @@ bool EventHub::openPlatformInput(void) return true; } +/* + * Inspect the known devices to determine whether physical keys exist for the given + * framework-domain key codes. + */ +bool EventHub::hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags) { + for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { + outFlags[codeIndex] = 0; + + // check each available hardware device for support for this keycode + Vector scanCodes; + for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) { + if (mDevices[n]) { + status_t err = mDevices[n]->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes); + if (!err) { + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) { + outFlags[codeIndex] = 1; + break; + } + } + } + } + } + } + + return true; +} + // ---------------------------------------------------------------------------- int EventHub::open_device(const char *deviceName) @@ -527,6 +558,16 @@ int EventHub::open_device(const char *deviceName) break; } } + if ((device->classes & CLASS_KEYBOARD) != 0) { + device->keyBitmask = new uint8_t[(KEY_MAX+1)/8]; + if (device->keyBitmask != NULL) { + memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); + } else { + delete device; + LOGE("out of memory allocating key bitmask"); + return -1; + } + } } if (test_bit(BTN_MOUSE, key_bitmask)) { uint8_t rel_bitmask[(REL_MAX+1)/8]; diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp index 7b0922ec72754..ab0fef1489d64 100644 --- a/libs/ui/ICamera.cpp +++ b/libs/ui/ICamera.cpp @@ -28,7 +28,7 @@ namespace android { enum { DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, SET_PREVIEW_DISPLAY, - SET_FRAME_CALLBACK_FLAG, + SET_PREVIEW_CALLBACK_FLAG, START_PREVIEW, STOP_PREVIEW, AUTO_FOCUS, @@ -38,7 +38,11 @@ enum { CONNECT, LOCK, UNLOCK, - PREVIEW_ENABLED + PREVIEW_ENABLED, + START_RECORDING, + STOP_RECORDING, + RECORDING_ENABLED, + RELEASE_RECORDING_FRAME, }; class BpCamera: public BpInterface @@ -69,15 +73,15 @@ public: return reply.readInt32(); } - // set the frame callback flag to affect how the received frames from - // preview are handled. - void setFrameCallbackFlag(int frame_callback_flag) + // set the preview callback flag to affect how the received frames from + // preview are handled. See Camera.h for details. + void setPreviewCallbackFlag(int flag) { - LOGV("setFrameCallbackFlag(%d)", frame_callback_flag); + LOGV("setPreviewCallbackFlag(%d)", flag); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - data.writeInt32(frame_callback_flag); - remote()->transact(SET_FRAME_CALLBACK_FLAG, data, &reply); + data.writeInt32(flag); + remote()->transact(SET_PREVIEW_CALLBACK_FLAG, data, &reply); } // start preview mode, must call setPreviewDisplay first @@ -90,6 +94,16 @@ public: return reply.readInt32(); } + // start recording mode, must call setPreviewDisplay first + status_t startRecording() + { + LOGV("startRecording"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(START_RECORDING, data, &reply); + return reply.readInt32(); + } + // stop preview mode void stopPreview() { @@ -99,6 +113,24 @@ public: remote()->transact(STOP_PREVIEW, data, &reply); } + // stop recording mode + void stopRecording() + { + LOGV("stopRecording"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(STOP_RECORDING, data, &reply); + } + + void releaseRecordingFrame(const sp& mem) + { + LOGV("releaseRecordingFrame"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeStrongBinder(mem->asBinder()); + remote()->transact(RELEASE_RECORDING_FRAME, data, &reply); + } + // check preview state bool previewEnabled() { @@ -109,6 +141,16 @@ public: return reply.readInt32(); } + // check recording state + bool recordingEnabled() + { + LOGV("recordingEnabled"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(RECORDING_ENABLED, data, &reply); + return reply.readInt32(); + } + // auto focus status_t autoFocus() { @@ -202,11 +244,11 @@ status_t BnCamera::onTransact( reply->writeInt32(setPreviewDisplay(surface)); return NO_ERROR; } break; - case SET_FRAME_CALLBACK_FLAG: { - LOGV("SET_FRAME_CALLBACK_TYPE"); + case SET_PREVIEW_CALLBACK_FLAG: { + LOGV("SET_PREVIEW_CALLBACK_TYPE"); CHECK_INTERFACE(ICamera, data, reply); - int frame_callback_flag = data.readInt32(); - setFrameCallbackFlag(frame_callback_flag); + int callback_flag = data.readInt32(); + setPreviewCallbackFlag(callback_flag); return NO_ERROR; } break; case START_PREVIEW: { @@ -215,18 +257,43 @@ status_t BnCamera::onTransact( reply->writeInt32(startPreview()); return NO_ERROR; } break; + case START_RECORDING: { + LOGV("START_RECORDING"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(startRecording()); + return NO_ERROR; + } break; case STOP_PREVIEW: { LOGV("STOP_PREVIEW"); CHECK_INTERFACE(ICamera, data, reply); stopPreview(); return NO_ERROR; } break; + case STOP_RECORDING: { + LOGV("STOP_RECORDING"); + CHECK_INTERFACE(ICamera, data, reply); + stopRecording(); + return NO_ERROR; + } break; + case RELEASE_RECORDING_FRAME: { + LOGV("RELEASE_RECORDING_FRAME"); + CHECK_INTERFACE(ICamera, data, reply); + sp mem = interface_cast(data.readStrongBinder()); + releaseRecordingFrame(mem); + return NO_ERROR; + } break; case PREVIEW_ENABLED: { LOGV("PREVIEW_ENABLED"); CHECK_INTERFACE(ICamera, data, reply); reply->writeInt32(previewEnabled()); return NO_ERROR; } break; + case RECORDING_ENABLED: { + LOGV("RECORDING_ENABLED"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(recordingEnabled()); + return NO_ERROR; + } break; case AUTO_FOCUS: { LOGV("AUTO_FOCUS"); CHECK_INTERFACE(ICamera, data, reply); diff --git a/libs/ui/ICameraClient.cpp b/libs/ui/ICameraClient.cpp index c5d6d52f4df9a..4bec9d2ae6c3c 100644 --- a/libs/ui/ICameraClient.cpp +++ b/libs/ui/ICameraClient.cpp @@ -28,9 +28,10 @@ enum { SHUTTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, RAW_CALLBACK, JPEG_CALLBACK, - FRAME_CALLBACK, + PREVIEW_CALLBACK, ERROR_CALLBACK, - AUTOFOCUS_CALLBACK + AUTOFOCUS_CALLBACK, + RECORDING_CALLBACK, }; class BpCameraClient: public BpInterface @@ -70,14 +71,24 @@ public: remote()->transact(JPEG_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); } - // callback from camera service to app with video frame data - void frameCallback(const sp& frame) + // callback from camera service to app with preview frame data + void previewCallback(const sp& frame) { - LOGV("frameCallback"); + LOGV("previewCallback"); Parcel data, reply; data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); data.writeStrongBinder(frame->asBinder()); - remote()->transact(FRAME_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + remote()->transact(PREVIEW_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app with recording frame data + void recordingCallback(const sp& frame) + { + LOGV("recordingCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeStrongBinder(frame->asBinder()); + remote()->transact(RECORDING_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); } // callback from camera service to app to report error @@ -135,11 +146,18 @@ status_t BnCameraClient::onTransact( jpegCallback(picture); return NO_ERROR; } break; - case FRAME_CALLBACK: { - LOGV("FRAME_CALLBACK"); + case PREVIEW_CALLBACK: { + LOGV("PREVIEW_CALLBACK"); CHECK_INTERFACE(ICameraClient, data, reply); sp frame = interface_cast(data.readStrongBinder()); - frameCallback(frame); + previewCallback(frame); + return NO_ERROR; + } break; + case RECORDING_CALLBACK: { + LOGV("RECORDING_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + sp frame = interface_cast(data.readStrongBinder()); + recordingCallback(frame); return NO_ERROR; } break; case ERROR_CALLBACK: { diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp index 6f3cd4776b92f..abd3634b9a34f 100644 --- a/libs/ui/ISurface.cpp +++ b/libs/ui/ISurface.cpp @@ -27,12 +27,33 @@ namespace android { -enum { - REGISTER_BUFFERS = IBinder::FIRST_CALL_TRANSACTION, - UNREGISTER_BUFFERS, - POST_BUFFER, // one-way transaction - CREATE_OVERLAY, -}; +ISurface::BufferHeap::BufferHeap() + : w(0), h(0), hor_stride(0), ver_stride(0), format(0), + transform(0), flags(0) +{ +} + +ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, const sp& heap) + : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), + format(format), heap(heap) +{ +} + +ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, uint32_t transform, uint32_t flags, + const sp& heap) + : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), + format(format), transform(transform), flags(flags), heap(heap) +{ +} + + +ISurface::BufferHeap::~BufferHeap() +{ +} class BpSurface : public BpInterface { @@ -42,17 +63,18 @@ public: { } - virtual status_t registerBuffers(int w, int h, int hstride, int vstride, - PixelFormat format, const sp& heap) + virtual status_t registerBuffers(const BufferHeap& buffers) { Parcel data, reply; data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(w); - data.writeInt32(h); - data.writeInt32(hstride); - data.writeInt32(vstride); - data.writeInt32(format); - data.writeStrongBinder(heap->asBinder()); + data.writeInt32(buffers.w); + data.writeInt32(buffers.h); + data.writeInt32(buffers.hor_stride); + data.writeInt32(buffers.ver_stride); + data.writeInt32(buffers.format); + data.writeInt32(buffers.transform); + data.writeInt32(buffers.flags); + data.writeStrongBinder(buffers.heap->asBinder()); remote()->transact(REGISTER_BUFFERS, data, &reply); status_t result = reply.readInt32(); return result; @@ -102,13 +124,16 @@ status_t BnSurface::onTransact( switch(code) { case REGISTER_BUFFERS: { CHECK_INTERFACE(ISurface, data, reply); - int w = data.readInt32(); - int h = data.readInt32(); - int hs= data.readInt32(); - int vs= data.readInt32(); - PixelFormat f = data.readInt32(); - sp heap(interface_cast(data.readStrongBinder())); - status_t err = registerBuffers(w,h,hs,vs,f,heap); + BufferHeap buffer; + buffer.w = data.readInt32(); + buffer.h = data.readInt32(); + buffer.hor_stride = data.readInt32(); + buffer.ver_stride= data.readInt32(); + buffer.format = data.readInt32(); + buffer.transform = data.readInt32(); + buffer.flags = data.readInt32(); + buffer.heap = interface_cast(data.readStrongBinder()); + status_t err = registerBuffers(buffer); reply->writeInt32(err); return NO_ERROR; } break; diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp index c8e61689d5763..b236edc290f58 100644 --- a/libs/ui/Overlay.cpp +++ b/libs/ui/Overlay.cpp @@ -59,6 +59,12 @@ status_t Overlay::queueBuffer(overlay_buffer_t buffer) return mOverlayData->queueBuffer(mOverlayData, buffer); } +int32_t Overlay::getBufferCount() const +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->getBufferCount(mOverlayData); +} + void* Overlay::getBufferAddress(overlay_buffer_t buffer) { if (mStatus != NO_ERROR) return NULL; diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp index 605c8aef931f9..b65ed9736ba94 100644 --- a/libs/ui/PixelFormat.cpp +++ b/libs/ui/PixelFormat.cpp @@ -19,6 +19,18 @@ namespace android { +size_t PixelFormatInfo::getScanlineSize(unsigned int width) const +{ + size_t size; + if ((components >= 6) && (components <= 8)) { + // YCbCr formats are differents. + size = (width * bitsPerPixel)>>3; + } else { + size = width * bytesPerPixel; + } + return size; +} + ssize_t bytesPerPixel(PixelFormat format) { PixelFormatInfo info; @@ -47,7 +59,25 @@ status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) if (!valid) { return BAD_INDEX; } - + + #define COMPONENT(name) \ + case GGL_##name: info->components = PixelFormatInfo::name; break; + + switch (i->components) { + COMPONENT(ALPHA) + COMPONENT(RGB) + COMPONENT(RGBA) + COMPONENT(LUMINANCE) + COMPONENT(LUMINANCE_ALPHA) + COMPONENT(Y_CB_CR_SP) + COMPONENT(Y_CB_CR_P) + COMPONENT(Y_CB_CR_I) + default: + return BAD_INDEX; + } + + #undef COMPONENT + info->format = format; info->bytesPerPixel = i->size; info->bitsPerPixel = i->bitsPerPixel; @@ -59,6 +89,7 @@ status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) info->l_green = i->gl; info->h_blue = i->bh; info->l_blue = i->bl; + return NO_ERROR; } diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp index 0eba0b07c8d4d..0f4b6473008d3 100644 --- a/libs/utils/Parcel.cpp +++ b/libs/utils/Parcel.cpp @@ -658,15 +658,20 @@ status_t Parcel::writeNativeHandle(const native_handle& handle) status_t err; err = writeInt32(handle.numFds); if (err != NO_ERROR) return err; - + err = writeInt32(handle.numInts); if (err != NO_ERROR) return err; - + for (int i=0 ; err==NO_ERROR && idata[i] = readFileDescriptor(); + h->data[i] = dup(readFileDescriptor()); if (h->data[i] < 0) err = BAD_VALUE; } diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 5a09fb44a0c0c..71e7cd722bace 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1736,7 +1736,7 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const } ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, - uint32_t* outSpecFlags) const + uint32_t* outSpecFlags, ResTable_config* outConfig) const { if (mError != NO_ERROR) { return mError; @@ -1809,7 +1809,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag (const Res_value*)(((const uint8_t*)type) + offset); ResTable_config thisConfig; thisConfig.copyFromDtoH(type->config); - + if (outSpecFlags != NULL) { if (typeClass->typeSpecFlags != NULL) { *outSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); @@ -1834,6 +1834,9 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag outValue->res0 = bestValue->res0; outValue->dataType = bestValue->dataType; outValue->data = dtohl(bestValue->data); + if (outConfig != NULL) { + *outConfig = bestItem; + } TABLE_NOISY(size_t len; printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", bestPackage->header->index, @@ -3484,7 +3487,7 @@ ssize_t ResTable::getEntry( ResTable_config thisConfig; thisConfig.copyFromDtoH(thisType->config); - + TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d lang:%c%c=%c%c cnt:%c%c=%c%c " "orien:%d=%d touch:%d=%d density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d\n", entryIndex, typeIndex+1, dtohl(thisType->config.size), diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index ab843f6e798b8..c50d343a731b4 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -317,8 +317,10 @@ status_t String8::real_append(const char* other, size_t otherLen) ->editResize(myLen+otherLen+1); if (buf) { char* str = (char*)buf->data(); - memcpy(str+myLen, other, otherLen+1); mString = str; + str += myLen; + memcpy(str, other, otherLen); + str[otherLen] = '\0'; return NO_ERROR; } return NO_MEMORY; diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index c6f13c943127a..6672e299fe797 100644 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -164,16 +164,15 @@ public class GpsLocationProvider extends LocationProviderImpl { // current setting - 5 minutes private static final long RETRY_INTERVAL = 5*60*1000; - private LocationCollector mCollector; + private ILocationCollector mCollector; public static boolean isSupported() { return native_is_supported(); } - public GpsLocationProvider(Context context, LocationCollector collector) { + public GpsLocationProvider(Context context) { super(LocationManager.GPS_PROVIDER); mContext = context; - mCollector = collector; mProperties = new Properties(); try { @@ -183,10 +182,14 @@ public class GpsLocationProvider extends LocationProviderImpl { stream.close(); mNtpServer = mProperties.getProperty("NTP_SERVER", null); } catch (IOException e) { - Log.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE, e); + Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); } } + public void setLocationCollector(ILocationCollector collector) { + mCollector = collector; + } + /** * Returns true if the provider requires access to a * data network (e.g., the Internet), false otherwise. @@ -623,7 +626,8 @@ public class GpsLocationProvider extends LocationProviderImpl { } // Send to collector - if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) { + if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG + && mCollector != null) { mCollector.updateLocation(mLocation); } } @@ -644,7 +648,7 @@ public class GpsLocationProvider extends LocationProviderImpl { if (Config.LOGV) Log.v(TAG, "reportStatus status: " + status); boolean wasNavigating = mNavigating; - mNavigating = (status == GPS_STATUS_SESSION_BEGIN || status == GPS_STATUS_ENGINE_ON); + mNavigating = (status == GPS_STATUS_SESSION_BEGIN); if (wasNavigating != mNavigating) { synchronized(mListeners) { diff --git a/location/java/com/android/internal/location/ILocationCollector.java b/location/java/com/android/internal/location/ILocationCollector.java new file mode 100644 index 0000000000000..8a7cdcce1ed39 --- /dev/null +++ b/location/java/com/android/internal/location/ILocationCollector.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.location; + +import android.location.Location; +import android.net.wifi.ScanResult; + +import com.android.internal.location.CellState; + +import java.util.List; + +/** + * Listens for GPS and cell/wifi changes and anonymously uploads to server for + * improving quality of service of NetworkLocationProvider. This service is only enabled when + * the user has enabled the network location provider. + * + * {@hide} + */ +public interface ILocationCollector { + /** + * Updates GPS location if collection is enabled + * + * @param location location object + */ + abstract public void updateLocation(Location location); + + /** + * Updates wifi scan results if collection is enabled + * + * @param currentScanResults scan results + */ + abstract public void updateWifiScanResults(List currentScanResults); + + /** + * Updates the status of the network location provider. + * + * @param enabled true if user has enabled network location based on Google's database + * of wifi points and cell towers. + */ + abstract public void updateNetworkProviderStatus(boolean enabled); + + /** + * Updates cell tower state. This is usually always up to date so should be uploaded + * each time a new location is available. + * + * @param newState cell state + */ + abstract public void updateCellState(CellState newState); + + /** + * Updates the battery health. Battery level is healthy if there is greater than + * {@link #MIN_BATTERY_LEVEL} percentage left or if the device is plugged in + * + * @param scale maximum scale for battery + * @param level current level + * @param plugged true if device is plugged in + */ + abstract public void updateBatteryState(int scale, int level, boolean plugged); +} diff --git a/location/java/com/android/internal/location/protocol/GAddressComponent.java b/location/java/com/android/internal/location/INetworkLocationManager.java similarity index 58% rename from location/java/com/android/internal/location/protocol/GAddressComponent.java rename to location/java/com/android/internal/location/INetworkLocationManager.java index a06a23dd32522..83bbe1fdf526f 100644 --- a/location/java/com/android/internal/location/protocol/GAddressComponent.java +++ b/location/java/com/android/internal/location/INetworkLocationManager.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2007 The Android Open Source Project + /* + * Copyright (C) 2009 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. @@ -14,10 +14,15 @@ * limitations under the License. */ -package com.android.internal.location.protocol; - -public interface GAddressComponent { - static final int NAME = 1; - static final int FEATURE_TYPE = 2; -} +package com.android.internal.location; +/** + * Used to register network location and collection services + * with the Location Manager Service. + * + * {@hide} + */ +public interface INetworkLocationManager { + void setNetworkLocationProvider(INetworkLocationProvider provider); + void setLocationCollector(ILocationCollector collector); +} \ No newline at end of file diff --git a/location/java/com/android/internal/location/INetworkLocationProvider.java b/location/java/com/android/internal/location/INetworkLocationProvider.java new file mode 100644 index 0000000000000..730cb480a4d6c --- /dev/null +++ b/location/java/com/android/internal/location/INetworkLocationProvider.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.location; + +import android.location.Address; +import android.location.Location; +import android.net.wifi.ScanResult; + +import com.google.common.io.protocol.ProtoBuf; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Locale; + +/** + * Interface for network location provider + * + * {@hide} + */ +public interface INetworkLocationProvider { + + public interface Callback { + + /** + * Callback function to notify of a received network location + * + * @param location location object that is received. may be null if not a valid location + * @param successful true if network query was successful, even if no location was found + */ + void locationReceived(Location location, boolean successful); + } + + /** + * Updates the current cell lock status. + * + * @param acquired true if a cell lock has been acquired + */ + abstract public void updateCellLockStatus(boolean acquired); + + /** + * Notifies the provider if Wifi has been enabled or disabled + * by the user + * + * @param enabled true if wifi is enabled; false otherwise + */ + abstract public void updateWifiEnabledState(boolean enabled); + + /** + * Notifies the provider that there are scan results available. + * + * @param scanResults list of wifi scan results + */ + abstract public void updateWifiScanResults(List scanResults); + + /** + * Adds a list of application clients + * Only used by the NetworkLocationProvider + * + * @param applications list of package names + */ + abstract public void addListener(String[] applications); + + /** + * Removes a list of application clients + * Only used by the NetworkLocationProvider + * + * @param applications list of package names + */ + abstract public void removeListener(String[] applications); + + + abstract public String getFromLocation(double latitude, double longitude, int maxResults, + String language, String country, String variant, String appName, List
      addrs); + + abstract public String getFromLocationName(String locationName, + double lowerLeftLatitude, double lowerLeftLongitude, + double upperRightLatitude, double upperRightLongitude, int maxResults, + String language, String country, String variant, String appName, List
      addrs); + +} diff --git a/location/java/com/android/internal/location/LocationCache.java b/location/java/com/android/internal/location/LocationCache.java deleted file mode 100644 index 079c9c7e1306e..0000000000000 --- a/location/java/com/android/internal/location/LocationCache.java +++ /dev/null @@ -1,608 +0,0 @@ -// Copyright 2007 The Android Open Source Project - -package com.android.internal.location; - -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.DataOutput; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import android.location.Location; -import android.location.LocationManager; -import android.net.wifi.ScanResult; -import android.os.Bundle; -import android.util.Log; - -/** - * Data store to cache cell-id and wifi locations from the network - * - * {@hide} - */ -public class LocationCache { - private static final String TAG = "LocationCache"; - - // Version of cell cache - private static final int CACHE_DB_VERSION = 1; - - // Don't save cache more than once every minute - private static final long SAVE_FREQUENCY = 60 * 1000; - - // Location of the cache file; - private static final String mCellCacheFile = "cache.cell"; - private static final String mWifiCacheFile = "cache.wifi"; - - // Maximum time (in millis) that a record is valid for, before it needs - // to be refreshed from the server. - private static final long MAX_CELL_REFRESH_RECORD_AGE = 12 * 60 * 60 * 1000; // 12 hours - private static final long MAX_WIFI_REFRESH_RECORD_AGE = 48 * 60 * 60 * 1000; // 48 hours - - // Cache sizes - private static final int MAX_CELL_RECORDS = 50; - private static final int MAX_WIFI_RECORDS = 200; - - // Cache constants - private static final long CELL_SMOOTHING_WINDOW = 30 * 1000; // 30 seconds - private static final int WIFI_MIN_AP_REQUIRED = 2; - private static final int WIFI_MAX_MISS_ALLOWED = 5; - private static final int MAX_ACCURACY_ALLOWED = 5000; // 5km - - // Caches - private final Cache mCellCache; - private final Cache mWifiCache; - - // Currently calculated centroids - private final LocationCentroid mCellCentroid = new LocationCentroid(); - private final LocationCentroid mWifiCentroid = new LocationCentroid(); - - // Extra key and values - private final String EXTRA_KEY_LOCATION_TYPE = "networkLocationType"; - private final String EXTRA_VALUE_LOCATION_TYPE_CELL = "cell"; - private final String EXTRA_VALUE_LOCATION_TYPE_WIFI = "wifi"; - - public LocationCache() { - mCellCache = new Cache(LocationManager.SYSTEM_DIR, mCellCacheFile, - MAX_CELL_RECORDS, MAX_CELL_REFRESH_RECORD_AGE); - mWifiCache = new Cache(LocationManager.SYSTEM_DIR, mWifiCacheFile, - MAX_WIFI_RECORDS, MAX_WIFI_REFRESH_RECORD_AGE); - } - - /** - * Looks up network location on device cache - * - * @param cellState primary cell state - * @param cellHistory history of cell states - * @param scanResults wifi scan results - * @param result location object to fill if location is found - * @return true if cache was able to answer query (successfully or not), false if call to - * server is required - */ - public synchronized boolean lookup(CellState cellState, List cellHistory, - List scanResults, Location result) { - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "including cell:" + (cellState != null) + - ", wifi:" + ((scanResults != null)? scanResults.size() : "null")); - } - - long now = System.currentTimeMillis(); - - mCellCentroid.reset(); - mWifiCentroid.reset(); - - if (cellState != null && cellState.isValid()) { - String primaryCellKey = getCellCacheKey(cellState.getMcc(), cellState.getMnc(), - cellState.getLac(), cellState.getCid()); - Record record = mCellCache.lookup(primaryCellKey); - - // Relax MCC/MNC condition - if (record == null) { - primaryCellKey = getCellCacheKey(-1, -1, cellState.getLac(), cellState.getCid()); - record = mCellCache.lookup(primaryCellKey); - } - - if (record == null) { - // Make a server request if primary cell doesn't exist in DB - return false; - } - - if (record.isValid()) { - mCellCentroid.addLocation(record.getLat(), record.getLng(), record.getAccuracy(), - record.getConfidence()); - } - } - - if (cellHistory != null) { - for (CellState historicalCell : cellHistory) { - // Cell location might need to be smoothed if you are on the border of two cells - if (now - historicalCell.getTime() < CELL_SMOOTHING_WINDOW) { - String historicalCellKey = getCellCacheKey(historicalCell.getMcc(), - historicalCell.getMnc(), historicalCell.getLac(), historicalCell.getCid()); - Record record = mCellCache.lookup(historicalCellKey); - - // Relax MCC/MNC condition - if (record == null) { - historicalCellKey = getCellCacheKey(-1, -1, historicalCell.getLac(), - historicalCell.getCid()); - record = mCellCache.lookup(historicalCellKey); - } - - if (record != null && record.isValid()) { - mCellCentroid.addLocation(record.getLat(), record.getLng(), - record.getAccuracy(), record.getConfidence()); - } - } - } - } - - if (scanResults != null) { - int miss = 0; - for (ScanResult scanResult : scanResults) { - String wifiKey = scanResult.BSSID; - Record record = mWifiCache.lookup(wifiKey); - if (record == null) { - miss++; - } else { - if (record.isValid()) { - mWifiCentroid.addLocation(record.getLat(), record.getLng(), - record.getAccuracy(), record.getConfidence()); - } - } - } - - if (mWifiCentroid.getNumber() >= WIFI_MIN_AP_REQUIRED) { - // Try to return best out of the available cell or wifi location - } else if (miss > Math.min(WIFI_MAX_MISS_ALLOWED, (scanResults.size()+1)/2)) { - // Make a server request - return false; - } else { - // Don't use wifi location, only consider using cell location - mWifiCache.save(); - mWifiCentroid.reset(); - } - } - - if (mCellCentroid.getNumber() > 0) { - mCellCache.save(); - } - if (mWifiCentroid.getNumber() > 0) { - mWifiCache.save(); - } - - int cellAccuracy = mCellCentroid.getAccuracy(); - int wifiAccuracy = mWifiCentroid.getAccuracy(); - - int cellConfidence = mCellCentroid.getConfidence(); - int wifiConfidence = mWifiCentroid.getConfidence(); - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "cellAccuracy:" + cellAccuracy+ ", wifiAccuracy:" + wifiAccuracy); - } - - if ((mCellCentroid.getNumber() == 0 || cellAccuracy > MAX_ACCURACY_ALLOWED) && - (mWifiCentroid.getNumber() == 0 || wifiAccuracy > MAX_ACCURACY_ALLOWED)) { - // Return invalid location if neither location is valid - result.setAccuracy(-1); - - // don't make server request - return true; - } - - float[] distance = new float[1]; - Location.distanceBetween(mCellCentroid.getCentroidLat(), mCellCentroid.getCentroidLng(), - mWifiCentroid.getCentroidLat(), mWifiCentroid.getCentroidLng(), - distance); - - boolean consistent = distance[0] <= (cellAccuracy + wifiAccuracy); - - boolean useCell = true; - - if (consistent) { - // If consistent locations, use one with greater accuracy - useCell = (cellAccuracy <= wifiAccuracy); - } else { - // If inconsistent locations, use one with greater confidence - useCell = (cellConfidence >= wifiConfidence); - } - - if (useCell) { - // Use cell results - result.setAccuracy(cellAccuracy); - result.setLatitude(mCellCentroid.getCentroidLat()); - result.setLongitude(mCellCentroid.getCentroidLng()); - result.setTime(now); - - Bundle extras = result.getExtras() == null ? new Bundle() : result.getExtras(); - extras.putString(EXTRA_KEY_LOCATION_TYPE, EXTRA_VALUE_LOCATION_TYPE_CELL); - result.setExtras(extras); - - } else { - // Use wifi results - result.setAccuracy(wifiAccuracy); - result.setLatitude(mWifiCentroid.getCentroidLat()); - result.setLongitude(mWifiCentroid.getCentroidLng()); - result.setTime(now); - - Bundle extras = result.getExtras() == null ? new Bundle() : result.getExtras(); - extras.putString(EXTRA_KEY_LOCATION_TYPE, EXTRA_VALUE_LOCATION_TYPE_WIFI); - result.setExtras(extras); - - } - - // don't make a server request - return true; - } - - public synchronized void insert(int mcc, int mnc, int lac, int cid, double lat, double lng, - int accuracy, int confidence, long time) { - String key = getCellCacheKey(mcc, mnc, lac, cid); - if (accuracy <= 0) { - mCellCache.insert(key, new Record()); - } else { - mCellCache.insert(key, new Record(accuracy, confidence, lat, lng, time)); - } - } - - public synchronized void insert(String bssid, double lat, double lng, int accuracy, - int confidence, long time) { - if (accuracy <= 0) { - mWifiCache.insert(bssid, new Record()); - } else { - mWifiCache.insert(bssid, new Record(accuracy, confidence, lat, lng, time)); - } - } - - public synchronized void save() { - mCellCache.save(); - mWifiCache.save(); - } - - /** - * Cell or Wifi location record - */ - public static class Record { - - private final double lat; - private final double lng; - private final int accuracy; - private final int confidence; - - // Time (since the epoch) of original reading. - private final long originTime; - - public static Record read(DataInput dataInput) throws IOException { - final int accuracy = dataInput.readInt(); - final int confidence = dataInput.readInt(); - final double lat = dataInput.readDouble(); - final double lng = dataInput.readDouble(); - final long readingTime = dataInput.readLong(); - return new Record(accuracy, confidence, lat, lng, readingTime); - } - - /** - * Creates an "invalid" record indicating there was no location data - * available for the given data - */ - public Record() { - this(-1, 0, 0, 0, System.currentTimeMillis()); - } - - /** - * Creates a Record - * - * @param accuracy acuracy in meters. If < 0, then this is an invalid record. - * @param confidence confidence (0-100) - * @param lat latitude - * @param lng longitude - * @param time Time of the original location reading from the server - */ - public Record(int accuracy, int confidence, double lat, double lng, long time) { - this.accuracy = accuracy; - this.confidence = confidence; - this.originTime = time; - this.lat = lat; - this.lng = lng; - } - - public double getLat() { - return lat; - } - - public double getLng() { - return lng; - } - - public int getAccuracy() { - return accuracy; - } - - public int getConfidence() { - return confidence; - } - - public boolean isValid() { - return accuracy > 0; - } - - public long getTime() { - return originTime; - } - - public void write(DataOutput dataOut) throws IOException { - dataOut.writeInt(accuracy); - dataOut.writeInt(confidence); - dataOut.writeDouble(lat); - dataOut.writeDouble(lng); - dataOut.writeLong(originTime); - } - - @Override - public String toString() { - return lat + "," + lng + "," + originTime +"," + accuracy + "," + confidence; - } - } - - public class Cache extends LinkedHashMap { - private final long mMaxAge; - private final int mCapacity; - private final String mDir; - private final String mFile; - private long mLastSaveTime = 0; - - public Cache(String dir, String file, int capacity, long maxAge) { - super(capacity + 1, 1.1f, true); - this.mCapacity = capacity; - this.mDir = dir; - this.mFile = file; - this.mMaxAge = maxAge; - load(); - } - - private LocationCache.Record lookup(String key) { - LocationCache.Record result = (LocationCache.Record) get(key); - - if (result == null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "lookup: " + key + " failed"); - } - return null; - } - - // Cache entry needs refresh - if (result.getTime() + mMaxAge < System.currentTimeMillis()) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "lookup: " + key + " expired"); - } - return null; - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "lookup: " + key + " " + result.toString()); - } - - return result; - } - - private void insert(String key, LocationCache.Record record) { - remove(key); - put(key, record); - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "insert: " + key + " " + record.toString()); - } - } - - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - // Remove cache entries when it has more than capacity - return size() > mCapacity; - } - - private void load() { - FileInputStream istream; - try { - File f = new File(mDir, mFile); - istream = new FileInputStream(f); - } catch (FileNotFoundException e) { - // No existing DB - return new CellCache - return; - } - - DataInputStream dataInput = new DataInputStream(istream); - - try { - int version = dataInput.readUnsignedShort(); - if (version != CACHE_DB_VERSION) { - // Ignore records - invalid version ID. - dataInput.close(); - return; - } - int records = dataInput.readUnsignedShort(); - - for (int i = 0; i < records; i++) { - final String key = dataInput.readUTF(); - final LocationCache.Record record = LocationCache.Record.read(dataInput); - //Log.d(TAG, key + " " + record.toString()); - put(key, record); - } - - dataInput.close(); - } catch (IOException e) { - // Something's corrupted - return a new CellCache - } - } - - private void save() { - long now = System.currentTimeMillis(); - if (mLastSaveTime != 0 && (now - mLastSaveTime < SAVE_FREQUENCY)) { - // Don't save to file more often than SAVE_FREQUENCY - return; - } - - FileOutputStream ostream; - - File systemDir = new File(mDir); - if (!systemDir.exists()) { - if (!systemDir.mkdirs()) { - Log.e(TAG, "Cache.save(): couldn't create directory"); - return; - } - } - - try { - File f = new File(mDir, mFile); - ostream = new FileOutputStream(f); - } catch (FileNotFoundException e) { - Log.d(TAG, "Cache.save(): unable to create cache file", e); - return; - } - - DataOutputStream dataOut = new DataOutputStream(ostream); - try { - dataOut.writeShort(CACHE_DB_VERSION); - - dataOut.writeShort(size()); - - for (Iterator iter = entrySet().iterator(); iter.hasNext();) { - Map.Entry entry = (Map.Entry) iter.next(); - String key = (String) entry.getKey(); - LocationCache.Record record = (LocationCache.Record) entry.getValue(); - dataOut.writeUTF(key); - record.write(dataOut); - } - - dataOut.close(); - mLastSaveTime = now; - - } catch (IOException e) { - Log.e(TAG, "Cache.save(): unable to write cache", e); - // This should never happen - } - } - } - - public class LocationCentroid { - - double mLatSum = 0; - double mLngSum = 0; - int mNumber = 0; - int mConfidence = 0; - - double mCentroidLat = 0; - double mCentroidLng = 0; - - // Probably never have to calculate centroid for more than 10 locations - final static int MAX_SIZE = 10; - double[] mLats = new double[MAX_SIZE]; - double[] mLngs = new double[MAX_SIZE]; - int[] mRadii = new int[MAX_SIZE]; - - LocationCentroid() { - reset(); - } - - public void reset() { - mLatSum = 0; - mLngSum = 0; - mNumber = 0; - mConfidence = 0; - - mCentroidLat = 0; - mCentroidLng = 0; - - for (int i = 0; i < MAX_SIZE; i++) { - mLats[i] = 0; - mLngs[i] = 0; - mRadii[i] = 0; - } - } - - public void addLocation(double lat, double lng, int accuracy, int confidence) { - if (mNumber < MAX_SIZE && accuracy <= MAX_ACCURACY_ALLOWED) { - mLatSum += lat; - mLngSum += lng; - if (confidence > mConfidence) { - mConfidence = confidence; - } - - mLats[mNumber] = lat; - mLngs[mNumber] = lng; - mRadii[mNumber] = accuracy; - mNumber++; - } - } - - public int getNumber() { - return mNumber; - } - - public double getCentroidLat() { - if (mCentroidLat == 0 && mNumber != 0) { - mCentroidLat = mLatSum/mNumber; - } - return mCentroidLat; - } - - public double getCentroidLng() { - if (mCentroidLng == 0 && mNumber != 0) { - mCentroidLng = mLngSum/mNumber; - } - return mCentroidLng; - } - - public int getConfidence() { - return mConfidence; - } - - public int getAccuracy() { - if (mNumber == 0) { - return 0; - } - - if (mNumber == 1) { - return mRadii[0]; - } - - double cLat = getCentroidLat(); - double cLng = getCentroidLng(); - - int meanDistanceSum = 0; - int smallestCircle = MAX_ACCURACY_ALLOWED; - int smallestCircleDistance = MAX_ACCURACY_ALLOWED; - float[] distance = new float[1]; - boolean outlierExists = false; - - for (int i = 0; i < mNumber; i++) { - Location.distanceBetween(cLat, cLng, mLats[i], mLngs[i], distance); - meanDistanceSum += (int)distance[0]; - if (distance[0] > mRadii[i]) { - outlierExists = true; - } - if (mRadii[i] < smallestCircle) { - smallestCircle = mRadii[i]; - smallestCircleDistance = (int)distance[0]; - } - } - - if (outlierExists) { - return meanDistanceSum/mNumber; - } else { - return Math.max(smallestCircle, smallestCircleDistance); - } - } - - } - - private String getCellCacheKey(int mcc, int mnc, int lac, int cid) { - return mcc + ":" + mnc + ":" + lac + ":" + cid; - } - -} diff --git a/location/java/com/android/internal/location/LocationCollector.java b/location/java/com/android/internal/location/LocationCollector.java deleted file mode 100644 index 39f432f14b822..0000000000000 --- a/location/java/com/android/internal/location/LocationCollector.java +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location; - -import com.android.internal.location.protocol.GDebugProfile; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -import android.location.Location; -import android.net.wifi.ScanResult; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.util.Log; - -/** - * Listens for GPS and cell/wifi changes and anonymously uploads to server for - * improving quality of service of NetworkLocationProvider. This service is only enabled when - * the user has enabled the network location provider. - * - * {@hide} - */ -public class LocationCollector { - - private static final String TAG = "LocationCollector"; - - // last location valid for 12 minutes - private static final long MIN_VALID_LOCATION_TIME = 12 * 60 * 1000L; - - // don't send wifi more than every 10 min - private static final long MIN_TIME_BETWEEN_WIFI_REPORTS = 10 * 60 * 1000L; - - // atleast 5 changed APs for wifi collection - private static final int MIN_CHANGED_WIFI_POINTS = 5; - - // don't collect if distance moved less than 200 meters - private static final int MIN_DISTANCE_BETWEEN_REPORTS = 200; - - // don't collect if battery level less than 20% - private static final double MIN_BATTERY_LEVEL = 0.2; - - // if battery level is greater than 90% and plugged in, collect more frequently - private static final double CHARGED_BATTERY_LEVEL = 0.9; - - // collect bursts every 15 minutes (running on battery) - private static final long BURST_REST_TIME_ON_BATTERY = 15 * 60 * 1000L; - - // collect bursts every 8 minutes (when plugged in) - private static final long BURST_REST_TIME_PLUGGED = 8 * 60 * 1000L; - - // collect burst samples every 12 seconds - private static final int BURST_MEASUREMENT_INTERVAL = 12 * 1000; - - // collect 11 burst samples before resting (11 samples every 12 seconds = 2 minute bursts) - private static final int BURST_NUM_SAMPLES = 11; - - // don't collect bursts if user in same loc for 2 bursts - private static final int MAX_BURSTS_FROM_SAME_LOCATION = 2; - - // don't send more than 2 bursts if user hasn't moved more than 25 meters - private static final int MIN_DISTANCE_BETWEEN_BURSTS = 25; - - // Cell State - private CellState mCellState = null; - private CellUploads mCellUploads = new CellUploads(); - - // GPS state - private Location mLastKnownLocation = null; - private Location mLastUploadedLocation = null; - private long mLastKnownLocationTime = 0; - private long mLastUploadedLocationTime = 0; - - // Burst state - private Location mLastBurstLocation = null; - private long mLastBurstEndTime = 0; - private long mCurrentBurstStartTime = 0; - private int mCurrentBurstNumSamples = 0; - private int mNumBurstsFromLastLocation = 0; - - // WiFi state - private List mWifiLastScanResults = null; - private List mWifiCurrentScanResults = null; - private long mLastWifiScanElapsedTime = 0; - private long mLastWifiScanRealTime = 0; - private boolean mWifiUploadedWithoutLocation = false; - - // Collection state - private boolean mNetworkProviderIsEnabled = true; - private boolean mBatteryLevelIsHealthy = true; - private boolean mBatteryChargedAndPlugged = false; - - // Location masf service - private LocationMasfClient mMasfClient; - - public LocationCollector(LocationMasfClient masfClient) { - mMasfClient = masfClient; - } - - /** - * Updates cell tower state. This is usually always up to date so should be uploaded - * each time a new location is available. - * - * @param newState cell state - */ - public synchronized void updateCellState(CellState newState) { - if (newState == null) { - throw new IllegalArgumentException("cell state is null"); - } - - if (!newState.isValid()) { - return; - } - - if (mCellState != null && mCellState.equals(newState)) { - return; - } - - mCellState = newState; - log("updateCellState(): Updated to " + mCellState.getCid() + "," + mCellState.getLac()); - - if (isCollectionEnabled()) { - addToQueue(GDebugProfile.TRIGGER_CELL_CHANGE); - } - } - - /** - * Updates GPS location if collection is enabled - * - * @param location location object - */ - public synchronized void updateLocation(Location location) { - - // Don't do anything if collection is disabled - if (!isCollectionEnabled()) { - return; - } - - long now = SystemClock.elapsedRealtime(); - - // Update last known location - if (mLastKnownLocation == null) { - mLastKnownLocation = new Location(location); - } else { - mLastKnownLocation.set(location); - } - mLastKnownLocationTime = now; - - // Burst rest time depends on battery state - long restTime = BURST_REST_TIME_ON_BATTERY; - if (mBatteryChargedAndPlugged) { - restTime = BURST_REST_TIME_PLUGGED; - } - - int trigger; - - // In burst mode if either first burst or enough time has passed since last burst - if (mLastBurstEndTime == 0 || (now - mLastBurstEndTime > restTime)) { - - // If location is too recent, then don't do anything! - if (now - mLastUploadedLocationTime < BURST_MEASUREMENT_INTERVAL) { - return; - } - - int distanceFromLastBurst = -1; - if (mLastBurstLocation != null) { - distanceFromLastBurst = (int) mLastBurstLocation.distanceTo(location); - - // Too many bursts from same location, don't upload - if (distanceFromLastBurst < MIN_DISTANCE_BETWEEN_BURSTS && - mNumBurstsFromLastLocation >= MAX_BURSTS_FROM_SAME_LOCATION) { - log("NO UPLOAD: Too many bursts from same location."); - return; - } - } - - if (mCurrentBurstStartTime == 0) { - // Start the burst! - mCurrentBurstStartTime = now; - mCurrentBurstNumSamples = 1; - trigger = GDebugProfile.TRIGGER_COLLECTION_START_BURST; - - } else if (now - mCurrentBurstStartTime > restTime) { - // Burst got old, start a new one - mCurrentBurstStartTime = now; - mCurrentBurstNumSamples = 1; - trigger = GDebugProfile.TRIGGER_COLLECTION_RESTART_BURST; - - } else if (mCurrentBurstNumSamples == BURST_NUM_SAMPLES - 1) { - // Finished a burst - mLastBurstEndTime = now; - mCurrentBurstStartTime = 0; - mCurrentBurstNumSamples = 0; - - // Make sure we don't upload too many bursts from same location - if (mLastBurstLocation == null) { - mLastBurstLocation = new Location(location); - mNumBurstsFromLastLocation = 1; - trigger = GDebugProfile.TRIGGER_COLLECTION_END_BURST; - - } else { - - if (distanceFromLastBurst != -1 && - distanceFromLastBurst < MIN_DISTANCE_BETWEEN_BURSTS) { - // User hasnt moved much from last location, keep track of count, - // don't update last burst loc - mNumBurstsFromLastLocation++; - trigger = GDebugProfile.TRIGGER_COLLECTION_END_BURST_AT_SAME_LOCATION; - - } else { - // User has moved enough, update last burst loc - mLastBurstLocation.set(location); - mNumBurstsFromLastLocation = 1; - trigger = GDebugProfile.TRIGGER_COLLECTION_END_BURST; - } - } - - } else { - // Increment burst sample count - mCurrentBurstNumSamples++; - trigger = GDebugProfile.TRIGGER_COLLECTION_CONTINUE_BURST; - } - - } else if (mLastUploadedLocation != null - && (mLastUploadedLocation.distanceTo(location) > MIN_DISTANCE_BETWEEN_REPORTS)) { - // If not in burst mode but has moved a reasonable distance, upload! - trigger = GDebugProfile.TRIGGER_COLLECTION_MOVED_DISTANCE; - - } else { - // Not in burst mode or hasn't moved enough - log("NO UPLOAD: Not in burst or moving mode. Resting for " + restTime + " ms"); - return; - } - - log("updateLocation(): Updated location with trigger " + trigger); - addToQueue(trigger); - } - - /** - * Updates wifi scan results if collection is enabled - * - * @param currentScanResults scan results - */ - public synchronized void updateWifiScanResults(List currentScanResults) { - if (!isCollectionEnabled()) { - return; - } - - if (currentScanResults == null || currentScanResults.size() == 0) { - return; - } - - long now = SystemClock.elapsedRealtime(); - - // If wifi scan recently received, then don't upload - if ((mLastWifiScanElapsedTime != 0) - && ((now - mLastWifiScanElapsedTime) <= MIN_TIME_BETWEEN_WIFI_REPORTS)) { - return; - } - - if (mWifiCurrentScanResults == null) { - mWifiCurrentScanResults = new ArrayList(); - } else { - mWifiCurrentScanResults.clear(); - } - mWifiCurrentScanResults.addAll(currentScanResults); - - // If wifi has changed enough - boolean wifiHasChanged = false; - - if (mWifiLastScanResults == null) { - wifiHasChanged = true; - } else { - // Calculate the number of new AP points received - HashSet previous = new HashSet(); - HashSet current = new HashSet(); - for (ScanResult s : mWifiLastScanResults) { - previous.add(s.BSSID); - } - for (ScanResult s : mWifiCurrentScanResults) { - current.add(s.BSSID); - } - current.removeAll(previous); - - if (current.size() > - Math.min(MIN_CHANGED_WIFI_POINTS, ((mWifiCurrentScanResults.size()+1)/2))) { - wifiHasChanged = true; - } - } - - if (!wifiHasChanged) { - log("updateWifiScanResults(): Wifi results haven't changed much"); - return; - } - - if (mWifiLastScanResults == null) { - mWifiLastScanResults = new ArrayList(); - } else { - mWifiLastScanResults.clear(); - } - mWifiLastScanResults.addAll(mWifiCurrentScanResults); - - mLastWifiScanElapsedTime = now; - mLastWifiScanRealTime = System.currentTimeMillis(); - - log("updateWifiScanResults(): Updated " + mWifiLastScanResults.size() + " APs"); - addToQueue(GDebugProfile.TRIGGER_WIFI_CHANGE); - } - - /** - * Updates the status of the network location provider. - * - * @param enabled true if user has enabled network location based on Google's database - * of wifi points and cell towers. - */ - public void updateNetworkProviderStatus(boolean enabled) { - mNetworkProviderIsEnabled = enabled; - } - - /** - * Updates the battery health. Battery level is healthy if there is greater than - * {@link #MIN_BATTERY_LEVEL} percentage left or if the device is plugged in - * - * @param scale maximum scale for battery - * @param level current level - * @param plugged true if device is plugged in - */ - public void updateBatteryState(int scale, int level, boolean plugged) { - mBatteryLevelIsHealthy = (plugged || (level >= (MIN_BATTERY_LEVEL * scale))); - mBatteryChargedAndPlugged = (plugged && (level >= (CHARGED_BATTERY_LEVEL * scale))); - } - - /** - * Anonymous data collection is only enabled when the user has enabled the network - * location provider, i.e. is making use of the service and if the device battery level - * is healthy. - * - * Additionally, data collection will *never* happen if the system - * property ro.com.google.locationfeatures is not set. - * - * @return true if anonymous location collection is enabled - */ - private boolean isCollectionEnabled() { - // This class provides a Google-specific location feature, so it's enabled only - // when the system property ro.com.google.locationfeatures is set. - if (!SystemProperties.get("ro.com.google.locationfeatures").equals("1")) { - return false; - } - return mBatteryLevelIsHealthy && mNetworkProviderIsEnabled; - } - - /** - * Adds to the MASF request queue - * - * @param trigger the event that triggered this collection event - */ - private synchronized void addToQueue(int trigger) { - - long now = SystemClock.elapsedRealtime(); - - // Include location if: - // It has been received in the last 12 minutes. - boolean includeLocation = false; - if (mLastKnownLocation != null && - (now - mLastKnownLocationTime <= MIN_VALID_LOCATION_TIME)) { - includeLocation = true; - } - - // Include wifi if: - // Wifi is new OR - // Wifi is old but last wifi upload was without location - boolean includeWifi = false; - if (trigger == GDebugProfile.TRIGGER_WIFI_CHANGE || (mWifiUploadedWithoutLocation && - includeLocation && (now - mLastWifiScanElapsedTime < MIN_VALID_LOCATION_TIME))) { - includeWifi = true; - mWifiUploadedWithoutLocation = !includeLocation; - } - - // Include cell if: - // Wifi or location information is already being included - // The cell hasn't been uploaded with the same location recently - boolean includeCell = false; - - if (mCellState != null && (includeWifi || includeLocation)) { - includeCell = true; - - if (!includeWifi && includeLocation) { - if (mCellUploads.contains(mCellState, mLastKnownLocation)) { - includeCell = false; - } - } - } - - if (!includeLocation && !includeWifi) { - log("NO UPLOAD: includeLocation=false, includeWifi=false"); - return; - } else if (!includeCell && trigger == GDebugProfile.TRIGGER_CELL_CHANGE) { - log("NO UPLOAD: includeCell=false"); - return; - } else { - log("UPLOAD: includeLocation=" + includeLocation + ", includeWifi=" + - includeWifi + ", includeCell=" + includeCell); - } - - if (includeLocation) { - // Update last uploaded location - if (mLastUploadedLocation == null) { - mLastUploadedLocation = new Location(mLastKnownLocation); - } else { - mLastUploadedLocation.set(mLastKnownLocation); - } - mLastUploadedLocationTime = now; - } - - // Immediately send output if finishing a burst for live traffic requirements - boolean immediate = false; - if (trigger == GDebugProfile.TRIGGER_COLLECTION_END_BURST|| - trigger == GDebugProfile.TRIGGER_COLLECTION_END_BURST_AT_SAME_LOCATION) { - immediate = true; - } - - try { - CellState cell = includeCell ? mCellState : null; - List wifi = includeWifi ? mWifiLastScanResults : null; - Location loc = includeLocation ? mLastUploadedLocation : null; - - mMasfClient.queueCollectionReport( - trigger, loc, cell, wifi, mLastWifiScanRealTime, immediate); - - } catch(Exception e) { - Log.e(TAG, "addToQueue got exception:", e); - } - } - - private class CellUploads { - - private final int MIN_DISTANCE = MIN_DISTANCE_BETWEEN_REPORTS / 4; // 50 meters - private final int SIZE = 5; - private final String[] cells = new String[SIZE]; - private final boolean[] valid = new boolean[SIZE]; - private final double[] latitudes = new double[SIZE]; - private final double[] longitudes = new double[SIZE]; - private final float[] distance = new float[1]; - private int index = 0; - - private CellUploads() { - for (int i = 0; i < SIZE; i++) { - valid[i] = false; - } - } - - private boolean contains(CellState cellState, Location loc) { - String cell = - cellState.getCid() + ":" + cellState.getLac() + ":" + - cellState.getMnc() + ":" + cellState.getMcc(); - double lat = loc.getLatitude(); - double lng = loc.getLongitude(); - - for (int i = 0; i < SIZE; i++) { - if (valid[i] && cells[i].equals(cell)) { - Location.distanceBetween(latitudes[i], longitudes[i], lat, lng, distance); - if (distance[0] < MIN_DISTANCE) { - return true; - } - } - } - cells[index] = cell; - latitudes[index] = lat; - longitudes[index] = lng; - valid[index] = true; - - index++; - if (index == SIZE) { - index = 0; - } - return false; - } - } - - private void log(String string) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, string); - } - } -} diff --git a/location/java/com/android/internal/location/LocationMasfClient.java b/location/java/com/android/internal/location/LocationMasfClient.java deleted file mode 100644 index 179c6300136d6..0000000000000 --- a/location/java/com/android/internal/location/LocationMasfClient.java +++ /dev/null @@ -1,1194 +0,0 @@ -// Copyright 2008 The Android Open Source Project - -package com.android.internal.location; - -import com.google.common.Config; -import com.google.common.android.AndroidConfig; -import com.google.common.io.protocol.ProtoBuf; -import com.google.masf.MobileServiceMux; -import com.google.masf.ServiceCallback; -import com.google.masf.protocol.PlainRequest; -import com.google.masf.protocol.Request; - -import com.android.internal.location.protocol.GAddress; -import com.android.internal.location.protocol.GAddressComponent; -import com.android.internal.location.protocol.GAppProfile; -import com.android.internal.location.protocol.GCell; -import com.android.internal.location.protocol.GCellularPlatformProfile; -import com.android.internal.location.protocol.GCellularProfile; -import com.android.internal.location.protocol.GDebugProfile; -import com.android.internal.location.protocol.GDeviceLocation; -import com.android.internal.location.protocol.GFeature; -import com.android.internal.location.protocol.GGeocodeRequest; -import com.android.internal.location.protocol.GLatLng; -import com.android.internal.location.protocol.GLocReply; -import com.android.internal.location.protocol.GLocReplyElement; -import com.android.internal.location.protocol.GLocRequest; -import com.android.internal.location.protocol.GLocRequestElement; -import com.android.internal.location.protocol.GLocation; -import com.android.internal.location.protocol.GPlatformProfile; -import com.android.internal.location.protocol.GPrefetchMode; -import com.android.internal.location.protocol.GRectangle; -import com.android.internal.location.protocol.GWifiDevice; -import com.android.internal.location.protocol.GWifiProfile; -import com.android.internal.location.protocol.GcellularMessageTypes; -import com.android.internal.location.protocol.GdebugprofileMessageTypes; -import com.android.internal.location.protocol.GlatlngMessageTypes; -import com.android.internal.location.protocol.GlocationMessageTypes; -import com.android.internal.location.protocol.GrectangleMessageTypes; -import com.android.internal.location.protocol.GwifiMessageTypes; -import com.android.internal.location.protocol.LocserverMessageTypes; -import com.android.internal.location.protocol.ResponseCodes; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Locale; - -import android.content.Context; -import android.location.Address; -import android.location.Location; -import android.location.LocationManager; -import android.net.wifi.ScanResult; -import android.os.Build; -import android.os.Bundle; -import android.os.SystemClock; -import android.text.TextUtils; -import android.util.EventLog; -import android.util.Log; -import android.telephony.NeighboringCellInfo; - -/** - * Service to communicate to the Google Location Server (GLS) via MASF server - * - * {@hide} - */ -public class LocationMasfClient { - - static final String TAG = "LocationMasfClient"; - - // Address of the MASF server to connect to. - private static final String MASF_SERVER_ADDRESS = "http://www.google.com/loc/m/api"; - - // MobileServiceMux app/platform-specific values (application name matters!) - private static final String APPLICATION_NAME = "location"; - private static final String APPLICATION_VERSION = "1.0"; - private static final String PLATFORM_ID = "android"; - private static final String DISTRIBUTION_CHANNEL = "android"; - private static String PLATFORM_BUILD = null; - - // Methods exposed by the MASF server - private static final String REQUEST_QUERY_LOC = "g:loc/ql"; - private static final String REQUEST_UPLOAD_LOC = "g:loc/ul"; - - // Max time to wait for request to end - private static final long REQUEST_TIMEOUT = 5000; - - // Constant to divide Lat, Lng returned by server - private static final double E7 = 10000000.0; - - // Max wifi points to include - private static final int MAX_WIFI_TO_INCLUDE = 25; - - // Location of GLS cookie - private static final String PLATFORM_KEY_FILE = "gls.platform.key"; - private String mPlatformKey; - - // Cell cache - private LocationCache mLocationCache; - - // Location object that the cache manages - private Location mLocation = new Location(LocationManager.NETWORK_PROVIDER); - - // ProtoBuf objects we can reuse for subsequent requests - private final int MAX_COLLECTION_BUFFER_SIZE = 30; - private final long MIN_COLLECTION_INTERVAL = 15 * 60 * 1000; // 15 minutes - private ProtoBuf mPlatformProfile = null; - private ProtoBuf mCellularPlatformProfile = null; - private ProtoBuf mCurrentCollectionRequest = null; - private long mLastCollectionUploadTime = 0; - - // Objects for current request - private List mWifiScanResults = new ArrayList(); - private CellState mCellState = null; - private List mCellHistory; - - // This tag is used for the event log. - private static final int COLLECTION_EVENT_LOG_TAG = 2740; - - // Extra values to designate whether location is from cache or network request - private static final String EXTRA_KEY_LOCATION_SOURCE = "networkLocationSource"; - private static final String EXTRA_VALUE_LOCATION_SOURCE_CACHED = "cached"; - private static final String EXTRA_VALUE_LOCATION_SOURCE_SERVER = "server"; - - // Maximum accuracy tolerated for a valid location - private static final int MAX_ACCURACY_ALLOWED = 5000; // 5km - - // Indicates whether this is the first message after a device restart - private boolean mDeviceRestart = true; - - /** - * Initializes the MobileServiceMux. Must be called before using any other function in the - * class. - */ - public LocationMasfClient(Context context) { - MobileServiceMux mux = MobileServiceMux.getSingleton(); - if (mux == null) { - AndroidConfig config = new AndroidConfig(context); - Config.setConfig(config); - - MobileServiceMux.initialize - (MASF_SERVER_ADDRESS, - APPLICATION_NAME, - APPLICATION_VERSION, - PLATFORM_ID, - DISTRIBUTION_CHANNEL); - } - mLocationCache = new LocationCache(); - - if (Build.FINGERPRINT != null) { - PLATFORM_BUILD = PLATFORM_ID + "/" + Build.FINGERPRINT; - } else { - PLATFORM_BUILD = PLATFORM_ID; - } - } - - /** - * Returns the location for the given cell or wifi information. - * - * @param apps list of apps requesting location - * @param trigger event that triggered this network request - * @param cellState cell tower state - * @param cellHistory history of acquired cell states - * @param scanResults list of wifi scan results - * @param scanTime time at which wireless scan was triggered - * @param callback function to call with received location - */ - public synchronized void getNetworkLocation(Collection apps, int trigger, - CellState cellState, List cellHistory, List scanResults, - long scanTime, NetworkLocationProvider.Callback callback) { - - final NetworkLocationProvider.Callback finalCallback = callback; - - boolean foundInCache = - mLocationCache.lookup(cellState, cellHistory, scanResults, mLocation); - - if (foundInCache) { - - if (SystemClock.elapsedRealtime() - mLastCollectionUploadTime > MIN_COLLECTION_INTERVAL) { - uploadCollectionReport(true); - } - - Bundle extras = mLocation.getExtras() == null ? new Bundle() : mLocation.getExtras(); - extras.putString(EXTRA_KEY_LOCATION_SOURCE, EXTRA_VALUE_LOCATION_SOURCE_CACHED); - mLocation.setExtras(extras); - - Log.d(TAG, "getNetworkLocation(): Returning cache location with accuracy " + - mLocation.getAccuracy()); - finalCallback.locationReceived(mLocation, true); - return; - } - - Log.d(TAG, "getNetworkLocation(): Location not found in cache, making network request"); - - // Copy over to objects for this request - mWifiScanResults.clear(); - if (scanResults != null) { - mWifiScanResults.addAll(scanResults); - } - mCellState = cellState; - mCellHistory = cellHistory; - - // Create a RequestElement - ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT); - - // Debug profile - ProtoBuf debugProfile = new ProtoBuf(GdebugprofileMessageTypes.GDEBUG_PROFILE); - requestElement.setProtoBuf(GLocRequestElement.DEBUG_PROFILE, debugProfile); - - if (mDeviceRestart) { - debugProfile.setBool(GDebugProfile.DEVICE_RESTART, true); - mDeviceRestart = false; - } - - if (trigger != -1) { - debugProfile.setInt(GDebugProfile.TRIGGER, trigger); - } - - // Cellular profile - if (mCellState != null && mCellState.isValid()) { - ProtoBuf cellularProfile = new ProtoBuf(GcellularMessageTypes.GCELLULAR_PROFILE); - cellularProfile.setLong(GCellularProfile.TIMESTAMP, mCellState.getTime()); - cellularProfile.setInt(GCellularProfile.PREFETCH_MODE, - GPrefetchMode.PREFETCH_MODE_MORE_NEIGHBORS); - - // Primary cell - ProtoBuf primaryCell = new ProtoBuf(GcellularMessageTypes.GCELL); - primaryCell.setInt(GCell.LAC, mCellState.getLac()); - primaryCell.setInt(GCell.CELLID, mCellState.getCid()); - - if ((mCellState.getMcc() != -1) && (mCellState.getMnc() != -1)) { - primaryCell.setInt(GCell.MCC, mCellState.getMcc()); - primaryCell.setInt(GCell.MNC, mCellState.getMnc()); - } - - if (mCellState.getSignalStrength() != -1) { - primaryCell.setInt(GCell.RSSI, mCellState.getSignalStrength()); - } - - cellularProfile.setProtoBuf(GCellularProfile.PRIMARY_CELL, primaryCell); - - // History of cells - for (CellState c : cellHistory) { - ProtoBuf pastCell = new ProtoBuf(GcellularMessageTypes.GCELL); - pastCell.setInt(GCell.LAC, c.getLac()); - pastCell.setInt(GCell.CELLID, c.getCid()); - if ((c.getMcc() != -1) && (c.getMnc() != -1)) { - pastCell.setInt(GCell.MCC, c.getMcc()); - pastCell.setInt(GCell.MNC, c.getMnc()); - } - - if (c.getSignalStrength() != -1) { - pastCell.setInt(GCell.RSSI, c.getSignalStrength()); - } - - pastCell.setInt(GCell.AGE, (int)(mCellState.getTime() - c.getTime())); - cellularProfile.addProtoBuf(GCellularProfile.HISTORICAL_CELLS, pastCell); - } - - // Neighboring Cells - addNeighborsToCellProfile(mCellState, cellularProfile); - - requestElement.setProtoBuf(GLocRequestElement.CELLULAR_PROFILE, cellularProfile); - } - - // Wifi profile - if (mWifiScanResults != null && mWifiScanResults.size() > 0) { - ProtoBuf wifiProfile = new ProtoBuf(GwifiMessageTypes.GWIFI_PROFILE); - wifiProfile.setLong(GWifiProfile.TIMESTAMP, scanTime); - wifiProfile.setInt(GWifiProfile.PREFETCH_MODE, - GPrefetchMode.PREFETCH_MODE_MORE_NEIGHBORS); - - int count = 0; - for (ScanResult s : mWifiScanResults) { - ProtoBuf wifiDevice = new ProtoBuf(GwifiMessageTypes.GWIFI_DEVICE); - wifiDevice.setString(GWifiDevice.MAC, s.BSSID); - wifiProfile.addProtoBuf(GWifiProfile.WIFI_DEVICES, wifiDevice); - count++; - if (count >= MAX_WIFI_TO_INCLUDE) { - break; - } - } - - requestElement.setProtoBuf(GLocRequestElement.WIFI_PROFILE, wifiProfile); - } - - // Request to send over wire - ProtoBuf request = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST); - request.addProtoBuf(GLocRequest.REQUEST_ELEMENTS, requestElement); - - // Create a Platform Profile - ProtoBuf platformProfile = createPlatformProfile(); - if (mCellState != null && mCellState.isValid()) { - // Include cellular platform Profile - ProtoBuf cellularPlatform = createCellularPlatformProfile(mCellState); - platformProfile.setProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE, - cellularPlatform); - } - request.setProtoBuf(GLocRequest.PLATFORM_PROFILE, platformProfile); - - // Include App Profiles - if (apps != null) { - for (String app : apps) { - ProtoBuf appProfile = new ProtoBuf(GlocationMessageTypes.GAPP_PROFILE); - appProfile.setString(GAppProfile.APP_NAME, app); - request.addProtoBuf(GLocRequest.APP_PROFILES, appProfile); - } - } - - // Queue any waiting collection events as well - uploadCollectionReport(false); - - ByteArrayOutputStream payload = new ByteArrayOutputStream(); - try { - request.outputTo(payload); - } catch (IOException e) { - Log.e(TAG, "getNetworkLocation(): unable to write request to payload", e); - return; - } - - // Creates request and a listener with a call back function - ProtoBuf reply = new ProtoBuf(LocserverMessageTypes.GLOC_REPLY); - Request plainRequest = - new PlainRequest(REQUEST_QUERY_LOC, (short)0, payload.toByteArray()); - - ProtoRequestListener listener = new ProtoRequestListener(reply, new ServiceCallback() { - public void onRequestComplete(Object result) { - ProtoBuf response = (ProtoBuf) result; - boolean successful = parseNetworkLocationReply(response); - finalCallback.locationReceived(mLocation, successful); - - } - }); - plainRequest.setListener(listener); - - // Send request - MobileServiceMux serviceMux = MobileServiceMux.getSingleton(); - serviceMux.submitRequest(plainRequest, true); - } - - private synchronized boolean parseNetworkLocationReply(ProtoBuf response) { - if (response == null) { - Log.e(TAG, "getNetworkLocation(): response is null"); - return false; - } - - int status1 = response.getInt(GLocReply.STATUS); - if (status1 != ResponseCodes.STATUS_STATUS_SUCCESS) { - Log.e(TAG, "getNetworkLocation(): RPC failed with status " + status1); - return false; - } - - if (response.has(GLocReply.PLATFORM_KEY)) { - String platformKey = response.getString(GLocReply.PLATFORM_KEY); - if (!TextUtils.isEmpty(platformKey)) { - setPlatformKey(platformKey); - } - } - - if (!response.has(GLocReply.REPLY_ELEMENTS)) { - Log.e(TAG, "getNetworkLocation(): no ReplyElement"); - return false; - } - ProtoBuf replyElement = response.getProtoBuf(GLocReply.REPLY_ELEMENTS); - - // Get prefetched data to add to the device cache - Log.d(TAG, "getNetworkLocation(): Number of prefetched entries " + - replyElement.getCount(GLocReplyElement.DEVICE_LOCATION)); - long now = System.currentTimeMillis(); - for (int i = 0; i < replyElement.getCount(GLocReplyElement.DEVICE_LOCATION); i++ ) { - ProtoBuf device = replyElement.getProtoBuf(GLocReplyElement.DEVICE_LOCATION, i); - double lat = 0; - double lng = 0; - int accuracy = -1; - int confidence = -1; - int locType = -1; - if (device.has(GDeviceLocation.LOCATION)) { - ProtoBuf deviceLocation = device.getProtoBuf(GDeviceLocation.LOCATION); - if (deviceLocation.has(GLocation.ACCURACY) && - deviceLocation.has(GLocation.LAT_LNG) - && deviceLocation.has(GLocation.CONFIDENCE)) { - lat = deviceLocation.getProtoBuf(GLocation.LAT_LNG). - getInt(GLatLng.LAT_E7) / E7; - lng = deviceLocation.getProtoBuf(GLocation.LAT_LNG). - getInt(GLatLng.LNG_E7) / E7; - accuracy = deviceLocation.getInt(GLocation.ACCURACY); - confidence = deviceLocation.getInt(GLocation.CONFIDENCE); - } - if (deviceLocation.has(GLocation.LOC_TYPE)) { - locType = deviceLocation.getInt(GLocation.LOC_TYPE); - } - } - - // Get cell key - if (device.has(GDeviceLocation.CELL) && locType != GLocation.LOCTYPE_TOWER_LOCATION) { - ProtoBuf deviceCell = device.getProtoBuf(GDeviceLocation.CELL); - int cid = deviceCell.getInt(GCell.CELLID); - int lac = deviceCell.getInt(GCell.LAC); - int mcc = -1; - int mnc = -1; - if (deviceCell.has(GCell.MNC) && deviceCell.has(GCell.MCC)) { - mcc = deviceCell.getInt(GCell.MCC); - mnc = deviceCell.getInt(GCell.MNC); - } - mLocationCache. - insert(mcc, mnc, lac, cid, lat, lng, accuracy, confidence, now); - } - - // Get wifi key - if (device.has(GDeviceLocation.WIFI_DEVICE)) { - ProtoBuf deviceWifi = device.getProtoBuf(GDeviceLocation.WIFI_DEVICE); - String bssid = deviceWifi.getString(GWifiDevice.MAC); - mLocationCache.insert(bssid, lat, lng, accuracy, confidence, now); - } - } - - mLocationCache.save(); - - int status2 = replyElement.getInt(GLocReplyElement.STATUS); - if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS && - status2 != ResponseCodes.STATUS_STATUS_FAILED) { - Log.e(TAG, "getNetworkLocation(): GLS failed with status " + status2); - return false; - } - - // For consistent results for user, always return cache computed location - boolean foundInCache = - mLocationCache.lookup(mCellState, mCellHistory, mWifiScanResults, mLocation); - - if (foundInCache) { - - Bundle extras = mLocation.getExtras() == null ? new Bundle() : mLocation.getExtras(); - extras.putString(EXTRA_KEY_LOCATION_SOURCE, EXTRA_VALUE_LOCATION_SOURCE_SERVER); - mLocation.setExtras(extras); - - Log.d(TAG, "getNetworkLocation(): Returning network location with accuracy " + - mLocation.getAccuracy()); - return true; - } - - if (status2 == ResponseCodes.STATUS_STATUS_FAILED) { - Log.e(TAG, "getNetworkLocation(): GLS does not have location"); - // We return true here since even though there is no location, there is no need to retry - // since server doesn't have location - return true; - } - - // Get server computed location to return for now - if (!replyElement.has(GLocReplyElement.LOCATION)) { - Log.e(TAG, "getNetworkLocation(): no location in ReplyElement"); - return false; - } - ProtoBuf location = replyElement.getProtoBuf(GLocReplyElement.LOCATION); - - if (!location.has(GLocation.LAT_LNG)) { - Log.e(TAG, "getNetworkLocation(): no Lat,Lng in location"); - return false; - } - - ProtoBuf point = location.getProtoBuf(GLocation.LAT_LNG); - double lat = point.getInt(GLatLng.LAT_E7) / E7; - double lng = point.getInt(GLatLng.LNG_E7) / E7; - - int accuracy = 0; - if (location.has(GLocation.ACCURACY)) { - accuracy = location.getInt(GLocation.ACCURACY); - } - - if (accuracy > MAX_ACCURACY_ALLOWED) { - Log.e(TAG, "getNetworkLocation(): accuracy is too high " + accuracy); - return false; - } - - mLocation.setLatitude(lat); - mLocation.setLongitude(lng); - mLocation.setTime(System.currentTimeMillis()); - mLocation.setAccuracy(accuracy); - - Bundle extras = mLocation.getExtras() == null ? new Bundle() : mLocation.getExtras(); - extras.putString(EXTRA_KEY_LOCATION_SOURCE, EXTRA_VALUE_LOCATION_SOURCE_SERVER); - mLocation.setExtras(extras); - - Log.e(TAG, "getNetworkLocation(): Returning *server* computed location with accuracy " + - accuracy); - - return true; - } - - /** - * Gets a reverse geocoded location from the given lat,lng point. Also attaches the name - * of the requesting application with the request - * - * @param locale locale for geocoded location - * @param appPackageName name of the package, may be null - * @param lat latitude - * @param lng longitude - * @param maxResults maximum number of addresses to return - * @param addrs the list of addresses to fill up - * @throws IOException if network is unavailable or some other issue - */ - public void reverseGeocode(Locale locale, String appPackageName, - double lat, double lng, int maxResults, List
      addrs) throws IOException { - - // Reverse geocoding request element - ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT); - - ProtoBuf latlngElement = new ProtoBuf(GlatlngMessageTypes.GLAT_LNG); - latlngElement.setInt(GLatLng.LAT_E7, (int)(lat * E7)); - latlngElement.setInt(GLatLng.LNG_E7, (int)(lng * E7)); - - ProtoBuf locationElement = new ProtoBuf(GlocationMessageTypes.GLOCATION); - locationElement.setProtoBuf(GLocation.LAT_LNG, latlngElement); - locationElement.setLong(GLocation.TIMESTAMP, System.currentTimeMillis()); - requestElement.setProtoBuf(GLocRequestElement.LOCATION, locationElement); - - ProtoBuf geocodeElement = - new ProtoBuf(LocserverMessageTypes.GGEOCODE_REQUEST); - geocodeElement.setInt(GGeocodeRequest.NUM_FEATURE_LIMIT, maxResults); - requestElement.setProtoBuf(GLocRequestElement.GEOCODE, geocodeElement); - - // Request to send over wire - ProtoBuf request = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST); - request.addProtoBuf(GLocRequest.REQUEST_ELEMENTS, requestElement); - - // Create platform profile - ProtoBuf platformProfile = createPlatformProfile(locale); - request.setProtoBuf(GLocRequest.PLATFORM_PROFILE, platformProfile); - - // Include app name - if (appPackageName != null) { - ProtoBuf appProfile = new ProtoBuf(GlocationMessageTypes.GAPP_PROFILE); - appProfile.setString(GAppProfile.APP_NAME, appPackageName); - request.setProtoBuf(GLocRequest.APP_PROFILES, appProfile); - } - - // Queue any waiting collection events as well - uploadCollectionReport(false); - - ByteArrayOutputStream payload = new ByteArrayOutputStream(); - try { - request.outputTo(payload); - } catch (IOException e) { - Log.e(TAG, "reverseGeocode(): unable to write request to payload"); - throw e; - } - - // Creates request and a listener with no callback function - ProtoBuf reply = new ProtoBuf(LocserverMessageTypes.GLOC_REPLY); - Request plainRequest = - new PlainRequest(REQUEST_QUERY_LOC, (short)0, payload.toByteArray()); - ProtoRequestListener listener = new ProtoRequestListener(reply, null); - plainRequest.setListener(listener); - - // Immediately send request and block for response until REQUEST_TIMEOUT - MobileServiceMux serviceMux = MobileServiceMux.getSingleton(); - serviceMux.submitRequest(plainRequest, true); - ProtoBuf response; - try { - response = (ProtoBuf)listener.getAsyncResult().get(REQUEST_TIMEOUT); - } catch (InterruptedException e) { - Log.e(TAG, "reverseGeocode(): response timeout"); - throw new IOException("response time-out"); - } - - if (response == null) { - throw new IOException("Unable to parse response from server"); - } - - // Parse the response - int status1 = response.getInt(GLocReply.STATUS); - if (status1 != ResponseCodes.STATUS_STATUS_SUCCESS) { - Log.e(TAG, "reverseGeocode(): RPC failed with status " + status1); - throw new IOException("RPC failed with status " + status1); - } - - if (response.has(GLocReply.PLATFORM_KEY)) { - String platformKey = response.getString(GLocReply.PLATFORM_KEY); - if (!TextUtils.isEmpty(platformKey)) { - setPlatformKey(platformKey); - } - } - - if (!response.has(GLocReply.REPLY_ELEMENTS)) { - Log.e(TAG, "reverseGeocode(): no ReplyElement"); - return; - } - ProtoBuf replyElement = response.getProtoBuf(GLocReply.REPLY_ELEMENTS); - - int status2 = replyElement.getInt(GLocReplyElement.STATUS); - if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS) { - Log.e(TAG, "reverseGeocode(): GLS failed with status " + status2); - return; - } - - if (!replyElement.has(GLocReplyElement.LOCATION)) { - Log.e(TAG, "reverseGeocode(): no location in ReplyElement"); - return; - } - - ProtoBuf location = replyElement.getProtoBuf(GLocReplyElement.LOCATION); - if (!location.has(GLocation.FEATURE)) { - Log.e(TAG, "reverseGeocode(): no feature in GLocation"); - return; - } - - getAddressFromProtoBuf(location, locale, addrs); - } - - /** - * Gets a forward geocoded location from the given location string. Also attaches the name - * of the requesting application with the request - * - * Optionally, can specify the bounding box that the search results should be restricted to - * - * @param locale locale for geocoded location - * @param appPackageName name of the package, may be null - * @param locationString string to forward geocode - * @param lowerLeftLatitude latitude of lower left point of bounding box - * @param lowerLeftLongitude longitude of lower left point of bounding box - * @param upperRightLatitude latitude of upper right point of bounding box - * @param upperRightLongitude longitude of upper right point of bounding box - * @param maxResults maximum number of results to return - * @param addrs the list of addresses to fill up - * @throws IOException if network is unavailable or some other issue - */ - public void forwardGeocode(Locale locale, String appPackageName, String locationString, - double lowerLeftLatitude, double lowerLeftLongitude, - double upperRightLatitude, double upperRightLongitude, int maxResults, List
      addrs) - throws IOException { - - // Forward geocoding request element - ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT); - - ProtoBuf locationElement = new ProtoBuf(GlocationMessageTypes.GLOCATION); - locationElement.setLong(GLocation.TIMESTAMP, System.currentTimeMillis()); - locationElement.setString(GLocation.LOCATION_STRING, locationString); - requestElement.setProtoBuf(GLocRequestElement.LOCATION, locationElement); - - ProtoBuf geocodeElement = - new ProtoBuf(LocserverMessageTypes.GGEOCODE_REQUEST); - geocodeElement.setInt(GGeocodeRequest.NUM_FEATURE_LIMIT, maxResults); - - if (lowerLeftLatitude != 0 && lowerLeftLongitude !=0 && - upperRightLatitude !=0 && upperRightLongitude !=0) { - ProtoBuf lowerLeft = new ProtoBuf(GlatlngMessageTypes.GLAT_LNG); - lowerLeft.setInt(GLatLng.LAT_E7, (int)(lowerLeftLatitude * E7)); - lowerLeft.setInt(GLatLng.LNG_E7, (int)(lowerLeftLongitude * E7)); - - ProtoBuf upperRight = new ProtoBuf(GlatlngMessageTypes.GLAT_LNG); - upperRight.setInt(GLatLng.LAT_E7, (int)(upperRightLatitude * E7)); - upperRight.setInt(GLatLng.LNG_E7, (int)(upperRightLongitude * E7)); - - ProtoBuf boundingBox = new ProtoBuf(GrectangleMessageTypes.GRECTANGLE); - boundingBox.setProtoBuf(GRectangle.LOWER_LEFT, lowerLeft); - boundingBox.setProtoBuf(GRectangle.UPPER_RIGHT, upperRight); - geocodeElement.setProtoBuf(GGeocodeRequest.BOUNDING_BOX, boundingBox); - } - requestElement.setProtoBuf(GLocRequestElement.GEOCODE, geocodeElement); - - // Request to send over wire - ProtoBuf request = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST); - request.addProtoBuf(GLocRequest.REQUEST_ELEMENTS, requestElement); - - // Create platform profile - ProtoBuf platformProfile = createPlatformProfile(locale); - request.setProtoBuf(GLocRequest.PLATFORM_PROFILE, platformProfile); - - // Include app name - if (appPackageName != null) { - ProtoBuf appProfile = new ProtoBuf(GlocationMessageTypes.GAPP_PROFILE); - appProfile.setString(GAppProfile.APP_NAME, appPackageName); - request.setProtoBuf(GLocRequest.APP_PROFILES, appProfile); - } - - // Queue any waiting collection events as well - uploadCollectionReport(false); - - ByteArrayOutputStream payload = new ByteArrayOutputStream(); - try { - request.outputTo(payload); - } catch (IOException e) { - Log.e(TAG, "forwardGeocode(): unable to write request to payload"); - throw e; - } - - // Creates request and a listener with no callback function - ProtoBuf reply = new ProtoBuf(LocserverMessageTypes.GLOC_REPLY); - Request plainRequest = - new PlainRequest(REQUEST_QUERY_LOC, (short)0, payload.toByteArray()); - ProtoRequestListener listener = new ProtoRequestListener(reply, null); - plainRequest.setListener(listener); - - // Immediately send request and block for response until REQUEST_TIMEOUT - MobileServiceMux serviceMux = MobileServiceMux.getSingleton(); - serviceMux.submitRequest(plainRequest, true); - ProtoBuf response; - try { - response = (ProtoBuf)listener.getAsyncResult().get(REQUEST_TIMEOUT); - } catch (InterruptedException e) { - Log.e(TAG, "forwardGeocode(): response timeout"); - throw new IOException("response time-out"); - } - - if (response == null) { - throw new IOException("Unable to parse response from server"); - } - - // Parse the response - int status1 = response.getInt(GLocReply.STATUS); - if (status1 != ResponseCodes.STATUS_STATUS_SUCCESS) { - Log.e(TAG, "forwardGeocode(): RPC failed with status " + status1); - throw new IOException("RPC failed with status " + status1); - } - - if (response.has(GLocReply.PLATFORM_KEY)) { - String platformKey = response.getString(GLocReply.PLATFORM_KEY); - if (!TextUtils.isEmpty(platformKey)) { - setPlatformKey(platformKey); - } - } - - if (!response.has(GLocReply.REPLY_ELEMENTS)) { - Log.e(TAG, "forwardGeocode(): no ReplyElement"); - return; - } - ProtoBuf replyElement = response.getProtoBuf(GLocReply.REPLY_ELEMENTS); - - int status2 = replyElement.getInt(GLocReplyElement.STATUS); - if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS) { - Log.e(TAG, "forwardGeocode(): GLS failed with status " + status2); - return; - } - - if (!replyElement.has(GLocReplyElement.LOCATION)) { - Log.e(TAG, "forwardGeocode(): no location in ReplyElement"); - return; - } - - ProtoBuf location = replyElement.getProtoBuf(GLocReplyElement.LOCATION); - if (!location.has(GLocation.FEATURE)) { - Log.e(TAG, "forwardGeocode(): no feature in GLocation"); - return; - } - - getAddressFromProtoBuf(location, locale, addrs); - } - - /** - * Queues a location collection request to be sent to the server - * - * @param trigger what triggered this collection event - * @param location last known location - * @param cellState cell tower state - * @param scanResults list of wifi points - * @param scanTime real time at which wifi scan happened - * @param immediate true if request should be sent immediately instead of being queued - */ - public synchronized void queueCollectionReport(int trigger, Location location, - CellState cellState, List scanResults, long scanTime, boolean immediate) { - - // Create a RequestElement - ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT); - - // Include debug profile - ProtoBuf debugProfile = new ProtoBuf(GdebugprofileMessageTypes.GDEBUG_PROFILE); - requestElement.setProtoBuf(GLocRequestElement.DEBUG_PROFILE, debugProfile); - - if (mDeviceRestart) { - debugProfile.setBool(GDebugProfile.DEVICE_RESTART, true); - mDeviceRestart = false; - } - - if (trigger != -1) { - debugProfile.setInt(GDebugProfile.TRIGGER, trigger); - EventLog.writeEvent(COLLECTION_EVENT_LOG_TAG, trigger); - } - - // Include cell profile - if (cellState != null && cellState.isValid()) { - ProtoBuf cellularProfile = new ProtoBuf(GcellularMessageTypes.GCELLULAR_PROFILE); - cellularProfile.setLong(GCellularProfile.TIMESTAMP, cellState.getTime()); - - // Primary cell - ProtoBuf primaryCell = new ProtoBuf(GcellularMessageTypes.GCELL); - primaryCell.setInt(GCell.LAC, cellState.getLac()); - primaryCell.setInt(GCell.CELLID, cellState.getCid()); - if ((cellState.getMcc() != -1) && (cellState.getMnc() != -1)) { - primaryCell.setInt(GCell.MCC, cellState.getMcc()); - primaryCell.setInt(GCell.MNC, cellState.getMnc()); - } - - if (cellState.getSignalStrength() != -1) { - primaryCell.setInt(GCell.RSSI, cellState.getSignalStrength()); - } - - cellularProfile.setProtoBuf(GCellularProfile.PRIMARY_CELL, primaryCell); - - // Cell neighbors - addNeighborsToCellProfile(cellState, cellularProfile); - - requestElement.setProtoBuf(GLocRequestElement.CELLULAR_PROFILE, cellularProfile); - } - - // Include Wifi profile - if (scanResults != null && scanResults.size() > 0) { - ProtoBuf wifiProfile = new ProtoBuf(GwifiMessageTypes.GWIFI_PROFILE); - wifiProfile.setLong(GWifiProfile.TIMESTAMP, scanTime); - - int count = 0; - for (ScanResult s : scanResults) { - ProtoBuf wifiDevice = new ProtoBuf(GwifiMessageTypes.GWIFI_DEVICE); - wifiDevice.setString(GWifiDevice.MAC, s.BSSID); - wifiDevice.setString(GWifiDevice.SSID, s.SSID); - wifiDevice.setInt(GWifiDevice.RSSI, s.level); - wifiProfile.addProtoBuf(GWifiProfile.WIFI_DEVICES, wifiDevice); - count++; - if (count >= MAX_WIFI_TO_INCLUDE) { - break; - } - } - - requestElement.setProtoBuf(GLocRequestElement.WIFI_PROFILE, wifiProfile); - } - - // Location information - if (location != null) { - ProtoBuf latlngElement = new ProtoBuf(GlatlngMessageTypes.GLAT_LNG); - latlngElement.setInt(GLatLng.LAT_E7, (int)(location.getLatitude() * E7)); - latlngElement.setInt(GLatLng.LNG_E7, (int)(location.getLongitude() * E7)); - - ProtoBuf locationElement = new ProtoBuf(GlocationMessageTypes.GLOCATION); - locationElement.setProtoBuf(GLocation.LAT_LNG, latlngElement); - locationElement.setInt(GLocation.LOC_TYPE, GLocation.LOCTYPE_GPS); - locationElement.setLong(GLocation.TIMESTAMP, location.getTime()); - if (location.hasAccuracy()) { - locationElement.setInt(GLocation.ACCURACY, (int)location.getAccuracy()); - } - if (location.hasSpeed()) { - locationElement.setInt(GLocation.VELOCITY, (int)location.getSpeed()); - } - if (location.hasBearing()) { - locationElement.setInt(GLocation.HEADING, (int)location.getBearing()); - } - - requestElement.setProtoBuf(GLocRequestElement.LOCATION, locationElement); - } - - if (mCurrentCollectionRequest == null) { - mCurrentCollectionRequest = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST); - - // Create a Platform Profile - ProtoBuf platformProfile = createPlatformProfile(); - if (cellState != null && cellState.isValid()) { - ProtoBuf cellularPlatform = createCellularPlatformProfile(cellState); - platformProfile.setProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE, - cellularPlatform); - } - - mCurrentCollectionRequest.setProtoBuf(GLocRequest.PLATFORM_PROFILE, platformProfile); - - } else { - - ProtoBuf platformProfile = - mCurrentCollectionRequest.getProtoBuf(GLocRequest.PLATFORM_PROFILE); - if (platformProfile == null) { - platformProfile = createPlatformProfile(); - mCurrentCollectionRequest.setProtoBuf( - GLocRequest.PLATFORM_PROFILE, platformProfile); - } - - // Add cellular platform profile is not already included - if (platformProfile.getProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE) == null && - cellState != null && cellState.isValid()) { - ProtoBuf cellularPlatform = createCellularPlatformProfile(cellState); - platformProfile.setProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE, - cellularPlatform); - } - } - - mCurrentCollectionRequest.addProtoBuf(GLocRequest.REQUEST_ELEMENTS, requestElement); - - // Immediately upload collection events if buffer exceeds certain size - if (mCurrentCollectionRequest.getCount(GLocRequest.REQUEST_ELEMENTS) - >= MAX_COLLECTION_BUFFER_SIZE) { - immediate = true; - } - - if (immediate) { - // Request to send over wire - uploadCollectionReport(immediate); - } - } - - /** - * Uploads the collection report either immediately or based on MASF's queueing logic. - * Does not need a reply back - * - * @param immediate true if request should be sent immediately instead of being queued - */ - private synchronized void uploadCollectionReport(boolean immediate) { - // There may be nothing to upload - if (mCurrentCollectionRequest == null || - mCurrentCollectionRequest.getCount(GLocRequest.REQUEST_ELEMENTS) == 0) { - return; - } - - ByteArrayOutputStream payload = new ByteArrayOutputStream(); - try { - mCurrentCollectionRequest.outputTo(payload); - } catch (IOException e) { - Log.e(TAG, "uploadCollectionReport(): unable to write request to payload"); - return; - } - - mLastCollectionUploadTime = SystemClock.elapsedRealtime(); - - // Since this has already been written to the wire, we can clear this request - int count = mCurrentCollectionRequest.getCount(GLocRequest.REQUEST_ELEMENTS); - while (count > 0) { - mCurrentCollectionRequest.remove(GLocRequest.REQUEST_ELEMENTS, count - 1); - count--; - } - - // Creates request and a listener with a call back function - ProtoBuf reply = new ProtoBuf(LocserverMessageTypes.GLOC_REPLY); - Request plainRequest = - new PlainRequest(REQUEST_UPLOAD_LOC, (short)0, payload.toByteArray()); - - ProtoRequestListener listener = new ProtoRequestListener(reply, new ServiceCallback() { - public void onRequestComplete(Object result) { - ProtoBuf response = (ProtoBuf) result; - - if (response == null) { - Log.e(TAG, "uploadCollectionReport(): response is null"); - return; - } - - int status1 = response.getInt(GLocReply.STATUS); - if (status1 != ResponseCodes.STATUS_STATUS_SUCCESS) { - Log.w(TAG, "uploadCollectionReport(): RPC failed with status " + status1); - return; - } - - if (response.has(GLocReply.PLATFORM_KEY)) { - String platformKey = response.getString(GLocReply.PLATFORM_KEY); - if (!TextUtils.isEmpty(platformKey)) { - setPlatformKey(platformKey); - } - } - - if (!response.has(GLocReply.REPLY_ELEMENTS)) { - Log.w(TAG, "uploadCollectionReport(): no ReplyElement"); - return; - } - - int count = response.getCount(GLocReply.REPLY_ELEMENTS); - for (int i = 0; i < count; i++) { - ProtoBuf replyElement = response.getProtoBuf(GLocReply.REPLY_ELEMENTS, i); - int status2 = replyElement.getInt(GLocReplyElement.STATUS); - if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS) { - Log.w(TAG, "uploadCollectionReport(): GLS failed with " + status2); - } - } - - } - }); - plainRequest.setListener(listener); - - // Send request - MobileServiceMux serviceMux = MobileServiceMux.getSingleton(); - serviceMux.submitRequest(plainRequest, immediate); - - } - - private String getPlatformKey() { - if (mPlatformKey != null) { - return mPlatformKey; - } - - try { - File file = new File(LocationManager.SYSTEM_DIR, PLATFORM_KEY_FILE); - FileInputStream istream = new FileInputStream(file); - DataInputStream dataInput = new DataInputStream(istream); - String platformKey = dataInput.readUTF(); - dataInput.close(); - mPlatformKey = platformKey; - return mPlatformKey; - } catch(FileNotFoundException e) { - // No file, just ignore - return null; - } catch(IOException e) { - // Unable to read from file, just ignore - return null; - } - } - - private void setPlatformKey(String platformKey) { - File systemDir = new File(LocationManager.SYSTEM_DIR); - if (!systemDir.exists()) { - if (!systemDir.mkdirs()) { - Log.w(TAG, "setPlatformKey(): couldn't create directory"); - return; - } - } - - try { - File file = new File(LocationManager.SYSTEM_DIR, PLATFORM_KEY_FILE); - FileOutputStream ostream = new FileOutputStream(file); - DataOutputStream dataOut = new DataOutputStream(ostream); - dataOut.writeUTF(platformKey); - dataOut.close(); - mPlatformKey = platformKey; - } catch (FileNotFoundException e) { - Log.w(TAG, "setPlatformKey(): unable to create platform key file"); - } catch (IOException e) { - Log.w(TAG, "setPlatformKey(): unable to write to platform key"); - } - } - - private ProtoBuf createPlatformProfile() { - Locale locale = Locale.getDefault(); - return createPlatformProfile(locale); - } - - private ProtoBuf createPlatformProfile(Locale locale) { - if (mPlatformProfile == null) { - mPlatformProfile = new ProtoBuf(GlocationMessageTypes.GPLATFORM_PROFILE); - mPlatformProfile.setString(GPlatformProfile.VERSION, APPLICATION_VERSION); - mPlatformProfile.setString(GPlatformProfile.PLATFORM, PLATFORM_BUILD); - } - - // Add Locale - if ((locale != null) && (locale.toString() != null)) { - mPlatformProfile.setString(GPlatformProfile.LOCALE, locale.toString()); - } - - // Add Platform Key - String platformKey = getPlatformKey(); - if (!TextUtils.isEmpty(platformKey)) { - mPlatformProfile.setString(GPlatformProfile.PLATFORM_KEY, platformKey); - } - - // Clear out cellular platform profile - mPlatformProfile.setProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE, null); - - return mPlatformProfile; - } - - private ProtoBuf createCellularPlatformProfile(CellState cellState) { - // Radio type - int radioType = -1; - if (cellState.getRadioType() == CellState.RADIO_TYPE_GPRS) { - radioType = GCellularPlatformProfile.RADIO_TYPE_GPRS; - } else if (cellState.getRadioType() == CellState.RADIO_TYPE_CDMA) { - radioType = GCellularPlatformProfile.RADIO_TYPE_CDMA; - } else if (cellState.getRadioType() == CellState.RADIO_TYPE_WCDMA) { - radioType = GCellularPlatformProfile.RADIO_TYPE_WCDMA; - } - - if (mCellularPlatformProfile == null) { - mCellularPlatformProfile = - new ProtoBuf(GlocationMessageTypes.GCELLULAR_PLATFORM_PROFILE); - } - - mCellularPlatformProfile.setInt(GCellularPlatformProfile.RADIO_TYPE, radioType); - if ((cellState.getHomeMcc() != -1) && (cellState.getHomeMnc() != -1)) { - mCellularPlatformProfile.setInt(GCellularPlatformProfile.HOME_MCC, - cellState.getHomeMcc()); - mCellularPlatformProfile.setInt(GCellularPlatformProfile.HOME_MNC, - cellState.getHomeMnc()); - } - if (cellState.getCarrier() != null) { - mCellularPlatformProfile.setString(GCellularPlatformProfile.CARRIER, - cellState.getCarrier()); - } - - return mCellularPlatformProfile; - } - - private void getAddressFromProtoBuf(ProtoBuf location, Locale locale, List
      addrs) { - - double lat = -1; - double lng = -1; - - if (location.has(GLocation.LAT_LNG)) { - ProtoBuf latlng = location.getProtoBuf(GLocation.LAT_LNG); - lat = latlng.getInt(GLatLng.LAT_E7)/E7; - lng = latlng.getInt(GLatLng.LNG_E7)/E7; - } - - for (int a = 0; a < location.getCount(GLocation.FEATURE); a++) { - - Address output = new Address(locale); - - ProtoBuf feature = location.getProtoBuf(GLocation.FEATURE, a); - output.setFeatureName(feature.getString(GFeature.NAME)); - - if (feature.has(GFeature.CENTER)) { - ProtoBuf center = feature.getProtoBuf(GFeature.CENTER); - output.setLatitude(center.getInt(GLatLng.LAT_E7)/E7); - output.setLongitude(center.getInt(GLatLng.LNG_E7)/E7); - - } else if (location.has(GLocation.LAT_LNG)) { - output.setLatitude(lat); - output.setLongitude(lng); - } - - ProtoBuf address = feature.getProtoBuf(GFeature.ADDRESS); - - for (int i = 0; i < address.getCount(GAddress.FORMATTED_ADDRESS_LINE); i++) { - String line = address.getString(GAddress.FORMATTED_ADDRESS_LINE, i); - output.setAddressLine(i, line); - } - - for (int i = 0; i < address.getCount(GAddress.COMPONENT); i++) { - ProtoBuf component = address.getProtoBuf(GAddress.COMPONENT, i); - int type = component.getInt(GAddressComponent.FEATURE_TYPE); - String name = component.getString(GAddressComponent.NAME); - - switch(type) { - case GFeature.FEATURE_TYPE_ADMINISTRATIVE_AREA : - output.setAdminArea(name); - break; - - case GFeature.FEATURE_TYPE_SUB_ADMINISTRATIVE_AREA : - output.setSubAdminArea(name); - break; - - case GFeature.FEATURE_TYPE_LOCALITY : - output.setLocality(name); - break; - - case GFeature.FEATURE_TYPE_THOROUGHFARE : - output.setThoroughfare(name); - break; - - case GFeature.FEATURE_TYPE_POST_CODE : - output.setPostalCode(name); - break; - - case GFeature.FEATURE_TYPE_COUNTRY : - output.setCountryName(name); - break; - - case GFeature.FEATURE_TYPE_COUNTRY_CODE : - output.setCountryCode(name); - break; - - default : - if (android.util.Config.LOGD) { - Log.d(TAG, "getAddressFromProtoBuf(): Ignore feature " + type + "," + name); - } - break; - } - } - - addrs.add(output); - } - } - - private void addNeighborsToCellProfile(CellState cellState, ProtoBuf cellularProfile) { - List neighbors = cellState.getNeighbors(); - - int mPrimaryMcc = cellState.getMcc(); - int mPrimaryMnc = cellState.getMnc(); - - if (neighbors != null) { - for (CellState.NeighborCell neighbor : neighbors) { - ProtoBuf nCell = new ProtoBuf(GcellularMessageTypes.GCELL); - nCell.setInt(GCell.CELLID, neighbor.getCid()); - nCell.setInt(GCell.LAC, neighbor.getLac()); - nCell.setInt(GCell.RSSI, neighbor.getRssi()); - if (neighbor.getPsc() != -1) { - nCell.setInt(GCell.PRIMARY_SCRAMBLING_CODE, neighbor.getPsc()); - } - if (mPrimaryMcc != -1) { - nCell.setInt(GCell.MCC, mPrimaryMcc); - } - if (mPrimaryMnc != -1) { - nCell.setInt(GCell.MNC, mPrimaryMnc); - } - cellularProfile.addProtoBuf(GCellularProfile.NEIGHBORS, nCell); - } - } - } - -} diff --git a/location/java/com/android/internal/location/NetworkLocationProvider.java b/location/java/com/android/internal/location/NetworkLocationProvider.java deleted file mode 100644 index d0a59b943d60a..0000000000000 --- a/location/java/com/android/internal/location/NetworkLocationProvider.java +++ /dev/null @@ -1,561 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location; - -import com.android.internal.location.protocol.GDebugProfile; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; - -import android.content.Context; -import android.location.Criteria; -import android.location.Location; -import android.location.LocationManager; -import android.location.LocationProviderImpl; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiManager; -import android.os.Bundle; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.util.Log; - -/** - * A location provider which gets approximate location from Google's - * database of cell-tower and wi-fi locations. - * - *

      It is the responsibility of the LocationManagerService to - * notify this class of any changes in the radio information - * by calling {@link #updateCellState} and of data state - * changes by calling {@link #updateNetworkState} - * - *

      The LocationManagerService must also notify the provider - * of Wifi updates using the {@link #updateWifiScanResults} - * and {@link #updateWifiEnabledState} - * methods. - * - *

      The provider uses whichever radio is available - Cell - * or WiFi. If neither is available, it does NOT attempt to - * switch them on. - * - * {@hide} - */ -public class NetworkLocationProvider extends LocationProviderImpl { - private static final String TAG = "NetworkLocationProvider"; - - // Wait at least 60 seconds between network queries - private static final int MIN_NETWORK_RETRY_MILLIS = 60000; - - // Max time to wait for radio update - private static final long MAX_TIME_TO_WAIT_FOR_RADIO = 5 * 1000; // 5 seconds - - // State of entire provider - private int mStatus = AVAILABLE; - private long mStatusUpdateTime = 0; - - // Network state - private int mNetworkState = TEMPORARILY_UNAVAILABLE; - - // Cell state - private static final int MAX_CELL_HISTORY_TO_KEEP = 4; - private LinkedList mCellHistory = new LinkedList(); - private CellState mCellState = null; - private long mLastCellStateChangeTime = 0; - private long mLastCellLockTime = 0; - - // Wifi state - private static final long MIN_TIME_BETWEEN_WIFI_REPORTS = 45 * 1000; // 45 seconds - private List mWifiLastScanResults = null; - private long mLastWifiScanTriggerTime = 0; - private long mLastWifiScanElapsedTime = 0; - private long mLastWifiScanRealTime = 0; - private long mWifiScanFrequency = MIN_TIME_BETWEEN_WIFI_REPORTS; - private boolean mWifiEnabled = false; - - // Last known location state - private Location mLocation = new Location(LocationManager.NETWORK_PROVIDER); - private long mLastNetworkQueryTime = 0; // Last network request, successful or not - private long mLastSuccessfulNetworkQueryTime = 0; // Last successful network query time - - // Is provider enabled by user -- ignored by this class - private boolean mEnabled; - - // Is provider being used by an application - private HashSet mApplications = new HashSet(); - private boolean mTracking = false; - - // Location masf service - private LocationMasfClient mMasfClient; - - // Context of location manager service - private Context mContext; - - public static boolean isSupported() { - // This class provides a Google-specific location feature, so it's enabled only - // when the system property ro.com.google.locationfeatures is set. - if (!SystemProperties.get("ro.com.google.locationfeatures").equals("1")) { - return false; - } - - // Otherwise, assume cell location should work if we are not running in the emulator - return !SystemProperties.get("ro.kernel.qemu").equals("1"); - } - - public NetworkLocationProvider(Context context, LocationMasfClient masfClient) { - super(LocationManager.NETWORK_PROVIDER); - mContext = context; - mMasfClient = masfClient; - } - - @Override - public void updateNetworkState(int state) { - if (state == mNetworkState) { - return; - } - log("updateNetworkState(): Updating network state to " + state); - mNetworkState = state; - - updateStatus(mNetworkState); - } - - @Override - public void updateCellState(CellState newState) { - if (newState == null) { - log("updateCellState(): Cell state is invalid"); - return; - } - - if (mCellState != null && mCellState.equals(newState)) { - log("updateCellState(): Cell state is the same"); - return; - } - - // Add previous state to history - if ((mCellState != null) && mCellState.isValid()) { - if (mCellHistory.size() >= MAX_CELL_HISTORY_TO_KEEP) { - mCellHistory.remove(0); - } - mCellHistory.add(mCellState); - } - - mCellState = newState; - log("updateCellState(): Received"); - - mLastCellLockTime = 0; - mLastCellStateChangeTime = SystemClock.elapsedRealtime(); - } - - public void updateCellLockStatus(boolean acquired) { - if (acquired) { - mLastCellLockTime = SystemClock.elapsedRealtime(); - } else { - mLastCellLockTime = 0; - } - } - - @Override - public boolean requiresNetwork() { - return true; - } - - @Override - public boolean requiresSatellite() { - return false; - } - - @Override - public boolean requiresCell() { - return true; - } - - @Override - public boolean hasMonetaryCost() { - return true; - } - - @Override - public boolean supportsAltitude() { - return false; - } - - @Override - public boolean supportsSpeed() { - return false; - } - - @Override - public boolean supportsBearing() { - return false; - } - - @Override - public int getPowerRequirement() { - return Criteria.POWER_LOW; - } - - @Override - public void enable() { - // Nothing else needs to be done - mEnabled = true; - } - - @Override - public void disable() { - // Nothing else needs to be done - mEnabled = false; - } - - @Override - public boolean isEnabled() { - return mEnabled; - } - - @Override - public int getAccuracy() { - return Criteria.ACCURACY_COARSE; - } - - @Override - public int getStatus(Bundle extras) { - return mStatus; - } - - @Override - public long getStatusUpdateTime() { - return mStatusUpdateTime; - } - - @Override - public void setMinTime(long minTime) { - if (minTime < MIN_TIME_BETWEEN_WIFI_REPORTS) { - mWifiScanFrequency = MIN_TIME_BETWEEN_WIFI_REPORTS; - } else { - mWifiScanFrequency = minTime; - } - super.setMinTime(minTime); - } - - @Override - public boolean getLocation(Location l) { - - long now = SystemClock.elapsedRealtime(); - - // Trigger a wifi scan and wait for its results if necessary - if ((mWifiEnabled) && - (mWifiLastScanResults == null || - ((now - mLastWifiScanElapsedTime) > mWifiScanFrequency))) { - - boolean fallback = false; - - // If scan has been recently triggered - if (mLastWifiScanTriggerTime != 0 && - ((now - mLastWifiScanTriggerTime) < mWifiScanFrequency)) { - if ((now - mLastWifiScanTriggerTime) > MAX_TIME_TO_WAIT_FOR_RADIO) { - // If no results from last trigger available, use cell results - // This will also trigger a new scan - log("getLocation(): falling back to cell"); - fallback = true; - } else { - // Just wait for the Wifi results to be available - return false; - } - } - - WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - log("getLocation(): triggering a wifi scan"); - mLastWifiScanTriggerTime = now; - boolean succeeded = wifiManager.startScan(); - if (!succeeded) { - log("getLocation(): wifi scan did not succeed"); - // Wifi trigger failed, use cell results - fallback = true; - } - - // Wait for scan results - if (!fallback) { - return false; - } - } - - // If waiting for cell location - if (mLastCellLockTime != 0 && ((now - mLastCellLockTime) < MAX_TIME_TO_WAIT_FOR_RADIO)) { - return false; - } - - // Update Location - // 1) If there has been a cell state change - // 2) If there was no successful reply for last network request - if (mLastCellStateChangeTime > mLastNetworkQueryTime) { - updateLocation(); - return false; - - } else if ((mLastNetworkQueryTime != 0) - && (mLastNetworkQueryTime > mLastSuccessfulNetworkQueryTime) - && ((now - mLastNetworkQueryTime) > MIN_NETWORK_RETRY_MILLIS)) { - updateLocation(); - return false; - } - - if (mLocation != null && mLocation.getAccuracy() > 0) { - - // We could have a Cell Id location which hasn't changed in a - // while because we haven't switched towers so if the last location - // time + mWifiScanFrequency is less than current time update the - // locations time. - long currentTime = System.currentTimeMillis(); - if ((mLocation.getTime() + mWifiScanFrequency) < currentTime) { - mLocation.setTime(currentTime); - } - l.set(mLocation); - return true; - } else { - return false; - } - } - - @Override - public void enableLocationTracking(boolean enable) { - if (enable == mTracking) { - return; - } - - log("enableLocationTracking(): " + enable); - mTracking = enable; - - if (!enable) { - // When disabling the location provider, be sure to clear out old location - clearLocation(); - } else { - // When enabling provider, force location - forceLocation(); - } - } - - @Override - public boolean isLocationTracking() { - return mTracking; - } - - /** - * Notifies the provider that there are scan results available. - * - * @param scanResults list of wifi scan results - */ - public void updateWifiScanResults(List scanResults) { - if (!mTracking) { - return; - } - - long now = SystemClock.elapsedRealtime(); - - if (scanResults == null) { - mWifiLastScanResults = null; - mLastWifiScanElapsedTime = now; - mLastWifiScanRealTime = System.currentTimeMillis(); - - log("updateWifIScanResults(): NULL APs"); - - // Force cell location since no wifi results available - if (mWifiEnabled) { - mLastCellLockTime = 0; - mLastCellStateChangeTime = SystemClock.elapsedRealtime(); - } - - } else if ((mWifiLastScanResults == null) - || (mWifiLastScanResults.size() <= 2 && scanResults.size() > mWifiLastScanResults.size()) - || ((now - mLastWifiScanElapsedTime) > mWifiScanFrequency)) { - - if (mWifiLastScanResults == null) { - mWifiLastScanResults = new ArrayList(); - } else { - mWifiLastScanResults.clear(); - } - mWifiLastScanResults.addAll(scanResults); - mLastWifiScanElapsedTime = now; - mLastWifiScanRealTime = System.currentTimeMillis(); - - log("updateWifIScanResults(): " + mWifiLastScanResults.size() + " APs"); - updateLocation(); - - } - } - - /** - * Notifies the provider if Wifi has been enabled or disabled - * by the user - * - * @param enabled true if wifi is enabled; false otherwise - */ - public void updateWifiEnabledState(boolean enabled) { - mWifiEnabled = enabled; - - log("updateWifiEnabledState(): " + enabled); - - // Force location update - forceLocation(); - } - - public void addListener(String[] applications) { - if (applications != null) { - for (String app : applications) { - String a = app.replaceAll("com.google.android.", ""); - a = a.replaceAll("com.android.", ""); - mApplications.add(a); - log("addListener(): " + a); - } - } - } - - public void removeListener(String[] applications) { - if (applications != null) { - for (String app : applications) { - String a = app.replaceAll("com.google.android.", ""); - a = a.replaceAll("com.android.", ""); - mApplications.remove(a); - log("removeListener(): " + a); - } - } - } - - private void clearLocation() { - mLocation.setAccuracy(-1); - updateStatus(TEMPORARILY_UNAVAILABLE); - } - - private void forceLocation() { - if (mWifiEnabled) { - // Force another wifi scan - mWifiLastScanResults = null; - mLastWifiScanTriggerTime = 0; - mLastWifiScanElapsedTime = 0; - mLastWifiScanRealTime = 0; - } else { - // Force another cell location request - mLastCellLockTime = 0; - mLastCellStateChangeTime = SystemClock.elapsedRealtime(); - } - } - - private void updateStatus(int status) { - if (status != mStatus) { - mStatus = status; - mStatusUpdateTime = SystemClock.elapsedRealtime(); - } - } - - /** - * Gets location from the server is applications are tracking this provider - * - */ - private void updateLocation() { - - // If not being tracked, no need to do anything. - if (!mTracking) { - return; - } - - // If network is not available, can't do anything - if (mNetworkState != AVAILABLE) { - return; - } - - final long now = SystemClock.elapsedRealtime(); - - // There is a pending network request - if ((mLastNetworkQueryTime != 0) && - (mLastNetworkQueryTime > mLastSuccessfulNetworkQueryTime) && - ((now - mLastNetworkQueryTime) <= MIN_NETWORK_RETRY_MILLIS)) { - return; - } - - // Don't include wifi points if they're too old - List scanResults = null; - if (mWifiEnabled && (mWifiLastScanResults != null && - ((now - mLastWifiScanElapsedTime) < (mWifiScanFrequency + MAX_TIME_TO_WAIT_FOR_RADIO)))) { - scanResults = mWifiLastScanResults; - } - - // If no valid cell information available - boolean noCell = mCellState == null || !mCellState.isValid(); - - // If no valid wifi information available - boolean noWifi = scanResults == null || (scanResults.size() == 0); - - // If no cell-id or wi-fi update, just return invalid location - if (noCell && noWifi) { - clearLocation(); - return; - } - - // What kind of a network location request was it - int trigger; - if (!mWifiEnabled) { - if (!noCell) { - trigger = GDebugProfile.TRIGGER_CELL_AND_WIFI_CHANGE; - } else { - trigger = GDebugProfile.TRIGGER_WIFI_CHANGE; - } - } else { - trigger = GDebugProfile.TRIGGER_CELL_CHANGE; - } - - try { - mLastNetworkQueryTime = now; - mMasfClient.getNetworkLocation(mApplications, trigger, mCellState, mCellHistory, - scanResults, mLastWifiScanRealTime, new Callback() { - public void locationReceived(Location location, boolean networkSuccessful) { - // If location is valid and not the same as previously known location - if ((location != null) && (location.getAccuracy() > 0) && - (location.getTime() != mLocation.getTime())) { - mLocation.set(location); - updateStatus(AVAILABLE); - } else { - // Location is unavailable - clearLocation(); - } - - // Even if no location is available, network request could have succeeded - if (networkSuccessful) { - mLastSuccessfulNetworkQueryTime = SystemClock.elapsedRealtime(); - } - - } - }); - } catch(Exception e) { - Log.e(TAG, "updateLocation got exception:", e); - } - } - - public interface Callback { - - /** - * Callback function to notify of a received network location - * - * @param location location object that is received. may be null if not a valid location - * @param successful true if network query was successful, even if no location was found - */ - void locationReceived(Location location, boolean successful); - } - - private void log(String log) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, log); - } - } - -} diff --git a/location/java/com/android/internal/location/ProtoRequestListener.java b/location/java/com/android/internal/location/ProtoRequestListener.java deleted file mode 100644 index d73cd057a0c97..0000000000000 --- a/location/java/com/android/internal/location/ProtoRequestListener.java +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2007 The Android Open Source Project - -package com.android.internal.location; - -import com.google.common.io.GoogleHttpConnection; -import com.google.common.io.protocol.ProtoBuf; -import com.google.masf.ServiceCallback; -import com.google.masf.protocol.Request; -import com.google.masf.protocol.Response; -import com.google.masf.services.AsyncResult; - -import java.io.IOException; -import java.io.InputStream; - -import android.util.Log; - -/** - * Listener for protocol buffer requests - * - * {@hide} - */ - -public class ProtoRequestListener implements Request.Listener { - private final static String TAG = "ProtoRequestListener"; - private AsyncResult result; - private ProtoBuf protoResponse; - - /** - * @return the asynchronous result object - */ - public AsyncResult getAsyncResult() { - return result; - } - - /** - * Constructor for a ProtoRequestListener - * - * @param protoResponse ProtoBuf with correct type to fill response with - * @param callback function to call after completed request (may be null) - */ - public ProtoRequestListener(ProtoBuf protoResponse, ServiceCallback callback) { - this.result = new AsyncResult(callback); - this.protoResponse = protoResponse; - } - - public boolean requestComplete(Request request, Response response) - throws IOException { - InputStream is = response.getInputStream(); - if (response.getStatusCode() == GoogleHttpConnection.HTTP_OK) { - protoResponse.parse(is); - result.setResult(protoResponse); - } else { - result.setResult(null); - } - return true; - } - - public void requestException(Request request, Exception exception) { - Log.e(TAG, "requestException()", exception); - } -} diff --git a/location/java/com/android/internal/location/protocol/GCell.java b/location/java/com/android/internal/location/protocol/GCell.java deleted file mode 100644 index 21d1c48e78284..0000000000000 --- a/location/java/com/android/internal/location/protocol/GCell.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GCell { - static final int LAC = 1; - static final int CELLID = 2; - static final int MNC = 3; - static final int MCC = 4; - static final int RSSI = 5; - static final int AGE = 6; - static final int TIMING_ADVANCE = 7; - static final int PRIMARY_SCRAMBLING_CODE = 8; -} - diff --git a/location/java/com/android/internal/location/protocol/GCellularPlatformProfile.java b/location/java/com/android/internal/location/protocol/GCellularPlatformProfile.java deleted file mode 100644 index a17da20ff1936..0000000000000 --- a/location/java/com/android/internal/location/protocol/GCellularPlatformProfile.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GCellularPlatformProfile { - static final int RADIO_TYPE_GPRS = 3; - static final int RADIO_TYPE_CDMA = 4; - static final int RADIO_TYPE_WCDMA = 5; - - static final int RADIO_TYPE = 1; - static final int CARRIER = 2; - static final int IP = 3; - static final int HOME_MNC = 4; - static final int HOME_MCC = 5; -} - diff --git a/location/java/com/android/internal/location/protocol/GCellularProfile.java b/location/java/com/android/internal/location/protocol/GCellularProfile.java deleted file mode 100644 index 8c85bf7bc1d18..0000000000000 --- a/location/java/com/android/internal/location/protocol/GCellularProfile.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GCellularProfile { - static final int PRIMARY_CELL = 1; - static final int TIMESTAMP = 2; - static final int NEIGHBORS = 3; - static final int HISTORICAL_CELLS = 4; - static final int PREFETCH_MODE = 5; -} - diff --git a/location/java/com/android/internal/location/protocol/GDebugProfile.java b/location/java/com/android/internal/location/protocol/GDebugProfile.java deleted file mode 100644 index b96438722c244..0000000000000 --- a/location/java/com/android/internal/location/protocol/GDebugProfile.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GDebugProfile { - static final int TRIGGER_CELL_CHANGE = 1; - static final int TRIGGER_WIFI_CHANGE = 2; - static final int TRIGGER_CELL_AND_WIFI_CHANGE = 3; - static final int TRIGGER_GPS_CHANGE = 4; - static final int TRIGGER_OTHER = 5; - static final int TRIGGER_COLLECTION_START_BURST = 6; - static final int TRIGGER_COLLECTION_RESTART_BURST = 7; - static final int TRIGGER_COLLECTION_CONTINUE_BURST = 8; - static final int TRIGGER_COLLECTION_END_BURST = 9; - static final int TRIGGER_COLLECTION_END_BURST_AT_SAME_LOCATION = 10; - static final int TRIGGER_COLLECTION_MOVED_DISTANCE = 11; - - static final int TRIGGER = 1; - static final int ACTUAL_REQUEST = 2; - static final int CACHE_LOCATION = 3; - static final int DEVICE_RESTART = 4; -} - diff --git a/location/java/com/android/internal/location/protocol/GDeviceLocation.java b/location/java/com/android/internal/location/protocol/GDeviceLocation.java deleted file mode 100644 index 462ab073dbb87..0000000000000 --- a/location/java/com/android/internal/location/protocol/GDeviceLocation.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GDeviceLocation { - static final int LOCATION = 1; - static final int CELL = 2; - static final int WIFI_DEVICE = 3; -} - diff --git a/location/java/com/android/internal/location/protocol/GFeature.java b/location/java/com/android/internal/location/protocol/GFeature.java deleted file mode 100644 index 73fc1b347eb20..0000000000000 --- a/location/java/com/android/internal/location/protocol/GFeature.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GFeature { - static final int NAME = 1; - static final int FEATURE_TYPE = 2; - static final int ADDRESS = 3; - static final int BOUNDS = 4; - static final int CENTER = 5; - - static final int FEATURE_TYPE_UNKNOWN_TYPE = 0; - static final int FEATURE_TYPE_COUNTRY = 1; - static final int FEATURE_TYPE_COUNTRY_CODE = 2; - static final int FEATURE_TYPE_ADMINISTRATIVE_AREA = 3; - static final int FEATURE_TYPE_SUB_ADMINISTRATIVE_AREA = 4; - static final int FEATURE_TYPE_LOCALITY = 5; - static final int FEATURE_TYPE_SUB_LOCALITY = 6; - static final int FEATURE_TYPE_PREMISES = 7; - static final int FEATURE_TYPE_THOROUGHFARE = 8; - static final int FEATURE_TYPE_SUB_THOROUGHFARE = 9; - static final int FEATURE_TYPE_POST_CODE = 10; -} - diff --git a/location/java/com/android/internal/location/protocol/GGeocodeRequest.java b/location/java/com/android/internal/location/protocol/GGeocodeRequest.java deleted file mode 100644 index 4d56cc0e1b16f..0000000000000 --- a/location/java/com/android/internal/location/protocol/GGeocodeRequest.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GGeocodeRequest { - static final int NUM_FEATURE_LIMIT = 1; - static final int INCLUDE_BOUNDING_BOXES = 2; - static final int BOUNDING_BOX = 3; -} - diff --git a/location/java/com/android/internal/location/protocol/GGpsProfile.java b/location/java/com/android/internal/location/protocol/GGpsProfile.java deleted file mode 100644 index be69eb021d66c..0000000000000 --- a/location/java/com/android/internal/location/protocol/GGpsProfile.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GGpsProfile { - static final int FIX_QUALITY_INVALID = 0; - static final int FIX_QUALITY_GPS_FIX = 1; - static final int FIX_QUALITY_DGPS_FIX = 2; - - static final int GPS_FIX_TYPE = 1; - static final int PDOP = 2; - static final int HDOP = 3; - static final int VDOP = 4; -} - diff --git a/location/java/com/android/internal/location/protocol/GLocReply.java b/location/java/com/android/internal/location/protocol/GLocReply.java deleted file mode 100644 index 7a0504fcb39f5..0000000000000 --- a/location/java/com/android/internal/location/protocol/GLocReply.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GLocReply { - static final int STATUS = 1; - static final int REPLY_ELEMENTS = 2; - static final int PLATFORM_KEY = 3; -} - diff --git a/location/java/com/android/internal/location/protocol/GLocReplyElement.java b/location/java/com/android/internal/location/protocol/GLocReplyElement.java deleted file mode 100644 index bc47fcfd45ae9..0000000000000 --- a/location/java/com/android/internal/location/protocol/GLocReplyElement.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GLocReplyElement { - static final int STATUS = 1; - static final int LOCATION = 2; - static final int DEVICE_LOCATION = 3; -} - diff --git a/location/java/com/android/internal/location/protocol/GLocRequest.java b/location/java/com/android/internal/location/protocol/GLocRequest.java deleted file mode 100644 index 7761c11413c7c..0000000000000 --- a/location/java/com/android/internal/location/protocol/GLocRequest.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GLocRequest { - static final int PLATFORM_PROFILE = 1; - static final int APP_PROFILES = 2; - static final int USER_PROFILE = 3; - static final int REQUEST_ELEMENTS = 4; - static final int MASF_CLIENT_INFO = 257; -} - diff --git a/location/java/com/android/internal/location/protocol/GLocRequestElement.java b/location/java/com/android/internal/location/protocol/GLocRequestElement.java deleted file mode 100644 index d758953df6e64..0000000000000 --- a/location/java/com/android/internal/location/protocol/GLocRequestElement.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GLocRequestElement { - static final int CELLULAR_PROFILE = 1; - static final int WIFI_PROFILE = 2; - static final int LOCATION = 3; - static final int GEOCODE = 4; - static final int DEBUG_PROFILE = 99; -} - diff --git a/location/java/com/android/internal/location/protocol/GLocation.java b/location/java/com/android/internal/location/protocol/GLocation.java deleted file mode 100644 index 9a1eb1f151aea..0000000000000 --- a/location/java/com/android/internal/location/protocol/GLocation.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GLocation { - static final int LOCTYPE_GPS = 0; - static final int LOCTYPE_MAPCENTER = 1; - static final int LOCTYPE_CENTROID = 2; - static final int LOCTYPE_TOWER_LOCATION = 3; - - static final int LAT_LNG = 1; - static final int SOURCE = 2; - static final int ACCURACY = 3; - static final int CONFIDENCE = 4; - static final int FEATURE = 5; - static final int TIMESTAMP = 6; - static final int OBSOLETE = 7; - static final int LOC_TYPE = 8; - static final int MISC = 9; - static final int ALTITUDE = 10; - static final int VERTICAL_ACCURACY = 11; - static final int VELOCITY = 12; - static final int HEADING = 13; - static final int GPS_PROFILE = 14; - static final int LOCATION_STRING = 15; -} - diff --git a/location/java/com/android/internal/location/protocol/GPlatformProfile.java b/location/java/com/android/internal/location/protocol/GPlatformProfile.java deleted file mode 100644 index 32a1f8ffdcb73..0000000000000 --- a/location/java/com/android/internal/location/protocol/GPlatformProfile.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GPlatformProfile { - static final int VERSION = 1; - static final int PLATFORM = 2; - static final int PLATFORM_KEY = 3; - static final int DISTRIBUTION_CHANNEL = 4; - static final int LOCALE = 5; - static final int CELLULAR_PLATFORM_PROFILE = 6; - static final int WIFI_PLATFORM_PROFILE = 7; -} - diff --git a/location/java/com/android/internal/location/protocol/GRectangle.java b/location/java/com/android/internal/location/protocol/GRectangle.java deleted file mode 100644 index b8412e65e7cfb..0000000000000 --- a/location/java/com/android/internal/location/protocol/GRectangle.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GRectangle { - static final int LOWER_LEFT = 1; - static final int UPPER_RIGHT = 2; -} - diff --git a/location/java/com/android/internal/location/protocol/GUserProfile.java b/location/java/com/android/internal/location/protocol/GUserProfile.java deleted file mode 100644 index 2ce962c6a553f..0000000000000 --- a/location/java/com/android/internal/location/protocol/GUserProfile.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GUserProfile { - static final int USER_NAME = 1; - static final int AUTH_TOKEN = 2; -} - diff --git a/location/java/com/android/internal/location/protocol/GWifiDevice.java b/location/java/com/android/internal/location/protocol/GWifiDevice.java deleted file mode 100644 index 62bd03a6b8066..0000000000000 --- a/location/java/com/android/internal/location/protocol/GWifiDevice.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GWifiDevice { - static final int MAC = 1; - static final int SSID = 2; - static final int CHANNEL = 3; - static final int RSSI = 4; - static final int NOISE = 5; -} - diff --git a/location/java/com/android/internal/location/protocol/GWifiPlatformProfile.java b/location/java/com/android/internal/location/protocol/GWifiPlatformProfile.java deleted file mode 100644 index 7f1efcbf9c582..0000000000000 --- a/location/java/com/android/internal/location/protocol/GWifiPlatformProfile.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GWifiPlatformProfile { - static final int RADIO_TYPE_WIFI802_11_A = 1; - static final int RADIO_TYPE_WIFI802_11_B = 2; - static final int RADIO_TYPE_WIFI802_11_G = 3; - static final int RADIO_TYPE_WIFI802_11_N = 4; - - static final int SCANNER_MAC = 1; - static final int SCANNER_IP = 2; - static final int RADIO_TYPE = 3; -} - diff --git a/location/java/com/android/internal/location/protocol/GWifiProfile.java b/location/java/com/android/internal/location/protocol/GWifiProfile.java deleted file mode 100644 index e73102718e379..0000000000000 --- a/location/java/com/android/internal/location/protocol/GWifiProfile.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface GWifiProfile { - static final int TIMESTAMP = 1; - static final int WIFI_DEVICES = 2; - static final int PREFETCH_MODE = 3; -} - diff --git a/location/java/com/android/internal/location/protocol/GaddressMessageTypes.java b/location/java/com/android/internal/location/protocol/GaddressMessageTypes.java deleted file mode 100644 index 7b6ffd018d93b..0000000000000 --- a/location/java/com/android/internal/location/protocol/GaddressMessageTypes.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -import com.google.common.io.protocol.ProtoBufType; - -public class GaddressMessageTypes { - public static final ProtoBufType GADDRESS = new ProtoBufType(); - public static final ProtoBufType GADDRESS_COMPONENT = new ProtoBufType(); - - static { - GADDRESS - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_DATA, - GAddress.FORMATTED_ADDRESS_LINE, null) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GAddress.COMPONENT, GADDRESS_COMPONENT); - - GADDRESS_COMPONENT - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_DATA, - GAddressComponent.NAME, null) - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32, - GAddressComponent.FEATURE_TYPE, null); - - } -} diff --git a/location/java/com/android/internal/location/protocol/GcellularMessageTypes.java b/location/java/com/android/internal/location/protocol/GcellularMessageTypes.java deleted file mode 100644 index 37a6d5274f6ea..0000000000000 --- a/location/java/com/android/internal/location/protocol/GcellularMessageTypes.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -import com.google.common.io.protocol.ProtoBufType; - -public class GcellularMessageTypes { - public static final ProtoBufType GCELL = new ProtoBufType(); - public static final ProtoBufType GCELLULAR_PROFILE = new ProtoBufType(); - - static { - GCELL - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32, - GCell.LAC, null) - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32, - GCell.CELLID, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCell.MNC, new Long(-1)) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCell.MCC, new Long(-1)) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCell.RSSI, new Long(-9999)) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCell.AGE, new Long(0)) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCell.TIMING_ADVANCE, new Long(-1)) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCell.PRIMARY_SCRAMBLING_CODE, null); - - GCELLULAR_PROFILE - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE, - GCellularProfile.PRIMARY_CELL, GCELL) - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT64, - GCellularProfile.TIMESTAMP, null) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GCellularProfile.NEIGHBORS, GCELL) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GCellularProfile.HISTORICAL_CELLS, GCELL) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCellularProfile.PREFETCH_MODE, null); - - } -} diff --git a/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java b/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java deleted file mode 100644 index 9a51efe8cc30c..0000000000000 --- a/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -import com.google.common.io.protocol.ProtoBuf; -import com.google.common.io.protocol.ProtoBufType; - -public class GdebugprofileMessageTypes { - public static final ProtoBufType GDEBUG_PROFILE = new ProtoBufType(); - - static { - GDEBUG_PROFILE - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GDebugProfile.TRIGGER, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_BOOL, - GDebugProfile.ACTUAL_REQUEST, ProtoBuf.TRUE) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GDebugProfile.CACHE_LOCATION, GlocationMessageTypes.GDEVICE_LOCATION) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_BOOL, - GDebugProfile.DEVICE_RESTART, ProtoBuf.FALSE); - - } -} diff --git a/location/java/com/android/internal/location/protocol/GfeatureMessageTypes.java b/location/java/com/android/internal/location/protocol/GfeatureMessageTypes.java deleted file mode 100644 index 24b182a2ce6d3..0000000000000 --- a/location/java/com/android/internal/location/protocol/GfeatureMessageTypes.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -import com.google.common.io.protocol.ProtoBufType; - -public class GfeatureMessageTypes { - public static final ProtoBufType GFEATURE_TYPE = new ProtoBufType(); - public static final ProtoBufType GFEATURE = new ProtoBufType(); - - static { - GFEATURE - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_DATA, - GFeature.NAME, null) - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32, - GFeature.FEATURE_TYPE, null) - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE, - GFeature.ADDRESS, GaddressMessageTypes.GADDRESS) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GFeature.BOUNDS, GrectangleMessageTypes.GRECTANGLE) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GFeature.CENTER, GlatlngMessageTypes.GLAT_LNG); - - } -} diff --git a/location/java/com/android/internal/location/protocol/GlatlngMessageTypes.java b/location/java/com/android/internal/location/protocol/GlatlngMessageTypes.java deleted file mode 100644 index b6a908611a1de..0000000000000 --- a/location/java/com/android/internal/location/protocol/GlatlngMessageTypes.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -import com.google.common.io.protocol.ProtoBufType; - -public class GlatlngMessageTypes { - public static final ProtoBufType GLAT_LNG = new ProtoBufType(); - - static { - GLAT_LNG - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_FIXED32, - GLatLng.LAT_E7, null) - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_FIXED32, - GLatLng.LNG_E7, null); - - } -} diff --git a/location/java/com/android/internal/location/protocol/GlocationMessageTypes.java b/location/java/com/android/internal/location/protocol/GlocationMessageTypes.java deleted file mode 100644 index 067d47c84b4bd..0000000000000 --- a/location/java/com/android/internal/location/protocol/GlocationMessageTypes.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -import com.google.common.io.protocol.ProtoBufType; - -public class GlocationMessageTypes { - public static final ProtoBufType GGPS_PROFILE = new ProtoBufType(); - public static final ProtoBufType GLOCATION = new ProtoBufType(); - public static final ProtoBufType GDEVICE_LOCATION = new ProtoBufType(); - public static final ProtoBufType GCELLULAR_PLATFORM_PROFILE = new ProtoBufType(); - public static final ProtoBufType GWIFI_PLATFORM_PROFILE = new ProtoBufType(); - public static final ProtoBufType GPREFETCH_MODE = new ProtoBufType(); - public static final ProtoBufType GPLATFORM_PROFILE = new ProtoBufType(); - public static final ProtoBufType GAPP_PROFILE = new ProtoBufType(); - public static final ProtoBufType GUSER_PROFILE = new ProtoBufType(); - - static { - GGPS_PROFILE - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GGpsProfile.GPS_FIX_TYPE, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GGpsProfile.PDOP, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GGpsProfile.HDOP, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GGpsProfile.VDOP, null); - - GLOCATION - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocation.LAT_LNG, GlatlngMessageTypes.GLAT_LNG) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GLocation.SOURCE, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GLocation.ACCURACY, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GLocation.CONFIDENCE, null) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GLocation.FEATURE, GfeatureMessageTypes.GFEATURE) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT64, - GLocation.TIMESTAMP, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_BOOL, - GLocation.OBSOLETE, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GLocation.LOC_TYPE, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GLocation.MISC, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GLocation.ALTITUDE, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GLocation.VERTICAL_ACCURACY, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GLocation.VELOCITY, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GLocation.HEADING, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocation.GPS_PROFILE, GGPS_PROFILE) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GLocation.LOCATION_STRING, null); - - GDEVICE_LOCATION - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GDeviceLocation.LOCATION, GLOCATION) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GDeviceLocation.CELL, GcellularMessageTypes.GCELL) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GDeviceLocation.WIFI_DEVICE, GwifiMessageTypes.GWIFI_DEVICE); - - GCELLULAR_PLATFORM_PROFILE - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCellularPlatformProfile.RADIO_TYPE, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GCellularPlatformProfile.CARRIER, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GCellularPlatformProfile.IP, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCellularPlatformProfile.HOME_MNC, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GCellularPlatformProfile.HOME_MCC, null); - - GWIFI_PLATFORM_PROFILE - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GWifiPlatformProfile.SCANNER_MAC, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GWifiPlatformProfile.SCANNER_IP, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GWifiPlatformProfile.RADIO_TYPE, null); - - GPLATFORM_PROFILE - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_DATA, - GPlatformProfile.VERSION, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GPlatformProfile.PLATFORM, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GPlatformProfile.PLATFORM_KEY, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GPlatformProfile.DISTRIBUTION_CHANNEL, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GPlatformProfile.LOCALE, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GPlatformProfile.CELLULAR_PLATFORM_PROFILE, GCELLULAR_PLATFORM_PROFILE) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GPlatformProfile.WIFI_PLATFORM_PROFILE, GWIFI_PLATFORM_PROFILE); - - GAPP_PROFILE - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GAppProfile.APP_NAME, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GAppProfile.APP_KEY, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GAppProfile.REQUEST_TYPE, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GAppProfile.SEARCH_TYPE, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GAppProfile.SEARCH_TERM, null); - - GUSER_PROFILE - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GUserProfile.USER_NAME, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GUserProfile.AUTH_TOKEN, null); - - } -} diff --git a/location/java/com/android/internal/location/protocol/GrectangleMessageTypes.java b/location/java/com/android/internal/location/protocol/GrectangleMessageTypes.java deleted file mode 100644 index aeb0047f954f6..0000000000000 --- a/location/java/com/android/internal/location/protocol/GrectangleMessageTypes.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -import com.google.common.io.protocol.ProtoBufType; - -public class GrectangleMessageTypes { - public static final ProtoBufType GRECTANGLE = new ProtoBufType(); - - static { - GRECTANGLE - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE, - GRectangle.LOWER_LEFT, GlatlngMessageTypes.GLAT_LNG) - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE, - GRectangle.UPPER_RIGHT, GlatlngMessageTypes.GLAT_LNG); - - } -} diff --git a/location/java/com/android/internal/location/protocol/GwifiMessageTypes.java b/location/java/com/android/internal/location/protocol/GwifiMessageTypes.java deleted file mode 100644 index cd7119bd8bf6c..0000000000000 --- a/location/java/com/android/internal/location/protocol/GwifiMessageTypes.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -import com.google.common.io.protocol.ProtoBufType; - -public class GwifiMessageTypes { - public static final ProtoBufType GWIFI_DEVICE = new ProtoBufType(); - public static final ProtoBufType GWIFI_PROFILE = new ProtoBufType(); - - static { - GWIFI_DEVICE - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_DATA, - GWifiDevice.MAC, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GWifiDevice.SSID, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GWifiDevice.CHANNEL, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GWifiDevice.RSSI, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GWifiDevice.NOISE, null); - - GWIFI_PROFILE - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT64, - GWifiProfile.TIMESTAMP, null) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GWifiProfile.WIFI_DEVICES, GWIFI_DEVICE) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32, - GWifiProfile.PREFETCH_MODE, null); - - } -} diff --git a/location/java/com/android/internal/location/protocol/LocserverMessageTypes.java b/location/java/com/android/internal/location/protocol/LocserverMessageTypes.java deleted file mode 100644 index 8ffd00409aaf9..0000000000000 --- a/location/java/com/android/internal/location/protocol/LocserverMessageTypes.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -import com.google.common.io.protocol.ProtoBuf; -import com.google.common.io.protocol.ProtoBufType; - -public class LocserverMessageTypes { - public static final ProtoBufType RESPONSE_CODES = new ProtoBufType(); - public static final ProtoBufType GLOC_REQUEST_ELEMENT = new ProtoBufType(); - public static final ProtoBufType GLOC_REQUEST = new ProtoBufType(); - public static final ProtoBufType GGEOCODE_REQUEST = new ProtoBufType(); - public static final ProtoBufType GLOC_REPLY_ELEMENT = new ProtoBufType(); - public static final ProtoBufType GLOC_REPLY = new ProtoBufType(); - - static { - GLOC_REQUEST_ELEMENT - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocRequestElement.CELLULAR_PROFILE, GcellularMessageTypes.GCELLULAR_PROFILE) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocRequestElement.WIFI_PROFILE, GwifiMessageTypes.GWIFI_PROFILE) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocRequestElement.LOCATION, GlocationMessageTypes.GLOCATION) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocRequestElement.GEOCODE, GGEOCODE_REQUEST) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocRequestElement.DEBUG_PROFILE, GdebugprofileMessageTypes.GDEBUG_PROFILE); - - GLOC_REQUEST - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE, - GLocRequest.PLATFORM_PROFILE, GlocationMessageTypes.GPLATFORM_PROFILE) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GLocRequest.APP_PROFILES, GlocationMessageTypes.GAPP_PROFILE) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocRequest.USER_PROFILE, GlocationMessageTypes.GUSER_PROFILE) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GLocRequest.REQUEST_ELEMENTS, GLOC_REQUEST_ELEMENT) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocRequest.MASF_CLIENT_INFO, null); - - GGEOCODE_REQUEST - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_FIXED32, - GGeocodeRequest.NUM_FEATURE_LIMIT, new Long(1)) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_BOOL, - GGeocodeRequest.INCLUDE_BOUNDING_BOXES, ProtoBuf.FALSE) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GGeocodeRequest.BOUNDING_BOX, GrectangleMessageTypes.GRECTANGLE); - - GLOC_REPLY_ELEMENT - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32, - GLocReplyElement.STATUS, null) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE, - GLocReplyElement.LOCATION, GlocationMessageTypes.GLOCATION) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GLocReplyElement.DEVICE_LOCATION, GlocationMessageTypes.GDEVICE_LOCATION); - - GLOC_REPLY - .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32, - GLocReply.STATUS, null) - .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE, - GLocReply.REPLY_ELEMENTS, GLOC_REPLY_ELEMENT) - .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA, - GLocReply.PLATFORM_KEY, null); - - } -} diff --git a/location/java/com/android/internal/location/protocol/ResponseCodes.java b/location/java/com/android/internal/location/protocol/ResponseCodes.java deleted file mode 100644 index 2ea9318460b43..0000000000000 --- a/location/java/com/android/internal/location/protocol/ResponseCodes.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location.protocol; - -public interface ResponseCodes { - static final int STATUS_STATUS_SUCCESS = 0; - static final int STATUS_STATUS_FAILED = 1; - static final int STATUS_AUTHORIZATION_REJECTED = 2; - static final int STATUS_NO_SOURCE_EXISTS = 3; - static final int STATUS_SIGNAL_TOO_WEAK = 4; - static final int STATUS_INVALID_REQUEST = 5; - static final int STATUS_INVALID_NUM_REQUESTS = 6; - static final int STATUS_INVALID_USERLOCATION_FORMAT = 7; - static final int STATUS_INVALID_OPERATION_CODE = 8; - static final int STATUS_INVALID_MAC_STRING_FORMAT = 9; - static final int STATUS_INVALID_CELLID_STRING_FORMAT = 10; - static final int STATUS_NON_EXISTENT_AP = 11; - static final int STATUS_NON_EXISTENT_CELLID = 12; - static final int STATUS_STATUS_FAILED_NO_SOURCE = 13; - static final int STATUS_STATUS_FAILED_NO_SAVE = 14; - static final int STATUS_PLATFORM_KEY_EXPIRED = 15; - static final int STATUS_NO_STORE_EXISTS = 16; - static final int STATUS_NO_CELLIDDATA_FOR_UPDATE = 17; - static final int STATUS_NON_SUPPORTED_OPERATION_IN_UPDATE = 18; - static final int STATUS_NON_SUPPORTED_OPERATION = 19; - static final int STATUS_STATUS_FAILED_NO_GEOCODE = 20; - static final int STATUS_BLACKLISTED_IP_CELLID = 100; - static final int STATUS_BLACKLISTED_IP_WIFI = 101; - -} - diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index c857e17bd7865..0732b615e7e5c 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -18,9 +18,9 @@ package android.media; /** * The AudioFormat class is used to access a number of audio format and - * channel configuration constants. + * channel configuration constants. They are for instance used + * in __link AudioTrack} and __link AudioRecord}. * - * {@hide Pending API council review} */ public class AudioFormat { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 52cf69f91a968..297877418a583 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -48,38 +48,61 @@ public class AudioManager { private static boolean DEBUG = false; private static boolean localLOGV = DEBUG || android.util.Config.LOGV; + /** + * Broadcast intent, a hint for applications that audio is about to become + * 'noisy' due to a change in audio outputs. For example, this intent may + * be sent when a wired headset is unplugged, or when an A2DP audio + * sink is disconnected, and the audio system is about to automatically + * switch audio route to the speaker. Applications that are controlling + * audio streams may consider pausing, reducing volume or some other action + * on receipt of this intent so as not to surprise the user with audio + * from the speaker. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY"; + /** * Sticky broadcast intent action indicating that the ringer mode has * changed. Includes the new ringer mode. - * + * * @see #EXTRA_RINGER_MODE */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String RINGER_MODE_CHANGED_ACTION = "android.media.RINGER_MODE_CHANGED"; - + /** * The new ringer mode. - * + * * @see #RINGER_MODE_CHANGED_ACTION * @see #RINGER_MODE_NORMAL * @see #RINGER_MODE_SILENT * @see #RINGER_MODE_VIBRATE */ public static final String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE"; - + /** * Broadcast intent action indicating that the vibrate setting has * changed. Includes the vibrate type and its new setting. - * + * * @see #EXTRA_VIBRATE_TYPE * @see #EXTRA_VIBRATE_SETTING */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String VIBRATE_SETTING_CHANGED_ACTION = "android.media.VIBRATE_SETTING_CHANGED"; - + + /** + * @hide Broadcast intent when the volume for a particular stream type changes. + * Includes the stream and the new volume + * + * @see #EXTRA_VOLUME_STREAM_TYPE + * @see #EXTRA_VOLUME_STREAM_VALUE + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION"; + /** * The new vibrate setting for a particular type. - * + * * @see #VIBRATE_SETTING_CHANGED_ACTION * @see #EXTRA_VIBRATE_TYPE * @see #VIBRATE_SETTING_ON @@ -87,16 +110,27 @@ public class AudioManager { * @see #VIBRATE_SETTING_ONLY_SILENT */ public static final String EXTRA_VIBRATE_SETTING = "android.media.EXTRA_VIBRATE_SETTING"; - + /** * The vibrate type whose setting has changed. - * + * * @see #VIBRATE_SETTING_CHANGED_ACTION * @see #VIBRATE_TYPE_NOTIFICATION * @see #VIBRATE_TYPE_RINGER */ public static final String EXTRA_VIBRATE_TYPE = "android.media.EXTRA_VIBRATE_TYPE"; - + + /** + * @hide The stream type for the volume changed intent. + */ + public static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE"; + + /** + * @hide The volume associated with the stream for the volume changed intent. + */ + public static final String EXTRA_VOLUME_STREAM_VALUE = + "android.media.EXTRA_VOLUME_STREAM_VALUE"; + /** The audio stream for phone calls */ public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL; /** The audio stream for system sounds */ @@ -109,6 +143,8 @@ public class AudioManager { public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM; /** The audio stream for notifications */ public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION; + /** @hide The audio stream for phone calls when connected to bluetooth */ + public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO; /** Number of audio streams */ /** * @deprecated Use AudioSystem.getNumStreamTypes() instead @@ -123,9 +159,10 @@ public class AudioManager { 8, // STREAM_RING 16, // STREAM_MUSIC 8, // STREAM_ALARM - 8 // STREAM_NOTIFICATION - }; - + 8, // STREAM_NOTIFICATION + 15, // STREAM_BLUETOOTH_SCO + }; + /** @hide Default volume index values for audio streams */ public static final int[] DEFAULT_STREAM_VOLUME = new int[] { 4, // STREAM_VOICE_CALL @@ -133,12 +170,13 @@ public class AudioManager { 5, // STREAM_RING 11, // STREAM_MUSIC 6, // STREAM_ALARM - 5 // STREAM_NOTIFICATION - }; - + 5, // STREAM_NOTIFICATION + 7 // STREAM_BLUETOOTH_SCO + }; + /** * Increase the ringer volume. - * + * * @see #adjustVolume(int, int) * @see #adjustStreamVolume(int, int, int) */ @@ -146,7 +184,7 @@ public class AudioManager { /** * Decrease the ringer volume. - * + * * @see #adjustVolume(int, int) * @see #adjustStreamVolume(int, int, int) */ @@ -155,17 +193,17 @@ public class AudioManager { /** * Maintain the previous ringer volume. This may be useful when needing to * show the volume toast without actually modifying the volume. - * + * * @see #adjustVolume(int, int) * @see #adjustStreamVolume(int, int, int) */ public static final int ADJUST_SAME = 0; // Flags should be powers of 2! - + /** * Show a toast containing the current volume. - * + * * @see #adjustStreamVolume(int, int, int) * @see #adjustVolume(int, int) * @see #setStreamVolume(int, int, int) @@ -183,12 +221,12 @@ public class AudioManager { * mode (for example, the ring stream type). If this flag is included, this * behavior will be present regardless of the stream type being affected by * the ringer mode. - * + * * @see #adjustVolume(int, int) * @see #adjustStreamVolume(int, int, int) */ public static final int FLAG_ALLOW_RINGER_MODES = 1 << 1; - + /** * Whether to play a sound when changing the volume. *

      @@ -197,28 +235,28 @@ public class AudioManager { * in some cases (for example, the decided stream type is not * {@link AudioManager#STREAM_RING}, or the volume is being adjusted * downward). - * + * * @see #adjustStreamVolume(int, int, int) * @see #adjustVolume(int, int) * @see #setStreamVolume(int, int, int) */ public static final int FLAG_PLAY_SOUND = 1 << 2; - + /** * Removes any sounds/vibrate that may be in the queue, or are playing (related to * changing volume). */ public static final int FLAG_REMOVE_SOUND_AND_VIBRATE = 1 << 3; - + /** * Whether to vibrate if going into the vibrate ringer mode. */ public static final int FLAG_VIBRATE = 1 << 4; - + /** * Ringer mode that will be silent and will not vibrate. (This overrides the * vibrate setting.) - * + * * @see #setRingerMode(int) * @see #getRingerMode() */ @@ -228,7 +266,7 @@ public class AudioManager { * Ringer mode that will be silent and will vibrate. (This will cause the * phone ringer to always vibrate, but the notification vibrate to only * vibrate if set.) - * + * * @see #setRingerMode(int) * @see #getRingerMode() */ @@ -238,7 +276,7 @@ public class AudioManager { * Ringer mode that may be audible and may vibrate. It will be audible if * the volume before changing out of this mode was audible. It will vibrate * if the vibrate setting is on. - * + * * @see #setRingerMode(int) * @see #getRingerMode() */ @@ -246,53 +284,53 @@ public class AudioManager { /** * Vibrate type that corresponds to the ringer. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) * @see #shouldVibrate(int) */ public static final int VIBRATE_TYPE_RINGER = 0; - + /** * Vibrate type that corresponds to notifications. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) * @see #shouldVibrate(int) */ public static final int VIBRATE_TYPE_NOTIFICATION = 1; - + /** * Vibrate setting that suggests to never vibrate. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) */ public static final int VIBRATE_SETTING_OFF = 0; - + /** * Vibrate setting that suggests to vibrate when possible. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) */ public static final int VIBRATE_SETTING_ON = 1; - + /** * Vibrate setting that suggests to only vibrate when in the vibrate ringer * mode. - * + * * @see #setVibrateSetting(int, int) * @see #getVibrateSetting(int) */ public static final int VIBRATE_SETTING_ONLY_SILENT = 2; - + /** * Suggests using the default stream type. This may not be used in all * places a stream type is needed. */ public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE; - + private static IAudioService sService; /** @@ -315,8 +353,8 @@ public class AudioManager { /** * Adjusts the volume of a particular stream by one step in a direction. - * - * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL}, + * + * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL}, * {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC} or * {@link #STREAM_ALARM} * @param direction The direction to adjust the volume. One of @@ -340,7 +378,7 @@ public class AudioManager { * active, it will have the highest priority regardless of if the in-call * screen is showing. Another example, if music is playing in the background * and a call is not active, the music stream will be adjusted. - * + * * @param direction The direction to adjust the volume. One of * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or * {@link #ADJUST_SAME}. @@ -361,7 +399,7 @@ public class AudioManager { /** * Adjusts the volume of the most relevant stream, or the given fallback * stream. - * + * * @param direction The direction to adjust the volume. One of * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or * {@link #ADJUST_SAME}. @@ -380,10 +418,10 @@ public class AudioManager { Log.e(TAG, "Dead object in adjustVolume", e); } } - + /** * Returns the current ringtone mode. - * + * * @return The current ringtone mode, one of {@link #RINGER_MODE_NORMAL}, * {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}. * @see #setRingerMode(int) @@ -400,7 +438,7 @@ public class AudioManager { /** * Returns the maximum volume index for a particular stream. - * + * * @param streamType The stream type whose maximum volume index is returned. * @return The maximum valid volume index for the stream. * @see #getStreamVolume(int) @@ -417,7 +455,7 @@ public class AudioManager { /** * Returns the current volume index for a particular stream. - * + * * @param streamType The stream type whose volume index is returned. * @return The current volume index for the stream. * @see #getStreamMaxVolume(int) @@ -439,7 +477,7 @@ public class AudioManager { * Silent mode will mute the volume and will not vibrate. Vibrate mode will * mute the volume and vibrate. Normal mode will be audible and may vibrate * according to user settings. - * + * * @param ringerMode The ringer mode, one of {@link #RINGER_MODE_NORMAL}, * {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}. * @see #getRingerMode() @@ -455,7 +493,7 @@ public class AudioManager { /** * Sets the volume index for a particular stream. - * + * * @param streamType The stream whose volume index should be set. * @param index The volume index to set. See * {@link #getStreamMaxVolume(int)} for the largest valid value. @@ -471,7 +509,7 @@ public class AudioManager { Log.e(TAG, "Dead object in setStreamVolume", e); } } - + /** * Solo or unsolo a particular stream. All other streams are muted. *

      @@ -479,13 +517,13 @@ public class AudioManager { * with an active solo request on a stream dies, all streams that were muted * because of this request will be unmuted automatically. *

      - * The solo requests for a given stream are cumulative: the AudioManager + * The solo requests for a given stream are cumulative: the AudioManager * can receive several solo requests from one or more clients and the stream * will be unsoloed only when the same number of unsolo requests are received. *

      - * For a better user experience, applications MUST unsolo a soloed stream + * For a better user experience, applications MUST unsolo a soloed stream * in onPause() and solo is again in onResume() if appropriate. - * + * * @param streamType The stream to be soloed/unsoloed. * @param state The required solo state: true for solo ON, false for solo OFF */ @@ -497,7 +535,7 @@ public class AudioManager { Log.e(TAG, "Dead object in setStreamSolo", e); } } - + /** * Mute or unmute an audio stream. *

      @@ -505,13 +543,13 @@ public class AudioManager { * with an active mute request on a stream dies, this stream will be unmuted * automatically. *

      - * The mute requests for a given stream are cumulative: the AudioManager + * The mute requests for a given stream are cumulative: the AudioManager * can receive several mute requests from one or more clients and the stream * will be unmuted only when the same number of unmute requests are received. *

      - * For a better user experience, applications MUST unmute a muted stream + * For a better user experience, applications MUST unmute a muted stream * in onPause() and mute is again in onResume() if appropriate. - * + * * @param streamType The stream to be muted/unmuted. * @param state The required mute state: true for mute ON, false for mute OFF */ @@ -532,7 +570,7 @@ public class AudioManager { * vibrate. The notification manager will not vibrate if the policy doesn't * allow it, so the client should always set a vibrate pattern and let the * notification manager control whether or not to actually vibrate. - * + * * @param vibrateType The type of vibrate. One of * {@link #VIBRATE_TYPE_NOTIFICATION} or * {@link #VIBRATE_TYPE_RINGER}. @@ -550,13 +588,13 @@ public class AudioManager { return false; } } - + /** * Returns whether the user's vibrate setting for a vibrate type. *

      * This shouldn't be needed by most clients that want to vibrate, instead * see {@link #shouldVibrate(int)}. - * + * * @param vibrateType The type of vibrate. One of * {@link #VIBRATE_TYPE_NOTIFICATION} or * {@link #VIBRATE_TYPE_RINGER}. @@ -578,7 +616,7 @@ public class AudioManager { /** * Sets the setting for when the vibrate type should vibrate. - * + * * @param vibrateType The type of vibrate. One of * {@link #VIBRATE_TYPE_NOTIFICATION} or * {@link #VIBRATE_TYPE_RINGER}. @@ -597,11 +635,11 @@ public class AudioManager { Log.e(TAG, "Dead object in setVibrateSetting", e); } } - + /** * Sets the speakerphone on or off. * - * @param on set true to turn on speakerphone; + * @param on set true to turn on speakerphone; * false to turn it off */ public void setSpeakerphoneOn(boolean on){ @@ -620,17 +658,17 @@ public class AudioManager { /** * Sets audio routing to the Bluetooth headset on or off. * - * @param on set true to route SCO (voice) audio to/from Bluetooth + * @param on set true to route SCO (voice) audio to/from Bluetooth * headset; false to route audio to/from phone earpiece */ public void setBluetoothScoOn(boolean on){ // Don't disable A2DP when turning off SCO. // A2DP does not affect in-call routing. - setRouting(MODE_RINGTONE, + setRouting(MODE_RINGTONE, on ? ROUTE_BLUETOOTH_SCO: ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_NORMAL, + setRouting(MODE_NORMAL, on ? ROUTE_BLUETOOTH_SCO: ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_IN_CALL, + setRouting(MODE_IN_CALL, on ? ROUTE_BLUETOOTH_SCO: ROUTE_EARPIECE, ROUTE_ALL); } @@ -647,15 +685,15 @@ public class AudioManager { /** * Sets A2DP audio routing to the Bluetooth headset on or off. * - * @param on set true to route A2DP audio to/from Bluetooth + * @param on set true to route A2DP audio to/from Bluetooth * headset; false disable A2DP audio */ public void setBluetoothA2dpOn(boolean on){ // the audio flinger chooses A2DP as a higher priority, // so there is no need to disable other routes. - setRouting(MODE_RINGTONE, + setRouting(MODE_RINGTONE, on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_NORMAL, + setRouting(MODE_NORMAL, on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP); } @@ -672,7 +710,7 @@ public class AudioManager { /** * Sets audio routing to the wired headset on or off. * - * @param on set true to route audio to/from wired + * @param on set true to route audio to/from wired * headset; false disable wired headset audio * @hide */ @@ -701,7 +739,7 @@ public class AudioManager { /** * Sets the microphone mute on or off. * - * @param on set true to mute the microphone; + * @param on set true to mute the microphone; * false to turn mute off */ public void setMicrophoneMute(boolean on){ @@ -762,57 +800,57 @@ public class AudioManager { /* modes for setMode/getMode/setRoute/getRoute */ /** - * Audio harware modes. - */ + * Audio harware modes. + */ /** - * Invalid audio mode. - */ + * Invalid audio mode. + */ public static final int MODE_INVALID = AudioSystem.MODE_INVALID; /** * Current audio mode. Used to apply audio routing to current mode. - */ + */ public static final int MODE_CURRENT = AudioSystem.MODE_CURRENT; /** * Normal audio mode: not ringing and no call established. - */ + */ public static final int MODE_NORMAL = AudioSystem.MODE_NORMAL; /** * Ringing audio mode. An incoming is being signaled. - */ + */ public static final int MODE_RINGTONE = AudioSystem.MODE_RINGTONE; /** * In call audio mode. A call is established. - */ + */ public static final int MODE_IN_CALL = AudioSystem.MODE_IN_CALL; /* Routing bits for setRouting/getRouting API */ /** * Routing audio output to earpiece - */ + */ public static final int ROUTE_EARPIECE = AudioSystem.ROUTE_EARPIECE; /** * Routing audio output to spaker - */ + */ public static final int ROUTE_SPEAKER = AudioSystem.ROUTE_SPEAKER; /** * @deprecated use {@link #ROUTE_BLUETOOTH_SCO} - */ + */ @Deprecated public static final int ROUTE_BLUETOOTH = AudioSystem.ROUTE_BLUETOOTH_SCO; /** * Routing audio output to bluetooth SCO - */ + */ public static final int ROUTE_BLUETOOTH_SCO = AudioSystem.ROUTE_BLUETOOTH_SCO; /** * Routing audio output to headset - */ + */ public static final int ROUTE_HEADSET = AudioSystem.ROUTE_HEADSET; /** * Routing audio output to bluetooth A2DP - */ + */ public static final int ROUTE_BLUETOOTH_A2DP = AudioSystem.ROUTE_BLUETOOTH_A2DP; /** * Used for mask parameter of {@link #setRouting(int,int,int)}. - */ + */ public static final int ROUTE_ALL = AudioSystem.ROUTE_ALL; /** @@ -892,33 +930,33 @@ public class AudioManager { /** * Keyboard and direction pad click sound * @see #playSoundEffect(int) - */ + */ public static final int FX_KEY_CLICK = 0; /** * Focuse has moved up * @see #playSoundEffect(int) - */ + */ public static final int FX_FOCUS_NAVIGATION_UP = 1; /** * Focuse has moved down * @see #playSoundEffect(int) - */ + */ public static final int FX_FOCUS_NAVIGATION_DOWN = 2; /** * Focuse has moved left * @see #playSoundEffect(int) - */ + */ public static final int FX_FOCUS_NAVIGATION_LEFT = 3; /** * Focuse has moved right * @see #playSoundEffect(int) - */ + */ public static final int FX_FOCUS_NAVIGATION_RIGHT = 4; /** - * @hide Number of sound effects - */ + * @hide Number of sound effects + */ public static final int NUM_SOUND_EFFECTS = 5; - + /** * Plays a sound effect (Key clicks, lid open/close...) * @param effectType The type of sound effect. One of @@ -951,10 +989,10 @@ public class AudioManager { private boolean querySoundEffectsEnabled() { return Settings.System.getInt(mContext.getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED, 0) != 0; } - - + + /** - * Load Sound effects. + * Load Sound effects. * This method must be called when sound effects are enabled. */ public void loadSoundEffects() { @@ -965,9 +1003,9 @@ public class AudioManager { Log.e(TAG, "Dead object in loadSoundEffects"+e); } } - + /** - * Unload Sound effects. + * Unload Sound effects. * This method can be called to free some memory when * sound effects are disabled. */ diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 7912003b3a216..1314ba14648b3 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -34,11 +34,14 @@ import android.util.Log; * to record audio from the audio input hardware of the platform. This is * achieved by "pulling" (reading) the data from the AudioRecord object. The * application is responsible for polling the AudioRecord object in time using one of - * the following three methods: {@link #read(byte[], int)}, {@link #read(short[], int)} + * the following three methods: {@link #read(byte[],int, int)}, {@link #read(short[], int, int)} * or {@link #read(ByteBuffer, int)}. The choice of which method to use will be based * on the audio data storage format that is the most convenient for the user of AudioRecord. - * - * {@hide Pending API council review} + *

      Upon creation, an AudioRecord object initializes its associated audio buffer that it will + * fill with the new audio data. The size of this buffer, specified during the construction, + * determines how long an AudioRecord can record before "over-running" data that has not + * been read yet. Data should be from the audio hardware in chunks of sizes inferior to + * the total recording buffer size. */ public class AudioRecord { @@ -82,22 +85,22 @@ public class AudioRecord */ public static final int ERROR_INVALID_OPERATION = -3; - private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -4; - private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT = -5; - private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -6; - private static final int AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE = -7; - private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED = -8; + private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -16; + private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT = -17; + private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -18; + private static final int AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE = -19; + private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED = -20; // Events: // to keep in sync with frameworks/base/include/media/AudioRecord.h /** * Event id for when the recording head has reached a previously set marker. */ - protected static final int EVENT_MARKER = 2; + private static final int NATIVE_EVENT_MARKER = 2; /** * Event id for when the previously set update period has passed during recording. */ - protected static final int EVENT_NEW_POS = 3; + private static final int NATIVE_EVENT_NEW_POS = 3; private final static String TAG = "AudioRecord-Java"; @@ -130,63 +133,63 @@ public class AudioRecord /** * The audio data sampling rate in Hz. */ - protected int mSampleRate = 22050; + private int mSampleRate = 22050; /** * The number of input audio channels (1 is mono, 2 is stereo) */ - protected int mChannelCount = 1; + private int mChannelCount = 1; /** * The current audio channel configuration */ - protected int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; + private int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; /** * The encoding of the audio samples. - * @see #AudioFormat.ENCODING_PCM_8BIT - * @see #AudioFormat.ENCODING_PCM_16BIT + * @see AudioFormat#ENCODING_PCM_8BIT + * @see AudioFormat#ENCODING_PCM_16BIT */ - protected int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; + private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; /** * Where the audio data is recorded from. */ - protected int mRecordSource = MediaRecorder.AudioSource.DEFAULT; + private int mRecordSource = MediaRecorder.AudioSource.DEFAULT; /** * Indicates the state of the AudioRecord instance. */ - protected int mState = STATE_UNINITIALIZED; + private int mState = STATE_UNINITIALIZED; /** * Indicates the recording state of the AudioRecord instance. */ - protected int mRecordingState = RECORDSTATE_STOPPED; + private int mRecordingState = RECORDSTATE_STOPPED; /** * Lock to make sure mRecordingState updates are reflecting the actual state of the object. */ - protected Object mRecordingStateLock = new Object(); + private Object mRecordingStateLock = new Object(); /** * The listener the AudioRecord notifies when a previously set marker is reached. * @see #setMarkerReachedListener(OnMarkerReachedListener) */ - protected OnMarkerReachedListener mMarkerListener = null; + private OnMarkerReachedListener mMarkerListener = null; /** * Lock to protect marker listener updates against event notifications */ - protected final Object mMarkerListenerLock = new Object(); + private final Object mMarkerListenerLock = new Object(); /** * The listener the AudioRecord notifies periodically during recording. * @see #setPeriodicNotificationListener(OnPeriodicNotificationListener) */ - protected OnPeriodicNotificationListener mPeriodicListener = null; + private OnPeriodicNotificationListener mPeriodicListener = null; /** * Lock to protect periodic listener updates against event notifications */ - protected final Object mPeriodicListenerLock = new Object(); + private final Object mPeriodicListenerLock = new Object(); /** * Handler for events coming from the native code */ - protected NativeEventHandler mNativeEventHandler = null; + private NativeEventHandler mNativeEventHandler = null; /** * Size of the native audio buffer. */ - protected int mNativeBufferSizeInBytes = 0; + private int mNativeBufferSizeInBytes = 0; //--------------------------------------------------------- @@ -194,30 +197,30 @@ public class AudioRecord //-------------------- /** * Class constructor. - * @param audioSource the recording source. See {@link MediaRecorder.AudioSource.DEFAULT} - * and {@link MediaRecorder.AudioSource.MIC}. + * @param audioSource the recording source. See {@link MediaRecorder.AudioSource} for + * recording source definitions. * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but * not limited to) 44100, 22050 and 11025. * @param channelConfig describes the configuration of the audio channels. - * See {@link AudioFormat.CHANNEL_CONFIGURATION_MONO} and - * {@link AudioFormat.CHANNEL_CONFIGURATION_STEREO} + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and + * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO} * @param audioFormat the format in which the audio data is represented. - * See {@link AudioFormat.ENCODING_PCM_16BIT} and - * {@link AudioFormat.ENCODING_PCM_8BIT} + * See {@link AudioFormat#ENCODING_PCM_16BIT} and + * {@link AudioFormat#ENCODING_PCM_8BIT} * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written * to during the recording. New audio data can be read from this buffer in smaller chunks * than this size. * @throws java.lang.IllegalArgumentException */ public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, - int blockSizeInBytes) + int bufferSizeInBytes) throws IllegalArgumentException { mState = STATE_UNINITIALIZED; mRecordingState = RECORDSTATE_STOPPED; audioParamCheck(audioSource, sampleRateInHz, channelConfig, audioFormat); - audioBuffSizeCheck(blockSizeInBytes); + audioBuffSizeCheck(bufferSizeInBytes); // native initialization //TODO: update native initialization when information about hardware init failure @@ -367,8 +370,8 @@ public class AudioRecord } /** - * Returns the configured audio data format. See {@link #AudioFormat.ENCODING_PCM_16BIT} - * and {@link #AudioFormat.ENCODING_PCM_8BIT}. + * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT} + * and {@link AudioFormat#ENCODING_PCM_8BIT}. */ public int getAudioFormat() { return mAudioFormat; @@ -376,8 +379,8 @@ public class AudioRecord /** * Returns the configured channel configuration. - * See {@link #AudioFormat.CHANNEL_CONFIGURATION_MONO} - * and {@link #AudioFormat.CHANNEL_CONFIGURATION_STEREO}. + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} + * and {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}. */ public int getChannelConfiguration() { return mChannelConfiguration; @@ -395,8 +398,8 @@ public class AudioRecord * AudioRecord instance has been created to check if it was initialized * properly. This ensures that the appropriate hardware resources have been * acquired. - * @see AudioRecord.STATE_INITIALIZED - * @see AudioRecord.STATE_UNINITIALIZED + * @see AudioRecord#STATE_INITIALIZED + * @see AudioRecord#STATE_UNINITIALIZED */ public int getState() { return mState; @@ -404,8 +407,8 @@ public class AudioRecord /** * Returns the recording state of the AudioRecord instance. - * @see AudioRecord.RECORDSTATE_STOPPED - * @see AudioRecord.RECORDSTATE_RECORDING + * @see AudioRecord#RECORDSTATE_STOPPED + * @see AudioRecord#RECORDSTATE_RECORDING */ public int getRecordingState() { return mRecordingState; @@ -472,16 +475,15 @@ public class AudioRecord //-------------------- /** * Reads audio data from the audio hardware for recording into a buffer. - * @throws IllegalStateException * @param audioData the array to which the recorded audio data is written. * @param offsetInBytes index in audioData from which the data is written. * @param sizeInBytes the number of requested bytes. - * @return the number of bytes that were read. This will not exceed sizeInBytes + * @return the number of bytes that were read or -1 if the object wasn't properly + * initialized. The number of bytes will not exceed sizeInBytes. */ - public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) - throws IllegalStateException { + public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("read() called on uninitialized AudioRecord.")); + return -1; } return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes); @@ -490,16 +492,15 @@ public class AudioRecord /** * Reads audio data from the audio hardware for recording into a buffer. - * @throws IllegalStateException * @param audioData the array to which the recorded audio data is written. * @param offsetInShorts index in audioData from which the data is written. * @param sizeInShorts the number of requested shorts. - * @return the number of shorts that were read. This will not exceed sizeInShorts + * @return the number of shorts that were read. or -1 if the object wasn't properly + * initialized. The number of shorts will not exceed sizeInShorts */ - public int read(short[] audioData, int offsetInShorts, int sizeInShorts) - throws IllegalStateException { + public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("read() called on uninitialized AudioRecord.")); + return -1; } return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts); @@ -509,15 +510,14 @@ public class AudioRecord /** * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer * is not a direct buffer, this method will always return 0. - * @throws IllegalStateException * @param audioBuffer the direct buffer to which the recorded audio data is written. * @param sizeInBytes the number of requested bytes. - * @return the number of bytes that were read. This will not exceed sizeInBytes. + * @return the number of bytes that were read or -1 if the object wasn't properly + * initialized. The number of bytes will not exceed sizeInBytes. */ - public int read(ByteBuffer audioBuffer, int sizeInBytes) - throws IllegalStateException { + public int read(ByteBuffer audioBuffer, int sizeInBytes) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("read() called on uninitialized AudioRecord.")); + return -1; } return native_read_in_direct_buffer(audioBuffer, sizeInBytes); @@ -590,7 +590,7 @@ public class AudioRecord * Called on the listener to notify it that the previously set marker has been reached * by the recording head. */ - void onMarkerReached(AudioRecord track); + void onMarkerReached(AudioRecord recorder); } @@ -603,7 +603,7 @@ public class AudioRecord * Called on the listener to periodically notify it that the recording head has reached * a multiple of the notification period. */ - void onPeriodicNotification(AudioRecord track); + void onPeriodicNotification(AudioRecord recorder); } @@ -628,14 +628,14 @@ public class AudioRecord return; } switch(msg.what) { - case EVENT_MARKER: + case NATIVE_EVENT_MARKER: synchronized (mMarkerListenerLock) { if (mAudioRecord.mMarkerListener != null) { mAudioRecord.mMarkerListener.onMarkerReached(mAudioRecord); } } break; - case EVENT_NEW_POS: + case NATIVE_EVENT_NEW_POS: synchronized (mPeriodicListenerLock) { if (mAudioRecord.mPeriodicListener != null) { mAudioRecord.mPeriodicListener.onPeriodicNotification(mAudioRecord); @@ -643,7 +643,7 @@ public class AudioRecord } break; default: - Log.e(TAG, "[ android.media.AudioTrack.NativeEventHandler ] " + + Log.e(TAG, "[ android.media.AudioRecord.NativeEventHandler ] " + "Unknown event type: " + msg.what); break; } @@ -658,14 +658,14 @@ public class AudioRecord private static void postEventFromNative(Object audiorecord_ref, int what, int arg1, int arg2, Object obj) { //logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2); - AudioRecord track = (AudioRecord)((WeakReference)audiorecord_ref).get(); - if (track == null) { + AudioRecord recorder = (AudioRecord)((WeakReference)audiorecord_ref).get(); + if (recorder == null) { return; } - if (track.mNativeEventHandler != null) { - Message m = track.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); - track.mNativeEventHandler.sendMessage(m); + if (recorder.mNativeEventHandler != null) { + Message m = recorder.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); + recorder.mNativeEventHandler.sendMessage(m); } } diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index b39e7bb1911d6..f3d895703b986 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -16,8 +16,7 @@ package android.media; -import com.android.internal.telephony.ITelephony; - +import android.app.ActivityManagerNative; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -32,11 +31,13 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; -import android.provider.Settings.System; import android.provider.Settings; +import android.provider.Settings.System; import android.util.Log; import android.view.VolumePanel; +import com.android.internal.telephony.ITelephony; + import java.io.IOException; import java.util.ArrayList; @@ -50,19 +51,19 @@ import java.util.ArrayList; * volume and later persist to the database. Similarly, setting the ringer mode * will update the state and broadcast a change and in a separate thread later * persist the ringer mode. - * + * * @hide */ public class AudioService extends IAudioService.Stub { - + private static final String TAG = "AudioService"; /** How long to delay before persisting a change in volume/ringer mode. */ private static final int PERSIST_DELAY = 3000; - + private Context mContext; private ContentResolver mContentResolver; - + /** The UI */ private VolumePanel mVolumePanel; @@ -75,7 +76,7 @@ public class AudioService extends IAudioService.Stub { private static final int SENDMSG_NOOP = 1; /** If the msg is already queued, queue this one and leave the old. */ private static final int SENDMSG_QUEUE = 2; - + // AudioHandler message.whats private static final int MSG_SET_SYSTEM_VOLUME = 0; private static final int MSG_PERSIST_VOLUME = 1; @@ -91,18 +92,18 @@ public class AudioService extends IAudioService.Stub { private AudioHandler mAudioHandler; /** @see VolumeStreamState */ private VolumeStreamState[] mStreamStates; - + private boolean mMicMute; private int mMode; private int[] mRoutes = new int[AudioSystem.NUM_MODES]; private Object mSettingsLock = new Object(); private boolean mMediaServerOk; - + private SoundPool mSoundPool; private Object mSoundEffectsLock = new Object(); private static final int NUM_SOUNDPOOL_CHANNELS = 4; private static final float SOUND_EFFECT_VOLUME = 1.0f; - + /* Sound effect file names */ private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/"; private static final String[] SOUND_EFFECT_FILES = new String[] { @@ -119,7 +120,7 @@ public class AudioService extends IAudioService.Stub { {0, -1}, // FX_FOCUS_NAVIGATION_LEFT {0, -1} // FX_FOCUS_NAVIGATION_RIGHT }; - + private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() { public void onError(int error) { switch (error) { @@ -145,7 +146,7 @@ public class AudioService extends IAudioService.Stub { * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL}, * {@link AudioManager#RINGER_MODE_SILENT}, or * {@link AudioManager#RINGER_MODE_VIBRATE}. - */ + */ private int mRingerMode; /** @see System#MODE_RINGER_STREAMS_AFFECTED */ @@ -153,7 +154,7 @@ public class AudioService extends IAudioService.Stub { /** @see System#MUTE_STREAMS_AFFECTED */ private int mMuteAffectedStreams; - + /** * Has multiple bits per vibrate type to indicate the type's vibrate * setting. See {@link #setVibrateSetting(int, int)}. @@ -162,23 +163,23 @@ public class AudioService extends IAudioService.Stub { * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}. */ private int mVibrateSetting; - + /////////////////////////////////////////////////////////////////////////// // Construction /////////////////////////////////////////////////////////////////////////// - + /** @hide */ public AudioService(Context context) { mContext = context; mContentResolver = context.getContentResolver(); mVolumePanel = new VolumePanel(context, this); - + createAudioSystemThread(); createStreamStates(); readPersistedSettings(); readAudioSettings(); mMediaServerOk = true; - AudioSystem.setErrorCallback(mAudioSystemCallback); + AudioSystem.setErrorCallback(mAudioSystemCallback); if (Settings.System.getInt(mContentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 0) == 1) { loadSoundEffects(); } @@ -189,8 +190,8 @@ public class AudioService extends IAudioService.Stub { mAudioSystemThread.start(); waitForAudioHandlerCreation(); } - - /** Waits for the volume handler to be created by the other thread. */ + + /** Waits for the volume handler to be created by the other thread. */ private void waitForAudioHandlerCreation() { synchronized(this) { while (mAudioHandler == null) { @@ -203,37 +204,51 @@ public class AudioService extends IAudioService.Stub { } } } - + private void createStreamStates() { - final int[] volumeLevelsPhone = createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]); - final int[] volumeLevelsCoarse = createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]); - final int[] volumeLevelsFine = createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]); - + final int[] volumeLevelsPhone = + createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]); + final int[] volumeLevelsCoarse = + createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]); + final int[] volumeLevelsFine = + createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]); + final int[] volumeLevelsBtPhone = + createVolumeLevels(0, + AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]); + int numStreamTypes = AudioSystem.getNumStreamTypes(); VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes]; - + for (int i = 0; i < numStreamTypes; i++) { final int[] levels; - + switch (i) { - + case AudioSystem.STREAM_MUSIC: levels = volumeLevelsFine; break; - + case AudioSystem.STREAM_VOICE_CALL: levels = volumeLevelsPhone; break; - + + case AudioSystem.STREAM_BLUETOOTH_SCO: + levels = volumeLevelsBtPhone; + break; + default: levels = volumeLevelsCoarse; break; } - - streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[i], i, levels); + + if (i == AudioSystem.STREAM_BLUETOOTH_SCO) { + streams[i] = new VolumeStreamState(AudioManager.DEFAULT_STREAM_VOLUME[i], i,levels); + } else { + streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[i], i, levels); + } } } - + private static int[] createVolumeLevels(int offset, int numlevels) { double curve = 1.0f; // 1.4f int [] volumes = new int[numlevels + offset]; @@ -249,7 +264,7 @@ public class AudioService extends IAudioService.Stub { } return volumes; } - + private void readPersistedSettings() { final ContentResolver cr = mContentResolver; @@ -260,19 +275,19 @@ public class AudioService extends IAudioService.Stub { mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0); mMuteAffectedStreams = System.getInt(cr, - System.MUTE_STREAMS_AFFECTED, + System.MUTE_STREAMS_AFFECTED, ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM))); - + // Each stream will read its own persisted settings - + // Broadcast the sticky intent broadcastRingerMode(); - + // Broadcast vibrate settings broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); } - + private void readAudioSettings() { synchronized (mSettingsLock) { mMicMute = AudioSystem.isMicrophoneMuted(); @@ -282,7 +297,7 @@ public class AudioService extends IAudioService.Stub { } } } - + private void applyAudioSettings() { synchronized (mSettingsLock) { AudioSystem.muteMicrophone(mMicMute); @@ -291,46 +306,46 @@ public class AudioService extends IAudioService.Stub { AudioSystem.setRouting(mode, mRoutes[mode], AudioSystem.ROUTE_ALL); } } - } - + } + /////////////////////////////////////////////////////////////////////////// // IPC methods /////////////////////////////////////////////////////////////////////////// - + /** @see AudioManager#adjustVolume(int, int) */ public void adjustVolume(int direction, int flags) { adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags); } - + /** @see AudioManager#adjustVolume(int, int, int) */ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { int streamType = getActiveStreamType(suggestedStreamType); - + // Don't play sound on other streams if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) { flags &= ~AudioManager.FLAG_PLAY_SOUND; } - + adjustStreamVolume(streamType, direction, flags); } - + /** @see AudioManager#adjustStreamVolume(int, int, int) */ public void adjustStreamVolume(int streamType, int direction, int flags) { ensureValidDirection(direction); ensureValidStreamType(streamType); - + boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver, Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1; if (notificationsUseRingVolume && streamType == AudioManager.STREAM_NOTIFICATION) { // Redirect the volume change to the ring stream streamType = AudioManager.STREAM_RING; } - + VolumeStreamState streamState = mStreamStates[streamType]; - final int oldIndex = streamState.mIndex; + final int oldIndex = streamState.mIndex; boolean adjustVolume = true; - + // If either the client forces allowing ringer modes for this adjustment, // or the stream type is one that is affected by ringer modes if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0 @@ -339,21 +354,21 @@ public class AudioService extends IAudioService.Stub { // it does, it will handle adjusting the volome, so we won't below adjustVolume = checkForRingerModeChange(oldIndex, direction); } - + if (adjustVolume && streamState.adjustIndex(direction)) { - + boolean alsoUpdateNotificationVolume = notificationsUseRingVolume && streamType == AudioManager.STREAM_RING; if (alsoUpdateNotificationVolume) { mStreamStates[AudioManager.STREAM_NOTIFICATION].adjustIndex(direction); } - + // Post message to set system volume (it in turn will post a message // to persist). Do not change volume if stream is muted. if (streamState.muteCount() == 0) { sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0, streamState, 0); - + if (alsoUpdateNotificationVolume) { sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, AudioManager.STREAM_NOTIFICATION, SENDMSG_NOOP, 0, 0, mStreamStates[AudioManager.STREAM_NOTIFICATION], 0); @@ -363,12 +378,44 @@ public class AudioService extends IAudioService.Stub { // UI mVolumePanel.postVolumeChanged(streamType, flags); + // Broadcast Intent + sendVolumeUpdate(streamType); } /** @see AudioManager#setStreamVolume(int, int, int) */ public void setStreamVolume(int streamType, int index, int flags) { ensureValidStreamType(streamType); - + syncRingerAndNotificationStreamVolume(streamType, index, false); + + setStreamVolumeInt(streamType, index, false); + + // UI, etc. + mVolumePanel.postVolumeChanged(streamType, flags); + // Broadcast Intent + sendVolumeUpdate(streamType); + } + + private void sendVolumeUpdate(int streamType) { + Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION); + intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); + intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, getStreamVolume(streamType)); + + // Currently, sending the intent only when the stream is BLUETOOTH_SCO + if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) { + mContext.sendBroadcast(intent); + } + } + + /** + * Sync the STREAM_RING and STREAM_NOTIFICATION volumes if mandated by the + * value in Settings. + * + * @param streamType Type of the stream + * @param index Volume index for the stream + * @param force If true, set the volume even if the current and desired + * volume as same + */ + private void syncRingerAndNotificationStreamVolume(int streamType, int index, boolean force) { boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver, Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1; if (notificationsUseRingVolume) { @@ -378,23 +425,24 @@ public class AudioService extends IAudioService.Stub { } if (streamType == AudioManager.STREAM_RING) { // One-off to sync notification volume to ringer volume - setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index); + setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index, force); } } - - setStreamVolumeInt(streamType, index); - - // UI, etc. - mVolumePanel.postVolumeChanged(streamType, flags); } + /** * Sets the stream state's index, and posts a message to set system volume. * This will not call out to the UI. Assumes a valid stream type. + * + * @param streamType Type of the stream + * @param index Desired volume index of the stream + * @param force If true, set the volume even if the desired volume is same + * as the current volume. */ - private void setStreamVolumeInt(int streamType, int index) { + private void setStreamVolumeInt(int streamType, int index, boolean force) { VolumeStreamState streamState = mStreamStates[streamType]; - if (streamState.setIndex(index)) { + if (streamState.setIndex(index) || force) { // Post message to set system volume (it in turn will post a message // to persist). Do not change volume if stream is muted. if (streamState.muteCount() == 0) { @@ -403,7 +451,7 @@ public class AudioService extends IAudioService.Stub { } } } - + /** @see AudioManager#setStreamSolo(int, boolean) */ public void setStreamSolo(int streamType, boolean state, IBinder cb) { for (int stream = 0; stream < mStreamStates.length; stream++) { @@ -412,7 +460,7 @@ public class AudioService extends IAudioService.Stub { mStreamStates[stream].mute(cb, state); } } - + /** @see AudioManager#setStreamMute(int, boolean) */ public void setStreamMute(int streamType, boolean state, IBinder cb) { if (isStreamAffectedByMute(streamType)) { @@ -441,32 +489,33 @@ public class AudioService extends IAudioService.Stub { public void setRingerMode(int ringerMode) { if (ringerMode != mRingerMode) { mRingerMode = ringerMode; - + // Adjust volumes via posting message int numStreamTypes = AudioSystem.getNumStreamTypes(); if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { if (!isStreamAffectedByRingerMode(streamType)) continue; // Bring back last audible volume - setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex); + setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex, + false); } } else { for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { if (!isStreamAffectedByRingerMode(streamType)) continue; // Either silent or vibrate, either way volume is 0 - setStreamVolumeInt(streamType, 0); + setStreamVolumeInt(streamType, 0, false); } } - + // Send sticky broadcast broadcastRingerMode(); - + // Post a persist ringer mode msg sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG, SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY); } } - + /** @see AudioManager#shouldVibrate(int) */ public boolean shouldVibrate(int vibrateType) { @@ -474,21 +523,21 @@ public class AudioService extends IAudioService.Stub { case AudioManager.VIBRATE_SETTING_ON: return mRingerMode != AudioManager.RINGER_MODE_SILENT; - + case AudioManager.VIBRATE_SETTING_ONLY_SILENT: return mRingerMode == AudioManager.RINGER_MODE_VIBRATE; - + case AudioManager.VIBRATE_SETTING_OFF: - // Phone ringer should always vibrate in vibrate mode + // Phone ringer should always vibrate in vibrate mode if (vibrateType == AudioManager.VIBRATE_TYPE_RINGER) { return mRingerMode == AudioManager.RINGER_MODE_VIBRATE; } - + default: return false; } } - + /** @see AudioManager#getVibrateSetting(int) */ public int getVibrateSetting(int vibrateType) { return (mVibrateSetting >> (vibrateType * 2)) & 3; @@ -498,10 +547,10 @@ public class AudioService extends IAudioService.Stub { public void setVibrateSetting(int vibrateType, int vibrateSetting) { mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting); - + // Broadcast change broadcastVibrateSetting(vibrateType); - + // Post message to set ringer mode (it in turn will post a message // to persist) sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0, @@ -518,13 +567,13 @@ public class AudioService extends IAudioService.Stub { // First clear the existing setting. Each vibrate type has two bits in // the value. Note '3' is '11' in binary. existingValue &= ~(3 << (vibrateType * 2)); - + // Set into the old value existingValue |= (vibrateSetting & 3) << (vibrateType * 2); - + return existingValue; } - + /** @see AudioManager#setMicrophoneMute(boolean) */ public void setMicrophoneMute(boolean on) { if (!checkAudioSettingsPermission("setMicrophoneMute()")) { @@ -534,15 +583,15 @@ public class AudioService extends IAudioService.Stub { if (on != mMicMute) { AudioSystem.muteMicrophone(on); mMicMute = on; - } + } } } - + /** @see AudioManager#isMicrophoneMute() */ public boolean isMicrophoneMute() { return mMicMute; } - + /** @see AudioManager#setMode(int) */ public void setMode(int mode) { if (!checkAudioSettingsPermission("setMode()")) { @@ -552,15 +601,19 @@ public class AudioService extends IAudioService.Stub { if (mode != mMode) { AudioSystem.setMode(mode); mMode = mode; - } + } + int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); + int index = mStreamStates[streamType].mIndex; + syncRingerAndNotificationStreamVolume(streamType, index, true); + setStreamVolumeInt(streamType, index, true); } } - + /** @see AudioManager#getMode() */ public int getMode() { return mMode; } - + /** @see AudioManager#setRouting(int, int, int) */ public void setRouting(int mode, int routes, int mask) { if (!checkAudioSettingsPermission("setRouting()")) { @@ -570,10 +623,14 @@ public class AudioService extends IAudioService.Stub { if ((mRoutes[mode] & mask) != (routes & mask)) { AudioSystem.setRouting(mode, routes, mask); mRoutes[mode] = (mRoutes[mode] & ~mask) | (routes & mask); - } + } + int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); + int index = mStreamStates[streamType].mIndex; + syncRingerAndNotificationStreamVolume(streamType, index, true); + setStreamVolumeInt(streamType, index, true); } } - + /** @see AudioManager#getRouting(int) */ public int getRouting(int mode) { return mRoutes[mode]; @@ -583,7 +640,7 @@ public class AudioService extends IAudioService.Stub { public boolean isMusicActive() { return AudioSystem.isMusicActive(); } - + /** @see AudioManager#setParameter(String, String) */ public void setParameter(String key, String value) { AudioSystem.setParameter(key, value); @@ -596,8 +653,8 @@ public class AudioService extends IAudioService.Stub { } /** - * Loads samples into the soundpool. - * This method must be called at when sound effects are enabled + * Loads samples into the soundpool. + * This method must be called at when sound effects are enabled */ public boolean loadSoundEffects() { synchronized (mSoundEffectsLock) { @@ -605,17 +662,17 @@ public class AudioService extends IAudioService.Stub { if (mSoundPool == null) { return false; } - /* - * poolId table: The value -1 in this table indicates that corresponding - * file (same index in SOUND_EFFECT_FILES[] has not been loaded. - * Once loaded, the value in poolId is the sample ID and the same + /* + * poolId table: The value -1 in this table indicates that corresponding + * file (same index in SOUND_EFFECT_FILES[] has not been loaded. + * Once loaded, the value in poolId is the sample ID and the same * sample can be reused for another effect using the same file. - */ + */ int[] poolId = new int[SOUND_EFFECT_FILES.length]; for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { poolId[fileIdx] = -1; } - /* + /* * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded. * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0: * this indicates we have a valid sample loaded for this effect. @@ -638,12 +695,12 @@ public class AudioService extends IAudioService.Stub { } } } - + return true; } /** - * Unloads samples from the sound pool. + * Unloads samples from the sound pool. * This method can be called to free some memory when * sound effects are disabled. */ @@ -665,12 +722,12 @@ public class AudioService extends IAudioService.Stub { mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]); SOUND_EFFECT_FILES_MAP[effect][1] = -1; poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1; - } + } } mSoundPool = null; } } - + /////////////////////////////////////////////////////////////////////////// // Internal methods /////////////////////////////////////////////////////////////////////////// @@ -683,7 +740,7 @@ public class AudioService extends IAudioService.Stub { private boolean checkForRingerModeChange(int oldIndex, int direction) { boolean adjustVolumeIndex = true; int newRingerMode = mRingerMode; - + if (mRingerMode == AudioManager.RINGER_MODE_NORMAL && oldIndex == 1 && direction == AudioManager.ADJUST_LOWER) { newRingerMode = AudioManager.RINGER_MODE_VIBRATE; @@ -697,7 +754,7 @@ public class AudioService extends IAudioService.Stub { && mRingerMode == AudioManager.RINGER_MODE_SILENT) { newRingerMode = AudioManager.RINGER_MODE_VIBRATE; } - + if (newRingerMode != mRingerMode) { setRingerMode(newRingerMode); @@ -708,18 +765,18 @@ public class AudioService extends IAudioService.Stub { */ adjustVolumeIndex = false; } - + return adjustVolumeIndex; } - + public boolean isStreamAffectedByRingerMode(int streamType) { - return (mRingerModeAffectedStreams & (1 << streamType)) != 0; + return (mRingerModeAffectedStreams & (1 << streamType)) != 0; } - + public boolean isStreamAffectedByMute(int streamType) { return (mMuteAffectedStreams & (1 << streamType)) != 0; } - + private void ensureValidDirection(int direction) { if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) { throw new IllegalArgumentException("Bad direction " + direction); @@ -736,13 +793,15 @@ public class AudioService extends IAudioService.Stub { boolean isOffhook = false; try { ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); - isOffhook = phone.isOffhook(); + if (phone != null) isOffhook = phone.isOffhook(); } catch (RemoteException e) { Log.w(TAG, "Couldn't connect to phone service", e); } - // TODO: applications can influence this - if (isOffhook) { + if ((getRouting(AudioSystem.MODE_IN_CALL) & AudioSystem.ROUTE_BLUETOOTH_SCO) != 0) { + // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); + return AudioSystem.STREAM_BLUETOOTH_SCO; + } else if (isOffhook) { // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); return AudioSystem.STREAM_VOICE_CALL; } else if (AudioSystem.isMusicActive()) { @@ -756,29 +815,33 @@ public class AudioService extends IAudioService.Stub { return suggestedStreamType; } } - + private void broadcastRingerMode() { // Send sticky broadcast - Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); - broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode); - long origCallerIdentityToken = Binder.clearCallingIdentity(); - mContext.sendStickyBroadcast(broadcast); - Binder.restoreCallingIdentity(origCallerIdentityToken); + if (ActivityManagerNative.isSystemReady()) { + Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); + broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode); + long origCallerIdentityToken = Binder.clearCallingIdentity(); + mContext.sendStickyBroadcast(broadcast); + Binder.restoreCallingIdentity(origCallerIdentityToken); + } } - + private void broadcastVibrateSetting(int vibrateType) { // Send broadcast - Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); - broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); - broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); - mContext.sendBroadcast(broadcast); + if (ActivityManagerNative.isSystemReady()) { + Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); + broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); + broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); + mContext.sendBroadcast(broadcast); + } } - + // Message helper methods private static int getMsg(int baseMsg, int streamType) { - return (baseMsg & 0xffff) | streamType << 16; + return (baseMsg & 0xffff) | streamType << 16; } - + private static int getMsgBase(int msg) { return msg & 0xffff; } @@ -792,11 +855,11 @@ public class AudioService extends IAudioService.Stub { } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { return; } - + handler .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); } - + boolean checkAudioSettingsPermission(String method) { if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS") == PackageManager.PERMISSION_GRANTED) { @@ -809,29 +872,29 @@ public class AudioService extends IAudioService.Stub { return false; } - + /////////////////////////////////////////////////////////////////////////// // Inner classes /////////////////////////////////////////////////////////////////////////// - + public class VolumeStreamState { private final String mVolumeIndexSettingName; - private final String mLastAudibleVolumeIndexSettingName; + private final String mLastAudibleVolumeIndexSettingName; private final int mStreamType; - + private final int[] mVolumes; private int mIndex; private int mLastAudibleIndex; private ArrayList mDeathHandlers; //handles mute/solo requests client death - + private VolumeStreamState(String settingName, int streamType, int[] volumes) { - + mVolumeIndexSettingName = settingName; mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; - + mStreamType = streamType; mVolumes = volumes; - + final ContentResolver cr = mContentResolver; mIndex = getValidIndex(Settings.System.getInt(cr, mVolumeIndexSettingName, AudioManager.DEFAULT_STREAM_VOLUME[streamType])); mLastAudibleIndex = getValidIndex(Settings.System.getInt(cr, @@ -841,14 +904,31 @@ public class AudioService extends IAudioService.Stub { mDeathHandlers = new ArrayList(); } + /** + * Constructor to be used when there is no setting associated with the VolumeStreamState. + * + * @param defaultVolume Default volume of the stream to use. + * @param streamType Type of the stream. + * @param volumes Volumes levels associated with this stream. + */ + private VolumeStreamState(int defaultVolume, int streamType, int[] volumes) { + mVolumeIndexSettingName = null; + mLastAudibleVolumeIndexSettingName = null; + mIndex = mLastAudibleIndex = defaultVolume; + mStreamType = streamType; + mVolumes = volumes; + AudioSystem.setVolume(mStreamType, defaultVolume); + mDeathHandlers = new ArrayList(); + } + public boolean adjustIndex(int deltaIndex) { return setIndex(mIndex + deltaIndex); } - + public boolean setIndex(int index) { int oldIndex = mIndex; mIndex = getValidIndex(index); - + if (oldIndex != mIndex) { if (mIndex > 0) { mLastAudibleIndex = mIndex; @@ -858,11 +938,11 @@ public class AudioService extends IAudioService.Stub { return false; } } - + public int getMaxIndex() { return mVolumes.length - 1; } - + public void mute(IBinder cb, boolean state) { VolumeDeathHandler handler = getDeathHandler(cb, state); if (handler == null) { @@ -871,17 +951,17 @@ public class AudioService extends IAudioService.Stub { } handler.mute(state); } - + private int getValidIndex(int index) { if (index < 0) { return 0; } else if (index >= mVolumes.length) { return mVolumes.length - 1; } - + return index; } - + private class VolumeDeathHandler implements IBinder.DeathRecipient { private IBinder mICallback; // To be notified of client's death private int mMuteCount; // Number of active mutes for this client @@ -924,7 +1004,7 @@ public class AudioService extends IAudioService.Stub { mDeathHandlers.remove(this); mICallback.unlinkToDeath(this, 0); if (muteCount() == 0) { - // If the stream is not mut any more, restore it's volume if + // If the stream is not mut any more, restore it's volume if // ringer mode allows it if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) { setIndex(mLastAudibleIndex); @@ -980,59 +1060,59 @@ public class AudioService extends IAudioService.Stub { } } } - + /** Thread that handles native AudioSystem control. */ private class AudioSystemThread extends Thread { AudioSystemThread() { super("AudioService"); } - + @Override public void run() { // Set this thread up so the handler will work on it Looper.prepare(); - + synchronized(AudioService.this) { mAudioHandler = new AudioHandler(); // Notify that the handler has been created AudioService.this.notify(); } - + // Listen for volume change requests that are set by VolumePanel Looper.loop(); } } - + /** Handles internal volume messages in separate volume thread. */ private class AudioHandler extends Handler { - + private void setSystemVolume(VolumeStreamState streamState) { - + // Adjust volume AudioSystem .setVolume(streamState.mStreamType, streamState.mVolumes[streamState.mIndex]); - + // Post a persist volume msg sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType, SENDMSG_REPLACE, 0, 0, streamState, PERSIST_DELAY); } - + private void persistVolume(VolumeStreamState streamState) { System.putInt(mContentResolver, streamState.mVolumeIndexSettingName, streamState.mIndex); System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName, streamState.mLastAudibleIndex); } - + private void persistRingerMode() { System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode); } - + private void persistVibrateSetting() { System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting); } - + private void playSoundEffect(int effectType) { synchronized (mSoundEffectsLock) { if (mSoundPool == null) { @@ -1084,36 +1164,36 @@ public class AudioService extends IAudioService.Stub { } } } - + @Override public void handleMessage(Message msg) { int baseMsgWhat = getMsgBase(msg.what); - + switch (baseMsgWhat) { - + case MSG_SET_SYSTEM_VOLUME: setSystemVolume((VolumeStreamState) msg.obj); break; - + case MSG_PERSIST_VOLUME: persistVolume((VolumeStreamState) msg.obj); break; - + case MSG_PERSIST_RINGER_MODE: persistRingerMode(); break; - + case MSG_PERSIST_VIBRATE_SETTING: persistVibrateSetting(); break; - + case MSG_MEDIA_SERVER_DIED: Log.e(TAG, "Media server died."); - // Force creation of new IAudioflinger interface + // Force creation of new IAudioflinger interface mMediaServerOk = false; AudioSystem.getMode(); break; - + case MSG_MEDIA_SERVER_STARTED: Log.e(TAG, "Media server started."); // Restore audio routing and stream volumes @@ -1139,5 +1219,5 @@ public class AudioService extends IAudioService.Stub { } } } - + } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 37100677329f8..d0fa7953dd210 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -43,15 +43,17 @@ public class AudioSystem public static final int STREAM_ALARM = 4; /* The audio stream for notifications */ public static final int STREAM_NOTIFICATION = 5; + /* @hide The audio stream for phone calls when connected on bluetooth */ + public static final int STREAM_BLUETOOTH_SCO = 6; /** * @deprecated Use {@link #numStreamTypes() instead} */ public static final int NUM_STREAMS = 5; // Expose only the getter method publicly so we can change it in the future - private static final int NUM_STREAM_TYPES = 6; + private static final int NUM_STREAM_TYPES = 7; public static final int getNumStreamTypes() { return NUM_STREAM_TYPES; } - + /* max and min volume levels */ /* Maximum volume setting, for use with setVolume(int,int) */ public static final int MAX_VOLUME = 100; @@ -78,7 +80,7 @@ public class AudioSystem /* * Sets the microphone mute on or off. * - * param on set true to mute the microphone; + * param on set true to mute the microphone; * false to turn mute off * return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR */ @@ -116,18 +118,18 @@ public class AudioSystem public static final int MODE_RINGTONE = 1; public static final int MODE_IN_CALL = 2; public static final int NUM_MODES = 3; - + /* Routing bits for setRouting/getRouting API */ public static final int ROUTE_EARPIECE = (1 << 0); public static final int ROUTE_SPEAKER = (1 << 1); - - /** @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */ + + /** @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */ @Deprecated public static final int ROUTE_BLUETOOTH = (1 << 2); public static final int ROUTE_BLUETOOTH_SCO = (1 << 2); public static final int ROUTE_HEADSET = (1 << 3); public static final int ROUTE_BLUETOOTH_A2DP = (1 << 4); - public static final int ROUTE_ALL = 0xFFFFFFFF; + public static final int ROUTE_ALL = 0xFFFFFFFF; /* * Sets the audio routing for a specified mode @@ -185,7 +187,7 @@ public class AudioSystem public static final int AUDIO_STATUS_ERROR = 1; /* Media server died. see ErrorCallback */ public static final int AUDIO_STATUS_SERVER_DIED = 100; - + private static ErrorCallback mErrorCallback; /* @@ -211,7 +213,7 @@ public class AudioSystem { mErrorCallback = cb; } - + private static void errorCallbackFromNative(int error) { if (mErrorCallback != null) { diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 2b7656f1dd860..9bb1df94080a0 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -30,12 +30,31 @@ import android.util.Log; /** * The AudioTrack class manages and plays a single audio resource for Java applications. * It allows to stream PCM audio buffers to the audio hardware for playback. This is - * be achieved by "pushing" the data to the AudioTrack object using the - * {@link #write(byte[], int, int)} or {@link #write(short[], int, int)} method. - * During construction, an AudioTrack object can be initialized with a given buffer. - * This size determines how long an AudioTrack can play before running out of data. - * - * {@hide Pending API council review} + * achieved by "pushing" the data to the AudioTrack object using one of the + * {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods. + *

      An AudioTrack instance can operate under two modes: static of streaming.
      + * The Streaming mode consists in continuously writing data to the AudioTrack, using one + * of the write() methods. These are blocking and return when the data has been transferred + * from the Java layer to the native layer, and is queued for playback. The streaming mode + * is most useful when playing blocks of audio data that for instance are: + *

        + *
      • too big to fit in memory because of the duration of the sound to play,
      • + *
      • too big to fit in memory because of the characteristics of the audio data + * (high sampling rate, bits per sample ...)
      • + *
      • chosen, received or generated as the audio keeps playing.
      • + *
      + * The static mode is to be chosen when dealing with short sounds that fit in memory and + * that need to be played with the smallest latency possible. Static mode AudioTrack instances can + * play the sound without the need to transfer the audio data from Java to the audio hardware + * each time the sound is to be played. The static mode will therefore be preferred for UI and + * game sounds that are played often, and with the smallest overhead possible. + *

      Upon creation, an AudioTrack object initializes its associated audio buffer. + * The size of this buffer, specified during the construction, determines how long an AudioTrack + * can play before running out of data.
      + * For an AudioTrack using the static mode, this size is the maximum size of the sound that can + * be played from it.
      + * For the streaming mode, data will be written to the hardware in chunks of + * sizes inferior to the total buffer size. */ public class AudioTrack { @@ -46,42 +65,41 @@ public class AudioTrack private static final float VOLUME_MIN = 0.0f; /** Maximum value for a channel volume */ private static final float VOLUME_MAX = 1.0f; - + /** state of an AudioTrack this is stopped */ public static final int PLAYSTATE_STOPPED = 1; // matches SL_PLAYSTATE_STOPPED /** state of an AudioTrack this is paused */ public static final int PLAYSTATE_PAUSED = 2; // matches SL_PLAYSTATE_PAUSED /** state of an AudioTrack this is playing */ public static final int PLAYSTATE_PLAYING = 3; // matches SL_PLAYSTATE_PLAYING - - /** - * Creation mode where audio data is transferred from Java to the native layer + + /** + * Creation mode where audio data is transferred from Java to the native layer * only once before the audio starts playing. */ public static final int MODE_STATIC = 0; - /** - * Creation mode where audio data is streamed from Java to the native layer + /** + * Creation mode where audio data is streamed from Java to the native layer * as the audio is playing. */ public static final int MODE_STREAM = 1; - - /** - * State of an AudioTrack that was not successfully initialized upon creation + + /** + * State of an AudioTrack that was not successfully initialized upon creation */ public static final int STATE_UNINITIALIZED = 0; - /** + /** * State of an AudioTrack that is ready to be used. */ public static final int STATE_INITIALIZED = 1; /** - * State of a successfully initialized AudioTrack that uses static data, + * State of a successfully initialized AudioTrack that uses static data, * but that hasn't received that data yet. */ public static final int STATE_NO_STATIC_DATA = 2; - - // to keep in sync with libs/android_runtime/android_media_AudioTrack.cpp - // error codes + // Error codes: + // to keep in sync with frameworks/base/core/jni/android_media_AudioTrack.cpp /** * Denotes a successful operation. */ @@ -90,139 +108,145 @@ public class AudioTrack * Denotes a generic operation failure. */ public static final int ERROR = -1; - private static final int ERROR_NATIVESETUP_AUDIOSYSTEM = -2; - private static final int ERROR_NATIVESETUP_INVALIDCHANNELCOUNT = -3; - private static final int ERROR_NATIVESETUP_INVALIDFORMAT = -4; - private static final int ERROR_NATIVESETUP_INVALIDSTREAMTYPE = -5; - private static final int ERROR_NATIVESETUP_NATIVEINITFAILED = -6; /** * Denotes a failure due to the use of an invalid value. */ - public static final int ERROR_BAD_VALUE = -7; + public static final int ERROR_BAD_VALUE = -2; /** * Denotes a failure due to the improper use of a method. */ - public static final int ERROR_INVALID_OPERATION = -8; - // events + public static final int ERROR_INVALID_OPERATION = -3; + + private static final int ERROR_NATIVESETUP_AUDIOSYSTEM = -16; + private static final int ERROR_NATIVESETUP_INVALIDCHANNELCOUNT = -17; + private static final int ERROR_NATIVESETUP_INVALIDFORMAT = -18; + private static final int ERROR_NATIVESETUP_INVALIDSTREAMTYPE = -19; + private static final int ERROR_NATIVESETUP_NATIVEINITFAILED = -20; + + // Events: + // to keep in sync with frameworks/base/include/media/AudioTrack.h /** * Event id for when the playback head has reached a previously set marker. */ - protected static final int NATIVE_EVENT_MARKER = 3; + private static final int NATIVE_EVENT_MARKER = 3; /** * Event id for when the previously set update period has passed during playback. */ - protected static final int NATIVE_EVENT_NEW_POS = 4; - + private static final int NATIVE_EVENT_NEW_POS = 4; + private final static String TAG = "AudioTrack-Java"; - + //-------------------------------------------------------------------------- // Member variables //-------------------- /** * Indicates the state of the AudioTrack instance */ - protected int mState = STATE_UNINITIALIZED; + private int mState = STATE_UNINITIALIZED; /** * Indicates the play state of the AudioTrack instance */ - protected int mPlayState = PLAYSTATE_STOPPED; + private int mPlayState = PLAYSTATE_STOPPED; /** * Lock to make sure mPlayState updates are reflecting the actual state of the object. */ - protected final Object mPlayStateLock = new Object(); + private final Object mPlayStateLock = new Object(); /** * The listener the AudioTrack notifies previously set marker is reached. * @see #setMarkerReachedListener(OnMarkerReachedListener) */ - protected OnMarkerReachedListener mMarkerListener = null; + private OnMarkerReachedListener mMarkerListener = null; /** * Lock to protect marker listener updates against event notifications */ - protected final Object mMarkerListenerLock = new Object(); + private final Object mMarkerListenerLock = new Object(); /** * The listener the AudioTrack notifies periodically during playback. * @see #setPeriodicNotificationListener(OnPeriodicNotificationListener) */ - protected OnPeriodicNotificationListener mPeriodicListener = null; + private OnPeriodicNotificationListener mPeriodicListener = null; /** * Lock to protect periodic listener updates against event notifications */ - protected final Object mPeriodicListenerLock = new Object(); + private final Object mPeriodicListenerLock = new Object(); /** * Size of the native audio buffer. */ - protected int mNativeBufferSizeInBytes = 0; + private int mNativeBufferSizeInBytes = 0; /** * Handler for events coming from the native code */ - protected NativeEventHandler mNativeEventHandler = null; + private NativeEventHandler mNativeEventHandler = null; /** * The audio data sampling rate in Hz. */ - protected int mSampleRate = 22050; + private int mSampleRate = 22050; /** * The number of input audio channels (1 is mono, 2 is stereo) */ - protected int mChannelCount = 1; + private int mChannelCount = 1; /** * The type of the audio stream to play. See - * {@link AudioManager.STREAM_VOICE_CALL}, {@link AudioManager.STREAM_SYSTEM}, - * {@link AudioManager.STREAM_RING}, {@link AudioManager.STREAM_MUSIC} and - * {@link AudioManager.STREAM_ALARM} + * {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM}, + * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and + * {@link AudioManager#STREAM_ALARM} */ - protected int mStreamType = AudioManager.STREAM_MUSIC; + private int mStreamType = AudioManager.STREAM_MUSIC; /** * The way audio is consumed by the hardware, streaming or static. */ - protected int mDataLoadMode = MODE_STREAM; + private int mDataLoadMode = MODE_STREAM; /** * The current audio channel configuration */ - protected int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; + private int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; /** * The encoding of the audio samples. - * @see #AudioFormat.ENCODING_PCM_8BIT - * @see #AudioFormat.ENCODING_PCM_16BIT + * @see AudioFormat#ENCODING_PCM_8BIT + * @see AudioFormat#ENCODING_PCM_16BIT */ - protected int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; + private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; //-------------------------------- // Used exclusively by native code //-------------------- - /** - * Accessed by native methods: provides access to C++ AudioTrack object + /** + * Accessed by native methods: provides access to C++ AudioTrack object */ @SuppressWarnings("unused") private int mNativeTrackInJavaObj; - /** + /** * Accessed by native methods: provides access to the JNI data (i.e. resources used by * the native AudioTrack object, but not stored in it). */ @SuppressWarnings("unused") private int mJniData; - - + + //-------------------------------------------------------------------------- // Constructor, Finalize //-------------------- /** * Class constructor. - * @param streamType the type of the audio stream. See - * {@link AudioSystem.STREAM_VOICE_CALL}, {@link AudioSystem.STREAM_SYSTEM}, - * {@link AudioSystem.STREAM_RING}, {@link AudioSystem.STREAM_MUSIC} and - * {@link AudioSystem.STREAM_ALARM} + * @param streamType the type of the audio stream. See + + * {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM}, + * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and + * {@link AudioManager#STREAM_ALARM} * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but * not limited to) 44100, 22050 and 11025. - * @param channelConfig describes the configuration of the audio channels. - * See {@link AudioFormat.CHANNEL_CONFIGURATION_MONO} and - * {@link AudioFormat.CHANNEL_CONFIGURATION_STEREO} - * @param audioFormat the format in which the audio data is represented. - * See {@link AudioFormat.ENCODING_PCM_16BIT} and - * {@link AudioFormat.ENCODING_PCM_8BIT} + * @param channelConfig describes the configuration of the audio channels. + + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and + * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO} + + * @param audioFormat the format in which the audio data is represented. + * See {@link AudioFormat#ENCODING_PCM_16BIT} and + * {@link AudioFormat#ENCODING_PCM_8BIT} * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read - * from for playback. If using the AudioTrack in streaming mode, you can write data into + * from for playback. If using the AudioTrack in streaming mode, you can write data into * this buffer in smaller chunks than this size. If using the AudioTrack in static mode, * this is the maximum size of the sound that will be played for this instance. * @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM} @@ -230,7 +254,7 @@ public class AudioTrack */ public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode) - throws IllegalArgumentException { + throws IllegalArgumentException { mState = STATE_UNINITIALIZED; audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode); @@ -238,8 +262,8 @@ public class AudioTrack audioBuffSizeCheck(bufferSizeInBytes); // native initialization - int initResult = native_setup(new WeakReference(this), - mStreamType, mSampleRate, mChannelCount, mAudioFormat, + int initResult = native_setup(new WeakReference(this), + mStreamType, mSampleRate, mChannelCount, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); @@ -252,8 +276,8 @@ public class AudioTrack mState = STATE_INITIALIZED; } } - - + + // Convenience method for the constructor's parameter checks. // This is where constructor IllegalArgumentException-s are thrown // postconditions: @@ -262,25 +286,27 @@ public class AudioTrack // mAudioFormat is valid // mSampleRate is valid // mDataLoadMode is valid - private void audioParamCheck(int streamType, int sampleRateInHz, + private void audioParamCheck(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int mode) { - + //-------------- // stream type if( (streamType != AudioManager.STREAM_ALARM) && (streamType != AudioManager.STREAM_MUSIC) && (streamType != AudioManager.STREAM_RING) && (streamType != AudioManager.STREAM_SYSTEM) - && (streamType != AudioManager.STREAM_VOICE_CALL) && (streamType != AudioManager.STREAM_NOTIFICATION) ) { + && (streamType != AudioManager.STREAM_VOICE_CALL) + && (streamType != AudioManager.STREAM_NOTIFICATION) + && (streamType != AudioManager.STREAM_BLUETOOTH_SCO)) { throw (new IllegalArgumentException("Invalid stream type.")); } else { mStreamType = streamType; } - + //-------------- // sample rate if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) { throw (new IllegalArgumentException(sampleRateInHz + "Hz is not a supported sample rate.")); - } else { + } else { mSampleRate = sampleRateInHz; } @@ -314,10 +340,10 @@ public class AudioTrack break; default: mAudioFormat = AudioFormat.ENCODING_INVALID; - throw(new IllegalArgumentException("Unsupported sample encoding." + throw(new IllegalArgumentException("Unsupported sample encoding." + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT.")); } - + //-------------- // audio load mode if ( (mode != MODE_STREAM) && (mode != MODE_STATIC) ) { @@ -326,8 +352,8 @@ public class AudioTrack mDataLoadMode = mode; } } - - + + // Convenience method for the contructor's audio buffer size check. // preconditions: // mChannelCount is valid @@ -335,18 +361,18 @@ public class AudioTrack // postcondition: // mNativeBufferSizeInBytes is valid (multiple of frame size, positive) private void audioBuffSizeCheck(int audioBufferSize) { - // NB: this section is only valid with PCM data. + // NB: this section is only valid with PCM data. // To update when supporting compressed formats - int frameSizeInBytes = mChannelCount + int frameSizeInBytes = mChannelCount * (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { throw (new IllegalArgumentException("Invalid audio buffer size.")); } - + mNativeBufferSizeInBytes = audioBufferSize; } - - + + // Convenience method for the creation of the native event handler // It is called only when a non-null event listener is set. // precondition: @@ -361,8 +387,8 @@ public class AudioTrack mNativeEventHandler = null; } } - - + + /** * Releases the native AudioTrack resources. */ @@ -377,7 +403,7 @@ public class AudioTrack @Override protected void finalize() { native_finalize(); - } + } //-------------------------------------------------------------------------- // Getters @@ -390,7 +416,7 @@ public class AudioTrack static public float getMinVolume() { return AudioTrack.VOLUME_MIN; } - + /** * Returns the maximum valid volume value. Volume values set above this one will * be clamped at this value. @@ -398,8 +424,8 @@ public class AudioTrack */ static public float getMaxVolume() { return AudioTrack.VOLUME_MAX; - } - + } + /** * Returns the configured audio data sample rate in Hz */ @@ -408,27 +434,28 @@ public class AudioTrack } /** - * Returns the configured audio data format. See {@link #AudioFormat.ENCODING_PCM_16BIT} - * and {@link #AudioFormat.ENCODING_PCM_8BIT}. + * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT} + * and {@link AudioFormat#ENCODING_PCM_8BIT}. */ public int getAudioFormat() { return mAudioFormat; } - + /** * Returns the type of audio stream this AudioTrack is configured for. - * Compare the result against {@link AudioManager.STREAM_VOICE_CALL}, - * {@link AudioManager.STREAM_SYSTEM}, {@link AudioManager.STREAM_RING}, - * {@link AudioManager.STREAM_MUSIC} or {@link AudioManager.STREAM_ALARM} + * Compare the result against {@link AudioManager#STREAM_VOICE_CALL}, + * {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING}, + * {@link AudioManager#STREAM_MUSIC} or {@link AudioManager#STREAM_ALARM} */ public int getStreamType() { return mStreamType; } /** - * Returns the configured channel configuration. - * See {@link #AudioFormat.CHANNEL_CONFIGURATION_MONO} - * and {@link #AudioFormat.CHANNEL_CONFIGURATION_STEREO}. + * Returns the configured channel configuration. + + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} + * and {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}. */ public int getChannelConfiguration() { return mChannelConfiguration; @@ -443,19 +470,19 @@ public class AudioTrack /** * Returns the state of the AudioTrack instance. This is useful after the - * AudioTrack instance has been created to check if it was initialized + * AudioTrack instance has been created to check if it was initialized * properly. This ensures that the appropriate hardware resources have been * acquired. */ public int getState() { return mState; } - + /** * Returns the playback state of the AudioTrack instance. - * @see AudioTrack.PLAYSTATE_STOPPED - * @see AudioTrack.PLAYSTATE_PAUSED - * @see AudioTrack.PLAYSTATE_PLAYING + * @see #PLAYSTATE_STOPPED + * @see #PLAYSTATE_PAUSED + * @see #PLAYSTATE_PLAYING */ public int getPlayState() { return mPlayState; @@ -495,11 +522,49 @@ public class AudioTrack static public int getNativeOutputSampleRate() { return native_get_output_sample_rate(); } - + /** + * {@hide} + * Returns the minimum buffer size required for the successful creation of an AudioTrack + * object to be created in the {@link #MODE_STREAM} mode. + * @param sampleRateInHz the sample rate expressed in Hertz. + * @param channelConfig describes the configuration of the audio channels. + * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and + * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO} + * @param audioFormat the format in which the audio data is represented. + * See {@link AudioFormat#ENCODING_PCM_16BIT} and + * {@link AudioFormat#ENCODING_PCM_8BIT} + * @return -1 if an invalid parameter was passed or if the implementation was unable to + * query the hardware for its output properties, or the minimum buffer size expressed + * in number of bytes. + */ + static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { + int channelCount = 0; + switch(channelConfig) { + case AudioFormat.CHANNEL_CONFIGURATION_MONO: + channelCount = 1; + break; + case AudioFormat.CHANNEL_CONFIGURATION_STEREO: + channelCount = 2; + break; + default: + loge("getMinBufferSize(): Invalid channel configuration."); + return -1; + } + + if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) + && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { + loge("getMinBufferSize(): Invalid audio format."); + return -1; + } + + return native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); + } + + //-------------------------------------------------------------------------- // Initialization / configuration - //-------------------- + //-------------------- /** * Sets the listener the AudioTrack notifies when a previously set marker is reached. * @param listener @@ -512,8 +577,8 @@ public class AudioTrack createNativeEventHandler(); } } - - + + /** * Sets the listener the AudioTrack notifies periodically during playback. * @param listener @@ -526,22 +591,20 @@ public class AudioTrack createNativeEventHandler(); } } - - - /** - * Sets the specified left/right output volume values on the AudioTrack. Values are clamped + + + /** + * Sets the specified left/right output volume values on the AudioTrack. Values are clamped * to the ({@link #getMinVolume()}, {@link #getMaxVolume()}) interval if outside this range. - * @param leftVolume output attenuation for the left channel. A value of 0.0f is silence, + * @param leftVolume output attenuation for the left channel. A value of 0.0f is silence, * a value of 1.0f is no attenuation. * @param rightVolume output attenuation for the right channel - * @return {@link #SUCCESS} - * @throws IllegalStateException + * @return error code or success, see {@link #SUCCESS}, + * {@link #ERROR_INVALID_OPERATION} */ - public int setStereoVolume(float leftVolume, float rightVolume) - throws IllegalStateException { + public int setStereoVolume(float leftVolume, float rightVolume) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("setStereoVolume() called on an "+ - "uninitialized AudioTrack.")); + return ERROR_INVALID_OPERATION; } // clamp the volumes @@ -559,11 +622,11 @@ public class AudioTrack } native_setVolume(leftVolume, rightVolume); - + return SUCCESS; } - - + + /** * Sets the playback sample rate for this track. This sets the sampling rate at which * the audio data will be consumed and played back, not the original sampling rate of the @@ -573,70 +636,88 @@ public class AudioTrack * sample rate (see {@link #getNativeOutputSampleRate()}). Use {@link #getSampleRate()} to * check the rate actually used in hardware after potential clamping. * @param sampleRateInHz - * @return {@link #SUCCESS} + * @return error code or success, see {@link #SUCCESS}, + * {@link #ERROR_INVALID_OPERATION} */ public int setPlaybackRate(int sampleRateInHz) { + if (mState != STATE_INITIALIZED) { + return ERROR_INVALID_OPERATION; + } native_set_playback_rate(sampleRateInHz); return SUCCESS; } - - + + /** - * + * * @param markerInFrames marker in frames * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, - * {@link #ERROR_INVALID_OPERATION} + * {@link #ERROR_INVALID_OPERATION} */ public int setNotificationMarkerPosition(int markerInFrames) { + if (mState != STATE_INITIALIZED) { + return ERROR_INVALID_OPERATION; + } return native_set_marker_pos(markerInFrames); } - - + + /** * @param periodInFrames update period in frames * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_INVALID_OPERATION} */ public int setPositionNotificationPeriod(int periodInFrames) { + if (mState != STATE_INITIALIZED) { + return ERROR_INVALID_OPERATION; + } return native_set_pos_update_period(periodInFrames); } - - + + /** * Sets the playback head position. The track must be stopped for the position to be changed. * @param positionInFrames playback head position in frames - * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE} - * @throws java.lang.IllegalStateException if the track is not in - * the {@link #PLAYSTATE_STOPPED} state. + * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, + * {@link #ERROR_INVALID_OPERATION} */ - public int setPlaybackHeadPosition(int positionInFrames) - throws IllegalStateException { + public int setPlaybackHeadPosition(int positionInFrames) { synchronized(mPlayStateLock) { if(mPlayState == PLAYSTATE_STOPPED) { return native_set_position(positionInFrames); + } else { + return ERROR_INVALID_OPERATION; } } - throw(new IllegalStateException("setPlaybackHeadPosition() called on a track that is "+ - "not in the PLAYSTATE_STOPPED play state.")); } - + /** * Sets the loop points and the loop count. The loop can be infinite. * @param startInFrames loop start marker in frames * @param endInFrames loop end marker in frames - * @param loopCount the number of times the loop is looped. + * @param loopCount the number of times the loop is looped. * A value of -1 means infinite looping. - * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE} + * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, + * {@link #ERROR_INVALID_OPERATION} */ public int setLoopPoints(int startInFrames, int endInFrames, int loopCount) { return native_set_loop(startInFrames, endInFrames, loopCount); } + /** + * Sets the initialization state of the instance. To be used in an AudioTrack subclass + * constructor to set a subclass-specific post-initialization state. + * @param state the state of the AudioTrack instance + */ + protected void setState(int state) { + mState = state; + } + //--------------------------------------------------------- // Transport control methods //-------------------- /** - * Starts playing an AudioTrack. + * Starts playing an AudioTrack. * @throws IllegalStateException */ public void play() @@ -644,7 +725,7 @@ public class AudioTrack if (mState != STATE_INITIALIZED) { throw(new IllegalStateException("play() called on uninitialized AudioTrack.")); } - + synchronized(mPlayStateLock) { native_start(); mPlayState = PLAYSTATE_PLAYING; @@ -671,7 +752,7 @@ public class AudioTrack /** * Pauses the playback of the audio data. * @throws IllegalStateException - */ + */ public void pause() throws IllegalStateException { if (mState != STATE_INITIALIZED) { @@ -685,25 +766,21 @@ public class AudioTrack mPlayState = PLAYSTATE_PAUSED; } } - - + + //--------------------------------------------------------- // Audio data supply //-------------------- /** * Flushes the audio data currently queued for playback. - * @throws IllegalStateException - */ - public void flush() - throws IllegalStateException { - if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("flush() called on uninitialized AudioTrack.")); - } - //logd("flush()"); + */ - // flush the data in native layer - native_flush(); + public void flush() { + if (mState == STATE_INITIALIZED) { + // flush the data in native layer + native_flush(); + } } @@ -712,12 +789,12 @@ public class AudioTrack * @param audioData the array that holds the data to play. * @param offsetInBytes the offset in audioData where the data to play starts. * @param sizeInBytes the number of bytes to read in audioData after the offset. - * @return the number of bytes that were written. - * @throws IllegalStateException - */ - public int write(byte[] audioData,int offsetInBytes, int sizeInBytes) - throws IllegalStateException { - if ((mDataLoadMode == MODE_STATIC) + * @return the number of bytes that were written or -1 if the object wasn't properly + * initialized. + */ + + public int write(byte[] audioData,int offsetInBytes, int sizeInBytes) { + if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) && (sizeInBytes > 0)) { mState = STATE_INITIALIZED; @@ -726,24 +803,24 @@ public class AudioTrack // or: how to update data for static tracks? if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("write() called on uninitialized AudioTrack.")); + return -1; } return native_write_byte(audioData, offsetInBytes, sizeInBytes); } - - + + /** * Writes the audio data to the audio hardware for playback. * @param audioData the array that holds the data to play. * @param offsetInShorts the offset in audioData where the data to play starts. * @param sizeInShorts the number of bytes to read in audioData after the offset. - * @return the number of shorts that were written. - * @throws IllegalStateException - */ - public int write(short[] audioData, int offsetInShorts, int sizeInShorts) - throws IllegalStateException { - if ((mDataLoadMode == MODE_STATIC) + * @return the number of shorts that were written or -1 if the object wasn't properly + * initialized. + */ + + public int write(short[] audioData, int offsetInShorts, int sizeInShorts) { + if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) && (sizeInShorts > 0)) { mState = STATE_INITIALIZED; @@ -752,13 +829,13 @@ public class AudioTrack // or: how to update data for static tracks? if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException("write() called on uninitialized AudioTrack.")); + return -1; } return native_write_short(audioData, offsetInShorts, sizeInShorts); } - - + + /** * Notifies the native resource to reuse the audio data already loaded in the native * layer. This call is only valid with AudioTrack instances that don't use the streaming @@ -791,7 +868,7 @@ public class AudioTrack /** - * Interface definition for a callback to be invoked for each periodic AudioTrack + * Interface definition for a callback to be invoked for each periodic AudioTrack * update during playback. The update interval is set by setPositionNotificationPeriod(). */ public interface OnPeriodicNotificationListener { @@ -858,28 +935,28 @@ public class AudioTrack if (track == null) { return; } - + if (track.mNativeEventHandler != null) { Message m = track.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); track.mNativeEventHandler.sendMessage(m); } } - - + + //--------------------------------------------------------- // Native methods called from the Java side //-------------------- - private native final int native_setup(Object audiotrack_this, - int streamType, int sampleRate, int nbChannels, int audioFormat, + private native final int native_setup(Object audiotrack_this, + int streamType, int sampleRate, int nbChannels, int audioFormat, int buffSizeInBytes, int mode); private native final void native_finalize(); - + private native final void native_release(); - private native final void native_start(); + private native final void native_start(); private native final void native_stop(); @@ -887,34 +964,36 @@ public class AudioTrack private native final void native_flush(); - private native final int native_write_byte(byte[] audioData, + private native final int native_write_byte(byte[] audioData, int offsetInBytes, int sizeInBytes); - - private native final int native_write_short(short[] audioData, + + private native final int native_write_short(short[] audioData, int offsetInShorts, int sizeInShorts); - + private native final int native_reload_static(); private native final int native_get_native_frame_count(); private native final void native_setVolume(float leftVolume, float rightVolume); - + private native final void native_set_playback_rate(int sampleRateInHz); private native final int native_get_playback_rate(); - + private native final int native_set_marker_pos(int marker); private native final int native_get_marker_pos(); - + private native final int native_set_pos_update_period(int updatePeriod); private native final int native_get_pos_update_period(); - + private native final int native_set_position(int position); private native final int native_get_position(); - + private native final int native_set_loop(int start, int end, int loopCount); - + static private native final int native_get_output_sample_rate(); - + static private native final int native_get_min_buff_size( + int sampleRateInHz, int channelConfig, int audioFormat); + //--------------------------------------------------------- // Utility methods @@ -928,7 +1007,4 @@ public class AudioTrack Log.e(TAG, "[ android.media.AudioTrack ] " + msg); } -} - - - +} \ No newline at end of file diff --git a/media/java/android/media/JetPlayer.java b/media/java/android/media/JetPlayer.java index b9268d5970a05..bfa2f801733f4 100644 --- a/media/java/android/media/JetPlayer.java +++ b/media/java/android/media/JetPlayer.java @@ -17,9 +17,11 @@ package android.media; +import java.io.FileDescriptor; import java.lang.ref.WeakReference; import java.lang.CloneNotSupportedException; +import android.content.res.AssetFileDescriptor; import android.os.Looper; import android.os.Handler; import android.os.Message; @@ -29,9 +31,7 @@ import android.util.Log; * JetPlayer provides access to JET content playback and control. *

      * Use JetPlayer.getJetPlayer() to get an instance of this class. - * There can only be one instance of this class at any one time. * - * @hide */ public class JetPlayer { @@ -39,15 +39,29 @@ public class JetPlayer // Constants //------------------------ /** - * The maximum number of simultaneous tracks. Use {@link #getMaxTracks()} to + * The maximum number of simultaneous tracks. Use __link #getMaxTracks()} to * access this value. */ - protected static int MAXTRACKS = 32; + private static int MAXTRACKS = 32; - // These constants are to be kept in sync with the ones in include/media/JetPlayer.h - protected static final int JET_USERID_UPDATE = 1; - protected static final int JET_NUMQUEUEDSEGMENT_UPDATE = 2; - protected static final int JET_PAUSE_UPDATE = 3; + // to keep in sync with the JetPlayer class constants + // defined in frameworks/base/include/media/JetPlayer.h + private static final int JET_EVENT = 1; + private static final int JET_USERID_UPDATE = 2; + private static final int JET_NUMQUEUEDSEGMENT_UPDATE = 3; + private static final int JET_PAUSE_UPDATE = 4; + + // to keep in sync with external/sonivox/arm-wt-22k/lib_src/jet_data.h + // Encoding of event information on 32 bits + private static final int JET_EVENT_VAL_MASK = 0x0000007f; // mask for value + private static final int JET_EVENT_CTRL_MASK = 0x00003f80; // mask for controller + private static final int JET_EVENT_CHAN_MASK = 0x0003c000; // mask for channel + private static final int JET_EVENT_TRACK_MASK = 0x00fc0000; // mask for track number + private static final int JET_EVENT_SEG_MASK = 0xff000000; // mask for segment ID + private static final int JET_EVENT_CTRL_SHIFT = 7; // shift to get controller number to bit 0 + private static final int JET_EVENT_CHAN_SHIFT = 14; // shift to get MIDI channel to bit 0 + private static final int JET_EVENT_TRACK_SHIFT = 18; // shift to get track ID to bit 0 + private static final int JET_EVENT_SEG_SHIFT = 24; // shift to get segment ID to bit 0 //-------------------------------------------- @@ -56,13 +70,20 @@ public class JetPlayer private EventHandler mNativeEventHandler = null; /** - * Lock to protect event listener updates against event notifications + * Lock to protect status listener updates against status change notifications */ - protected final Object mStatusListenerLock = new Object(); + private final Object mStatusListenerLock = new Object(); - protected JetStatusUpdateListener mJetStatusUpdateListener = null; + /** + * Lock to protect the event listener updates against event notifications + */ + private final Object mEventListenerLock = new Object(); - protected static JetPlayer singletonRef; + private JetStatusUpdateListener mJetStatusUpdateListener = null; + + private JetEventListener mJetEventListener = null; + + private static JetPlayer singletonRef; //-------------------------------- @@ -136,8 +157,14 @@ public class JetPlayer //-------------------------------------------- // Jet functionality //------------------------ - public boolean openJetFile(String path) { - return native_openJetFile(path); + public boolean loadJetFile(String path) { + return native_loadJetFromFile(path); + } + + + public boolean loadJetFile(AssetFileDescriptor afd) { + return native_loadJetFromFileD( + afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); } @@ -195,6 +222,10 @@ public class JetPlayer } + public boolean clearQueue() { + return native_clearQueue(); + } + //--------------------------------------------------------- // Internal class to handle events posted from native code @@ -211,28 +242,42 @@ public class JetPlayer @Override public void handleMessage(Message msg) { switch(msg.what) { + case JET_EVENT: + synchronized (mEventListenerLock) { + if (mJetEventListener != null) { + // call the appropriate listener after decoding the event parameters + // encoded in msg.arg1 + mJetEventListener.onJetEvent( + mJet, + (short)((msg.arg1 & JET_EVENT_SEG_MASK) >> JET_EVENT_SEG_SHIFT), + (byte) ((msg.arg1 & JET_EVENT_TRACK_MASK) >> JET_EVENT_TRACK_SHIFT), + (byte) ((msg.arg1 & JET_EVENT_CHAN_MASK) >> JET_EVENT_CHAN_SHIFT), + (byte) ((msg.arg1 & JET_EVENT_CTRL_MASK) >> JET_EVENT_CTRL_SHIFT), + (byte) (msg.arg1 & JET_EVENT_VAL_MASK) ); + } + } + return; case JET_USERID_UPDATE: synchronized (mStatusListenerLock) { if (mJetStatusUpdateListener != null) { - mJetStatusUpdateListener.onJetUserIdUpdate(msg.arg1, msg.arg2); + mJetStatusUpdateListener.onJetUserIdUpdate(mJet, msg.arg1, msg.arg2); } } return; case JET_NUMQUEUEDSEGMENT_UPDATE: synchronized (mStatusListenerLock) { if (mJetStatusUpdateListener != null) { - mJetStatusUpdateListener.onJetNumQueuedSegmentUpdate(msg.arg1); + mJetStatusUpdateListener.onJetNumQueuedSegmentUpdate(mJet, msg.arg1); } } return; case JET_PAUSE_UPDATE: synchronized (mStatusListenerLock) { if (mJetStatusUpdateListener != null) - mJetStatusUpdateListener.onJetPauseUpdate(msg.arg1); + mJetStatusUpdateListener.onJetPauseUpdate(mJet, msg.arg1); } return; - default: loge("Unknown message type " + msg.what); return; @@ -242,7 +287,7 @@ public class JetPlayer //-------------------------------------------- - // Jet event listener + // Jet status update listener //------------------------ public void setStatusUpdateListener(JetStatusUpdateListener listener) { synchronized(mStatusListenerLock) { @@ -255,31 +300,66 @@ public class JetPlayer } /** - * Handles the notification when the JET segment userID is updated. + * Handles the notification when the JET status is updated. */ public interface JetStatusUpdateListener { /** * Callback for when JET's currently playing segment userID is updated. * + * @param player the JET player the status update is coming from * @param userId the ID of the currently playing segment * @param repeatCount the repetition count for the segment (0 means it plays once) */ - void onJetUserIdUpdate(int userId, int repeatCount); + void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount); /** * Callback for when JET's number of queued segments is updated. * + * @param player the JET player the status update is coming from * @param nbSegments the number of segments in the JET queue */ - void onJetNumQueuedSegmentUpdate(int nbSegments); + void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments); /** * Callback for when JET pause state is updated. * + * @param player the JET player the status update is coming from * @param paused indicates whether JET is paused or not */ - void onJetPauseUpdate(int paused); - }; + void onJetPauseUpdate(JetPlayer player, int paused); + } + + + //-------------------------------------------- + // Jet event listener + //------------------------ + public void setEventListener(JetEventListener listener) { + synchronized(mEventListenerLock) { + mJetEventListener = listener; + } + + if ((listener != null) && (mNativeEventHandler == null)) { + createNativeEventHandler(); + } + } + + /** + * Handles the notification when the JET engine generates an event. + */ + public interface JetEventListener { + /** + * Callback for when the JET engine generates a new event. + * + * @param player the JET player the event is coming from + * @param segment 8 bit unsigned value + * @param track 6 bit unsigned value + * @param channel 4 bit unsigned value + * @param controller 7 bit unsigned value + * @param value 7 bit unsigned value + */ + void onJetEvent(JetPlayer player, + short segment, byte track, byte channel, byte controller, byte value); + } //-------------------------------------------- @@ -289,7 +369,8 @@ public class JetPlayer int maxTracks, int trackBufferSize); private native final void native_finalize(); private native final void native_release(); - private native final boolean native_openJetFile(String pathToJetFile); + private native final boolean native_loadJetFromFile(String pathToJetFile); + private native final boolean native_loadJetFromFileD(FileDescriptor fd, long offset, long len); private native final boolean native_closeJetFile(); private native final boolean native_playJet(); private native final boolean native_pauseJet(); @@ -300,7 +381,8 @@ public class JetPlayer private native final boolean native_setMuteFlags(int muteFlags, boolean sync); private native final boolean native_setMuteArray(boolean[]muteArray, boolean sync); private native final boolean native_setMuteFlag(int trackId, boolean muteFlag, boolean sync); - private native final boolean native_triggerClip(int clipId); + private native final boolean native_triggerClip(int clipId); + private native final boolean native_clearQueue(); //--------------------------------------------------------- // Called exclusively by native code diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 22361aade44ab..f05842d6ce02b 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -102,13 +102,17 @@ public class MediaFile { addFileType("AMR", FILE_TYPE_AMR, "audio/amr"); addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb"); addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma"); - addFileType("OGG", FILE_TYPE_OGG, "application/ogg"); + addFileType("OGG", FILE_TYPE_OGG, "application/ogg"); + addFileType("OGA", FILE_TYPE_OGG, "application/ogg"); addFileType("MID", FILE_TYPE_MID, "audio/midi"); + addFileType("MIDI", FILE_TYPE_MID, "audio/midi"); addFileType("XMF", FILE_TYPE_MID, "audio/midi"); addFileType("RTTTL", FILE_TYPE_MID, "audio/midi"); addFileType("SMF", FILE_TYPE_SMF, "audio/sp-midi"); addFileType("IMY", FILE_TYPE_IMY, "audio/imelody"); + addFileType("RTX", FILE_TYPE_MID, "audio/midi"); + addFileType("OTA", FILE_TYPE_MID, "audio/midi"); addFileType("MP4", FILE_TYPE_MP4, "video/mp4"); addFileType("M4V", FILE_TYPE_M4V, "video/mp4"); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 1a82654ca2c33..601557df897f7 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -39,11 +39,11 @@ import java.lang.ref.WeakReference; /** * MediaPlayer class can be used to control playback * of audio/video files and streams. An example on how to use the methods in - * this class can be found in VideoView. - * Please see Android Media APIs + * this class can be found in {@link android.widget.VideoView}. + * Please see Audio and Video * for additional help using MediaPlayer. * - *

      Topics covered are: + *

      Topics covered here are: *

        *
      1. State Diagram *
      2. Valid and Invalid States diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 651cc41439a50..4a301149da206 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -24,7 +24,7 @@ import java.io.IOException; * Used to record audio and video. The recording control is based on a * simple state machine (see below). * - *

        + *

        *

        * *

        A common case of using MediaRecorder to record audio works as follows: @@ -42,8 +42,8 @@ import java.io.IOException; * recorder.release(); // Now the object cannot be reused *

    * - *

    See the Android Media APIs - * page for additional help with using MediaRecorder. + *

    See the Audio and Video + * documentation for additional help with using MediaRecorder. */ public class MediaRecorder { diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 38203b6b8c88d..fc8476d4ae83f 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -118,6 +118,7 @@ public class MediaScanner private static final String NOTIFICATIONS_DIR = "/notifications/"; private static final String ALARMS_DIR = "/alarms/"; private static final String MUSIC_DIR = "/music/"; + private static final String PODCAST_DIR = "/podcasts/"; private static final String[] ID3_GENRES = { // ID3v1 Genres @@ -455,8 +456,9 @@ public class MediaScanner boolean ringtones = (path.indexOf(RINGTONES_DIR) > 0); boolean notifications = (path.indexOf(NOTIFICATIONS_DIR) > 0); boolean alarms = (path.indexOf(ALARMS_DIR) > 0); + boolean podcasts = (path.indexOf(PODCAST_DIR) > 0); boolean music = (path.indexOf(MUSIC_DIR) > 0) || - (!ringtones && !notifications && !alarms); + (!ringtones && !notifications && !alarms && !podcasts); if (mFileType == MediaFile.FILE_TYPE_MP3 || mFileType == MediaFile.FILE_TYPE_MP4 || @@ -473,7 +475,7 @@ public class MediaScanner // we used to compute the width and height but it's not worth it } - result = endFile(entry, ringtones, notifications, alarms, music); + result = endFile(entry, ringtones, notifications, alarms, music, podcasts); } } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); @@ -586,7 +588,8 @@ public class MediaScanner return map; } - public Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications, boolean alarms, boolean music) + private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications, + boolean alarms, boolean music, boolean podcasts) throws RemoteException { // update database Uri tableUri; @@ -634,6 +637,7 @@ public class MediaScanner values.put(Audio.Media.IS_NOTIFICATION, notifications); values.put(Audio.Media.IS_ALARM, alarms); values.put(Audio.Media.IS_MUSIC, music); + values.put(Audio.Media.IS_PODCAST, podcasts); } else if (isImage) { // nothing right now } diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 2810a9cc9ad55..8eb638ec19201 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -60,15 +60,18 @@ static sp get_surface(JNIEnv* env, jobject clazz) return sp(p); } -static void process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message) +// Returns true if it throws an exception. +static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message) { LOGV("process_media_recorder_call"); if (opStatus == (status_t)INVALID_OPERATION) { jniThrowException(env, "java/lang/IllegalStateException", NULL); + return true; } else if (opStatus != (status_t)OK) { jniThrowException(env, exception, message); + return true; } - return; + return false; } static void android_media_MediaRecorder_setCamera(JNIEnv* env, jobject thiz, jobject camera) @@ -196,7 +199,9 @@ android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz) if (surface != NULL) { const sp& native_surface = get_surface(env, surface); LOGI("prepare: surface=%p (id=%d)", native_surface.get(), native_surface->ID()); - process_media_recorder_call(env, mr->setPreviewSurface(native_surface), "java/lang/RuntimeException", "setPreviewSurface failed."); + if (process_media_recorder_call(env, mr->setPreviewSurface(native_surface), "java/lang/RuntimeException", "setPreviewSurface failed.")) { + return; + } } process_media_recorder_call(env, mr->prepare(), "java/io/IOException", "prepare failed."); } diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 7872a8df7d5c5..559f9d5ba3f02 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -64,13 +64,6 @@ SoundPool::SoundPool(jobject soundPoolRef, int maxChannels, int streamType, int mChannels.push_back(&mChannelPool[i]); } - if (AudioSystem::getOutputFrameCount(&mFrameCount) != NO_ERROR) { - mFrameCount = kDefaultFrameCount; - } - if (AudioSystem::getOutputSamplingRate(&mSampleRate) != NO_ERROR) { - mSampleRate = kDefaultSampleRate; - } - // start decode thread startThreads(); } @@ -481,8 +474,8 @@ void SoundChannel::play(const sp& sample, int nextChannelID, float leftV { AudioTrack* oldTrack; - LOGV("play: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f", - sample->sampleID(), nextChannelID, leftVolume, rightVolume, priority, loop, rate); + LOGV("play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f", + this, sample->sampleID(), nextChannelID, leftVolume, rightVolume, priority, loop, rate); // if not idle, this voice is being stolen if (mState != IDLE) { @@ -496,9 +489,17 @@ void SoundChannel::play(const sp& sample, int nextChannelID, float leftV } // initialize track + int afFrameCount; + int afSampleRate; + if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + afFrameCount = kDefaultFrameCount; + } + if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + afSampleRate = kDefaultSampleRate; + } int numChannels = sample->numChannels(); uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5); - uint32_t bufferFrames = (mSoundPool->mFrameCount * sampleRate) / mSoundPool->mSampleRate; + uint32_t bufferFrames = (afFrameCount * sampleRate) / afSampleRate; uint32_t frameCount = 0; if (loop) { @@ -511,12 +512,21 @@ void SoundChannel::play(const sp& sample, int nextChannelID, float leftV } AudioTrack* newTrack; + + // mToggle toggles each time a track is started on a given channel. + // The toggle is concatenated with the SoundChannel address and passed to AudioTrack + // as callback user data. This enables the detection of callbacks received from the old + // audio track while the new one is being started and avoids processing them with + // wrong audio audio buffer size (mAudioBufferSize) + unsigned long toggle = mToggle ^ 1; + void *userData = (void *)((unsigned long)this | toggle); + #ifdef USE_SHARED_MEM_BUFFER newTrack = new AudioTrack(mSoundPool->streamType(), sampleRate, sample->format(), - numChannels, sample->getIMemory(), 0, callback, this); + numChannels, sample->getIMemory(), 0, callback, userData); #else newTrack = new AudioTrack(mSoundPool->streamType(), sampleRate, sample->format(), - numChannels, frameCount, 0, callback, this, bufferFrames); + numChannels, frameCount, 0, callback, userData, bufferFrames); #endif if (newTrack->initCheck() != NO_ERROR) { LOGE("Error creating AudioTrack"); @@ -529,6 +539,8 @@ void SoundChannel::play(const sp& sample, int nextChannelID, float leftV { Mutex::Autolock lock(&mLock); + // From now on, AudioTrack callbacks recevieved with previous toggle value will be ignored. + mToggle = toggle; oldTrack = mAudioTrack; mAudioTrack = newTrack; mPos = 0; @@ -583,7 +595,13 @@ void SoundChannel::nextEvent() void SoundChannel::callback(int event, void* user, void *info) { - SoundChannel* channel = static_cast(user); + unsigned long toggle = (unsigned long)user & 1; + SoundChannel* channel = static_cast((void *)((unsigned long)user & ~1)); + + if (channel->mToggle != toggle) { + LOGV("callback with wrong toggle"); + return; + } channel->process(event, info); } @@ -592,7 +610,7 @@ void SoundChannel::process(int event, void *info) //LOGV("process(%d)", mChannelID); sp sample = mSample; - LOGV("SoundChannel::process event %d", event); +// LOGV("SoundChannel::process event %d", event); if (event == AudioTrack::EVENT_MORE_DATA) { AudioTrack::Buffer* b = static_cast(info); diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h index d02ae8b5a4e75..78027812fb956 100644 --- a/media/jni/soundpool/SoundPool.h +++ b/media/jni/soundpool/SoundPool.h @@ -118,7 +118,7 @@ protected: class SoundChannel : public SoundEvent { public: enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING }; - SoundChannel() : mAudioTrack(0), mState(IDLE), mNumChannels(1), mPos(0) {} + SoundChannel() : mAudioTrack(0), mState(IDLE), mNumChannels(1), mPos(0), mToggle(0) {} ~SoundChannel(); void init(SoundPool* soundPool); void play(const sp& sample, int channelID, float leftVolume, float rightVolume, @@ -151,6 +151,7 @@ private: int mNumChannels; int mPos; int mAudioBufferSize; + unsigned long mToggle; }; // application object for managing a pool of sounds @@ -215,8 +216,6 @@ private: int mAllocated; int mNextSampleID; int mNextChannelID; - int mFrameCount; - int mSampleRate; bool mQuit; }; diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 2a697b95232f1..8020da2b61005 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ AudioTrack.cpp \ IAudioFlinger.cpp \ + IAudioFlingerClient.cpp \ IAudioTrack.cpp \ IAudioRecord.cpp \ AudioRecord.cpp \ diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 3d39181922f1f..a987b9228d086 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -128,8 +128,22 @@ status_t AudioRecord::set( return BAD_VALUE; } - // TODO: Get input frame count from hardware. - int minFrameCount = 1024*2; + size_t inputBuffSizeInBytes = -1; + if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &inputBuffSizeInBytes) + != NO_ERROR) { + LOGE("AudioSystem could not query the input buffer size."); + return NO_INIT; + } + if (inputBuffSizeInBytes == 0) { + LOGE("Recording parameters are not supported: sampleRate %d, channelCount %d, format %d", + sampleRate, channelCount, format); + return BAD_VALUE; + } + int frameSizeInBytes = channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1); + + // We use 2* size of input buffer for ping pong use of record buffer. + int minFrameCount = 2 * inputBuffSizeInBytes / frameSizeInBytes; + LOGV("AudioRecord::set() minFrameCount = %d", minFrameCount); if (frameCount == 0) { frameCount = minFrameCount; diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index a375b55a63690..cf911058e411f 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -15,6 +15,8 @@ */ #define LOG_TAG "AudioSystem" +//#define LOG_NDEBUG 0 + #include #include #include @@ -26,12 +28,17 @@ namespace android { // client singleton for AudioFlinger binder interface Mutex AudioSystem::gLock; sp AudioSystem::gAudioFlinger; -sp AudioSystem::gDeathNotifier; +sp AudioSystem::gAudioFlingerClient; audio_error_callback AudioSystem::gAudioErrorCallback = NULL; // Cached values int AudioSystem::gOutSamplingRate = 0; int AudioSystem::gOutFrameCount = 0; uint32_t AudioSystem::gOutLatency = 0; +// Cached values for recording queries +uint32_t AudioSystem::gPrevInSamplingRate = 16000; +int AudioSystem::gPrevInFormat = AudioSystem::PCM_16_BIT; +int AudioSystem::gPrevInChannelCount = 1; +size_t AudioSystem::gInBuffSize = 0; // establish binder interface to AudioFlinger service @@ -48,15 +55,16 @@ const sp& AudioSystem::get_audio_flinger() LOGW("AudioFlinger not published, waiting..."); usleep(500000); // 0.5 s } while(true); - if (gDeathNotifier == NULL) { - gDeathNotifier = new DeathNotifier(); + if (gAudioFlingerClient == NULL) { + gAudioFlingerClient = new AudioFlingerClient(); } else { if (gAudioErrorCallback) { gAudioErrorCallback(NO_ERROR); } } - binder->linkToDeath(gDeathNotifier); + binder->linkToDeath(gAudioFlingerClient); gAudioFlinger = interface_cast(binder); + gAudioFlinger->registerClient(gAudioFlingerClient); // Cache frequently accessed parameters gOutFrameCount = (int)gAudioFlinger->frameCount(); gOutSamplingRate = (int)gAudioFlinger->sampleRate(); @@ -250,7 +258,7 @@ status_t AudioSystem::getOutputSamplingRate(int* samplingRate) const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; // gOutSamplingRate is updated by get_audio_flinger() - } + } *samplingRate = gOutSamplingRate; return NO_ERROR; @@ -261,7 +269,7 @@ status_t AudioSystem::getOutputFrameCount(int* frameCount) if (gOutFrameCount == 0) { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; - // gOutSamplingRate is updated by get_audio_flinger() + // gOutFrameCount is updated by get_audio_flinger() } *frameCount = gOutFrameCount; return NO_ERROR; @@ -279,14 +287,38 @@ status_t AudioSystem::getOutputLatency(uint32_t* latency) return NO_ERROR; } +status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, int format, int channelCount, + size_t* buffSize) +{ + // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values + if ((gInBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat) + || (channelCount != gPrevInChannelCount)) { + // save the request params + gPrevInSamplingRate = sampleRate; + gPrevInFormat = format; + gPrevInChannelCount = channelCount; + + gInBuffSize = 0; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + gInBuffSize = af->getInputBufferSize(sampleRate, format, channelCount); + } + *buffSize = gInBuffSize; + + return NO_ERROR; +} + // --------------------------------------------------------------------------- -void AudioSystem::DeathNotifier::binderDied(const wp& who) { +void AudioSystem::AudioFlingerClient::binderDied(const wp& who) { Mutex::Autolock _l(AudioSystem::gLock); AudioSystem::gAudioFlinger.clear(); AudioSystem::gOutSamplingRate = 0; AudioSystem::gOutFrameCount = 0; AudioSystem::gOutLatency = 0; + AudioSystem::gInBuffSize = 0; if (gAudioErrorCallback) { gAudioErrorCallback(DEAD_OBJECT); @@ -294,6 +326,15 @@ void AudioSystem::DeathNotifier::binderDied(const wp& who) { LOGW("AudioFlinger server died!"); } +void AudioSystem::AudioFlingerClient::audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency) { + + AudioSystem::gOutFrameCount = frameCount; + AudioSystem::gOutSamplingRate = samplingRate; + AudioSystem::gOutLatency = latency; + + LOGV("AudioFlinger output changed!"); +} + void AudioSystem::setErrorCallback(audio_error_callback cb) { Mutex::Autolock _l(AudioSystem::gLock); gAudioErrorCallback = cb; diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index f9f8568c969a5..63b2012fed31d 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -603,13 +603,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) if (__builtin_expect(result!=NO_ERROR, false)) { cblk->waitTimeMs += WAIT_PERIOD_MS; if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { - LOGW( "obtainBuffer timed out (is the CPU pegged?) " - "user=%08x, server=%08x", cblk->user, cblk->server); - mAudioTrack->start(); // FIXME: Wake up audioflinger - timeout = 1; + // timing out when a loop has been set and we have already written upto loop end + // is a normal condition: no need to wake AudioFlinger up. + if (cblk->user < cblk->loopEnd) { + LOGW( "obtainBuffer timed out (is the CPU pegged?) " + "user=%08x, server=%08x", cblk->user, cblk->server); + mAudioFlinger->wakeUp(); + timeout = 1; + } cblk->waitTimeMs = 0; } - ; + if (--waitCount == 0) { return TIMED_OUT; } diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 018ea6c3469b1..4215820b0e0d0 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -51,6 +51,9 @@ enum { GET_MIC_MUTE, IS_MUSIC_ACTIVE, SET_PARAMETER, + REGISTER_CLIENT, + GET_INPUTBUFFERSIZE, + WAKE_UP }; class BpAudioFlinger : public BpInterface @@ -303,6 +306,33 @@ public: remote()->transact(SET_PARAMETER, data, &reply); return reply.readInt32(); } + + virtual void registerClient(const sp& client) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeStrongBinder(client->asBinder()); + remote()->transact(REGISTER_CLIENT, data, &reply); + } + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(sampleRate); + data.writeInt32(format); + data.writeInt32(channelCount); + remote()->transact(GET_INPUTBUFFERSIZE, data, &reply); + return reply.readInt32(); + } + + virtual void wakeUp() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(WAKE_UP, data, &reply); + return; + } }; IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger"); @@ -470,6 +500,26 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32( setParameter(key, value) ); return NO_ERROR; } break; + case REGISTER_CLIENT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + sp client = interface_cast(data.readStrongBinder()); + registerClient(client); + return NO_ERROR; + } break; + case GET_INPUTBUFFERSIZE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + uint32_t sampleRate = data.readInt32(); + int format = data.readInt32(); + int channelCount = data.readInt32(); + reply->writeInt32( getInputBufferSize(sampleRate, format, channelCount) ); + return NO_ERROR; + } break; + case WAKE_UP: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + wakeUp(); + return NO_ERROR; + } break; + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp new file mode 100644 index 0000000000000..d9562665d00aa --- /dev/null +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009 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. + */ + +#define LOG_TAG "IAudioFlingerClient" +#include + +#include +#include + +#include + +#include + +namespace android { + +enum { + AUDIO_OUTPUT_CHANGED = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpAudioFlingerClient : public BpInterface +{ +public: + BpAudioFlingerClient(const sp& impl) + : BpInterface(impl) + { + } + + void audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor()); + data.writeInt32(frameCount); + data.writeInt32(samplingRate); + data.writeInt32(latency); + remote()->transact(AUDIO_OUTPUT_CHANGED, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(AudioFlingerClient, "android.media.IAudioFlingerClient"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnAudioFlingerClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case AUDIO_OUTPUT_CHANGED: { + CHECK_INTERFACE(IAudioFlingerClient, data, reply); + uint32_t frameCount = data.readInt32(); + uint32_t samplingRate = data.readInt32(); + uint32_t latency = data.readInt32(); + audioOutputChanged(frameCount, samplingRate, latency); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index f0edf887d6433..ead24d497f23a 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -214,12 +214,15 @@ int JetPlayer::render() { } p += count * pLibConfig->numChannels; num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM); + + // send events that were generated (if any) to the event callback + fireEventsFromJetQueue(); } // update playback state //LOGV("JetPlayer::render(): updating state"); JET_Status(mEasData, &mJetStatus); - fireEventOnStatusChange(); + fireUpdateOnStatusChange(); mPaused = mJetStatus.paused; mMutex.unlock(); // UNLOCK ]]]]]]]] ----------------------------------- @@ -261,9 +264,9 @@ threadExit: //------------------------------------------------------------------------------------------------- -// fire up an event if any of the status fields has changed +// fire up an update if any of the status fields has changed // precondition: mMutex locked -void JetPlayer::fireEventOnStatusChange() +void JetPlayer::fireUpdateOnStatusChange() { if( (mJetStatus.currentUserID != mPreviousJetStatus.currentUserID) ||(mJetStatus.segmentRepeatCount != mPreviousJetStatus.segmentRepeatCount) ) { @@ -303,9 +306,31 @@ void JetPlayer::fireEventOnStatusChange() //------------------------------------------------------------------------------------------------- -int JetPlayer::openFile(const char* path) +// fire up all the JET events in the JET engine queue (until the queue is empty) +// precondition: mMutex locked +void JetPlayer::fireEventsFromJetQueue() { - LOGV("JetPlayer::openFile(): path=%s", path); + if(!mEventCallback) { + // no callback, just empty the event queue + while (JET_GetEvent(mEasData, NULL, NULL)) { } + return; + } + + EAS_U32 rawEvent; + while (JET_GetEvent(mEasData, &rawEvent, NULL)) { + mEventCallback( + JetPlayer::JET_EVENT, + rawEvent, + -1, + mJavaJetPlayerRef); + } +} + + +//------------------------------------------------------------------------------------------------- +int JetPlayer::loadFromFile(const char* path) +{ + LOGV("JetPlayer::loadFromFile(): path=%s", path); Mutex::Autolock lock(mMutex); @@ -326,6 +351,29 @@ int JetPlayer::openFile(const char* path) return( result ); } + +//------------------------------------------------------------------------------------------------- +int JetPlayer::loadFromFD(const int fd, const long long offset, const long long length) +{ + LOGV("JetPlayer::loadFromFD(): fd=%d offset=%lld length=%lld", fd, offset, length); + + Mutex::Autolock lock(mMutex); + + mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE)); + mEasJetFileLoc->fd = fd; + mEasJetFileLoc->offset = offset; + mEasJetFileLoc->length = length; + mEasJetFileLoc->path = NULL; + + EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc); + if(result != EAS_SUCCESS) + mState = EAS_STATE_ERROR; + else + mState = EAS_STATE_OPEN; + return( result ); +} + + //------------------------------------------------------------------------------------------------- int JetPlayer::closeFile() { @@ -348,7 +396,7 @@ int JetPlayer::play() JET_Status(mEasData, &mJetStatus); this->dumpJetStatus(&mJetStatus); - fireEventOnStatusChange(); + fireUpdateOnStatusChange(); // wake up render thread LOGV("JetPlayer::play(): wakeup render thread"); @@ -368,7 +416,7 @@ int JetPlayer::pause() JET_Status(mEasData, &mJetStatus); this->dumpJetStatus(&mJetStatus); - fireEventOnStatusChange(); + fireUpdateOnStatusChange(); return result; @@ -407,6 +455,14 @@ int JetPlayer::triggerClip(int clipId) return JET_TriggerClip(mEasData, clipId); } +//------------------------------------------------------------------------------------------------- +int JetPlayer::clearQueue() +{ + LOGV("JetPlayer::clearQueue"); + Mutex::Autolock lock(mMutex); + return JET_Clear_Queue(mEasData); +} + //------------------------------------------------------------------------------------------------- void JetPlayer::dump() { diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 584d1350545e4..fa36460a77a01 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -97,10 +97,6 @@ ToneGenerator::ToneGenerator(int streamType, float volume) { LOGE("Unable to marshal AudioFlinger"); return; } - if (AudioSystem::getOutputFrameCount(&mBufferSize) != NO_ERROR) { - LOGE("Unable to marshal AudioFlinger"); - return; - } mStreamType = streamType; mVolume = volume; mpAudioTrack = 0; @@ -275,9 +271,9 @@ bool ToneGenerator::initAudioTrack() { mpAudioTrack = 0; } - // Open audio track in mono, PCM 16bit, default sampling rate, 2 buffers of + // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size mpAudioTrack - = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, 1, NUM_PCM_BUFFERS*mBufferSize, 0, audioCallback, this, mBufferSize); + = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, 1, 0, 0, audioCallback, this, 0); if (mpAudioTrack == 0) { LOGE("AudioTrack allocation failed"); diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index ebdbda89379de..31ff50730d7e5 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -172,7 +172,7 @@ status_t MediaPlayer::setDataSource(const sp& player) status_t MediaPlayer::setDataSource(const char *url) { LOGV("setDataSource(%s)", url); - status_t err = UNKNOWN_ERROR; + status_t err = BAD_VALUE; if (url != NULL) { const sp& service(getMediaPlayerService()); if (service != 0) { @@ -199,7 +199,7 @@ status_t MediaPlayer::setVideoSurface(const sp& surface) { LOGV("setVideoSurface"); Mutex::Autolock _l(mLock); - if (mPlayer == 0) return UNKNOWN_ERROR; + if (mPlayer == 0) return NO_INIT; return mPlayer->setVideoSurface(surface->getISurface()); } @@ -219,7 +219,7 @@ status_t MediaPlayer::prepare() { LOGV("prepare"); Mutex::Autolock _l(mLock); - if (mPrepareSync) return UNKNOWN_ERROR; + if (mPrepareSync) return -EALREADY; mPrepareSync = true; status_t ret = prepareAsync_l(); if (ret != NO_ERROR) return ret; @@ -253,7 +253,6 @@ status_t MediaPlayer::start() status_t ret = mPlayer->start(); if (ret != NO_ERROR) { mCurrentState = MEDIA_PLAYER_STATE_ERROR; - ret = UNKNOWN_ERROR; } else { if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) { LOGV("playback completed immediately following start()"); @@ -275,7 +274,6 @@ status_t MediaPlayer::stop() status_t ret = mPlayer->stop(); if (ret != NO_ERROR) { mCurrentState = MEDIA_PLAYER_STATE_ERROR; - ret = UNKNOWN_ERROR; } else { mCurrentState = MEDIA_PLAYER_STOPPED; } @@ -295,7 +293,6 @@ status_t MediaPlayer::pause() status_t ret = mPlayer->pause(); if (ret != NO_ERROR) { mCurrentState = MEDIA_PLAYER_STATE_ERROR; - ret = UNKNOWN_ERROR; } else { mCurrentState = MEDIA_PLAYER_PAUSED; } @@ -422,7 +419,6 @@ status_t MediaPlayer::reset() if (ret != NO_ERROR) { LOGE("reset() failed with return code (%d)", ret); mCurrentState = MEDIA_PLAYER_STATE_ERROR; - ret = UNKNOWN_ERROR; } else { mCurrentState = MEDIA_PLAYER_IDLE; } diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 53831717ec8a6..9e366e2660aa1 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -61,113 +61,32 @@ pid_t gettid() { return syscall(__NR_gettid);} #undef __KERNEL__ #endif -/* - When USE_SIGBUS_HANDLER is set to 1, a handler for SIGBUS will be - installed, which allows us to recover when there is a read error - when accessing an mmap'ed file. However, since the kernel folks - don't seem to like it when non kernel folks install signal handlers - in their own process, this is currently disabled. - Without the handler, the process hosting this service will die and - then be restarted. This is mostly OK right now because the process is - not being shared with any other services, and clients of the service - will be notified of its death in their MediaPlayer.onErrorListener - callback, assuming they have installed one, and can then attempt to - do their own recovery. - It does open us up to a DOS attack against the media server, where - a malicious application can trivially force the media server to - restart continuously. -*/ -#define USE_SIGBUS_HANDLER 0 - -// TODO: Temp hack until we can register players -static const char* MIDI_FILE_EXTS[] = -{ - ".mid", - ".smf", - ".xmf", - ".imy", - ".rtttl", - ".rtx", - ".ota" -}; namespace android { +// TODO: Temp hack until we can register players +typedef struct { + const char *extension; + const player_type playertype; +} extmap; +extmap FILE_EXTS [] = { + {".mid", SONIVOX_PLAYER}, + {".midi", SONIVOX_PLAYER}, + {".smf", SONIVOX_PLAYER}, + {".xmf", SONIVOX_PLAYER}, + {".imy", SONIVOX_PLAYER}, + {".rtttl", SONIVOX_PLAYER}, + {".rtx", SONIVOX_PLAYER}, + {".ota", SONIVOX_PLAYER}, + {".ogg", VORBIS_PLAYER}, + {".oga", VORBIS_PLAYER}, +}; + // TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround /* static */ const uint32_t MediaPlayerService::AudioOutput::kAudioVideoDelayMs = 96; /* static */ int MediaPlayerService::AudioOutput::mMinBufferCount = 4; /* static */ bool MediaPlayerService::AudioOutput::mIsOnEmulator = false; -static struct sigaction oldact; -static pthread_key_t sigbuskey; - -static void sigbushandler(int signal, siginfo_t *info, void *context) -{ - char *faultaddr = (char*) info->si_addr; - LOGE("SIGBUS at %p\n", faultaddr); - - struct mediasigbushandler* h = (struct mediasigbushandler*) pthread_getspecific(sigbuskey); - - if (h) { - if (h->len) { - if (faultaddr < h->base || faultaddr >= h->base + h->len) { - // outside specified range, call old handler - if (oldact.sa_flags & SA_SIGINFO) { - oldact.sa_sigaction(signal, info, context); - } else { - oldact.sa_handler(signal); - } - return; - } - } - - // no range specified or address was in range - - if (h->handlesigbus) { - if (h->handlesigbus(info, h)) { - // thread's handler didn't handle the signal - if (oldact.sa_flags & SA_SIGINFO) { - oldact.sa_sigaction(signal, info, context); - } else { - oldact.sa_handler(signal); - } - } - return; - } - - if (h->sigbusvar) { - // map in a zeroed out page so the operation can succeed - long pagesize = sysconf(_SC_PAGE_SIZE); - long pagemask = ~(pagesize - 1); - void * pageaddr = (void*) (((long)(faultaddr)) & pagemask); - - void * bar = mmap( pageaddr, pagesize, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0); - if (bar == MAP_FAILED) { - LOGE("couldn't map zero page at %p: %s", pageaddr, strerror(errno)); - if (oldact.sa_flags & SA_SIGINFO) { - oldact.sa_sigaction(signal, info, context); - } else { - oldact.sa_handler(signal); - } - return; - } - - LOGE("setting sigbusvar at %p", h->sigbusvar); - *(h->sigbusvar) = 1; - return; - } - } - - LOGE("SIGBUS: no handler, or improperly configured handler (%p)", h); - - if (oldact.sa_flags & SA_SIGINFO) { - oldact.sa_sigaction(signal, info, context); - } else { - oldact.sa_handler(signal); - } - return; -} - void MediaPlayerService::instantiate() { defaultServiceManager()->addService( String16("media.player"), new MediaPlayerService()); @@ -177,25 +96,10 @@ MediaPlayerService::MediaPlayerService() { LOGV("MediaPlayerService created"); mNextConnId = 1; - - pthread_key_create(&sigbuskey, NULL); - - -#if USE_SIGBUS_HANDLER - struct sigaction act; - memset(&act,0, sizeof act); - act.sa_sigaction = sigbushandler; - act.sa_flags = SA_SIGINFO; - sigaction(SIGBUS, &act, &oldact); -#endif } MediaPlayerService::~MediaPlayerService() { -#if USE_SIGBUS_HANDLER - sigaction(SIGBUS, &oldact, NULL); -#endif - pthread_key_delete(sigbuskey); LOGV("MediaPlayerService destroyed"); } @@ -481,7 +385,7 @@ static player_type getPlayerType(int fd, int64_t offset, int64_t length) locator.offset = offset; locator.length = length; EAS_HANDLE eashandle; - if (EAS_OpenFile(easdata, &locator, &eashandle, NULL) == EAS_SUCCESS) { + if (EAS_OpenFile(easdata, &locator, &eashandle) == EAS_SUCCESS) { EAS_CloseFile(easdata, eashandle); EAS_Shutdown(easdata); return SONIVOX_PLAYER; @@ -498,22 +402,16 @@ static player_type getPlayerType(const char* url) // use MidiFile for MIDI extensions int lenURL = strlen(url); - for (int i = 0; i < NELEM(MIDI_FILE_EXTS); ++i) { - int len = strlen(MIDI_FILE_EXTS[i]); + for (int i = 0; i < NELEM(FILE_EXTS); ++i) { + int len = strlen(FILE_EXTS[i].extension); int start = lenURL - len; if (start > 0) { - if (!strncmp(url + start, MIDI_FILE_EXTS[i], len)) { - LOGV("Type is MIDI"); - return SONIVOX_PLAYER; + if (!strncmp(url + start, FILE_EXTS[i].extension, len)) { + return FILE_EXTS[i].playertype; } } } - if (strcmp(url + strlen(url) - 4, ".ogg") == 0) { - LOGV("Type is Vorbis"); - return VORBIS_PLAYER; - } - // Fall through to PV return PV_PLAYER; } @@ -539,7 +437,6 @@ static sp createPlayer(player_type playerType, void* cookie, if (p != NULL) { if (p->initCheck() == NO_ERROR) { p->setNotifyCallback(cookie, notifyFunc); - p->setSigBusHandlerStructTLSKey(sigbuskey); } else { p.clear(); } diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp index cfad66c8ffe29..7ce2fab041e00 100644 --- a/media/libmediaplayerservice/MidiFile.cpp +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -40,8 +40,6 @@ static pid_t myTid() { return getpid(); } // ---------------------------------------------------------------------------- -extern pthread_key_t EAS_sigbuskey; - namespace android { // ---------------------------------------------------------------------------- @@ -132,7 +130,7 @@ status_t MidiFile::setDataSource(const char* path) mFileLocator.fd = -1; mFileLocator.offset = 0; mFileLocator.length = 0; - EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle, &mMemFailedVar); + EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle); if (result == EAS_SUCCESS) { updateState(); } @@ -148,12 +146,6 @@ status_t MidiFile::setDataSource(const char* path) return NO_ERROR; } -status_t MidiFile::setSigBusHandlerStructTLSKey(pthread_key_t key) -{ - EAS_sigbuskey = key; - return 0; -} - status_t MidiFile::setDataSource(int fd, int64_t offset, int64_t length) { LOGV("MidiFile::setDataSource fd=%d", fd); @@ -168,7 +160,7 @@ status_t MidiFile::setDataSource(int fd, int64_t offset, int64_t length) mFileLocator.fd = dup(fd); mFileLocator.offset = offset; mFileLocator.length = length; - EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle, &mMemFailedVar); + EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle); updateState(); if (result != EAS_SUCCESS) { @@ -332,7 +324,7 @@ status_t MidiFile::getDuration(int* duration) EAS_HANDLE easHandle = NULL; EAS_RESULT result = EAS_Init(&easData); if (result == EAS_SUCCESS) { - result = EAS_OpenFile(easData, &mFileLocator, &easHandle, NULL); + result = EAS_OpenFile(easData, &mFileLocator, &easHandle); } if (result == EAS_SUCCESS) { result = EAS_Prepare(easData, easHandle); @@ -451,8 +443,6 @@ int MidiFile::render() { LOGV("MidiFile::render"); - struct mediasigbushandler sigbushandler; - // allocate render buffer mAudioBuffer = new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * NUM_BUFFERS]; if (!mAudioBuffer) { @@ -468,10 +458,6 @@ int MidiFile::render() { mCondition.signal(); } - sigbushandler.handlesigbus = NULL; - sigbushandler.sigbusvar = mMemFailedVar; - pthread_setspecific(EAS_sigbuskey, &sigbushandler); - while (1) { mMutex.lock(); diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h index 9d2dfdd72f9d6..302f1cf2f8e46 100644 --- a/media/libmediaplayerservice/MidiFile.h +++ b/media/libmediaplayerservice/MidiFile.h @@ -30,7 +30,6 @@ public: ~MidiFile(); virtual status_t initCheck(); - virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key); virtual status_t setDataSource(const char* path); virtual status_t setDataSource(int fd, int64_t offset, int64_t length); virtual status_t setVideoSurface(const sp& surface) { return UNKNOWN_ERROR; } @@ -57,7 +56,6 @@ private: Mutex mMutex; Condition mCondition; - int* mMemFailedVar; EAS_DATA_HANDLE mEasData; EAS_HANDLE mEasHandle; EAS_PCM* mAudioBuffer; diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp index 9a644034a7520..009d6286a0f3a 100644 --- a/media/libmediaplayerservice/VorbisPlayer.cpp +++ b/media/libmediaplayerservice/VorbisPlayer.cpp @@ -455,13 +455,15 @@ int VorbisPlayer::render() { current_section = 0; numread = ov_read(&mVorbisFile, mAudioBuffer, AUDIOBUFFER_SIZE, ¤t_section); } else { - sendEvent(MEDIA_PLAYBACK_COMPLETE); mAudioSink->stop(); audioStarted = false; mRender = false; mPaused = true; int endpos = ov_time_tell(&mVorbisFile); + LOGV("send MEDIA_PLAYBACK_COMPLETE"); + sendEvent(MEDIA_PLAYBACK_COMPLETE); + // wait until we're started again LOGV("playback complete - wait for signal"); mCondition.wait(mMutex); diff --git a/media/sdutils/sdutil.cpp b/media/sdutils/sdutil.cpp index 0daa52314e2d0..a9aabf03624a8 100644 --- a/media/sdutils/sdutil.cpp +++ b/media/sdutils/sdutil.cpp @@ -114,6 +114,16 @@ static int unmount(const char* path) { return -1; } +static int format(const char* path) { + String16 string(path); + + if (isMounted(path)) + return -EBUSY; + gMountService->formatMedia(string); + + return 0; +} + static int umsEnable(bool enable) { gMountService->setMassStorageEnabled(enable); return 0; @@ -129,6 +139,9 @@ int main(int argc, char **argv) if (strcmp(command, "mount") == 0) { android::init(); return android::mount(argument); + } else if (strcmp(command, "format") == 0) { + android::init(); + return android::format(argument); } else if (strcmp(command, "unmount") == 0) { android::init(); return android::unmount(argument); @@ -145,6 +158,7 @@ int main(int argc, char **argv) fprintf(stderr, "usage:\n" " sdutil mount - mounts the SD card at the given mount point\n" " sdutil unmount - unmounts the SD card at the given mount point\n" + " sdutil format - formats the SD card at the given mount point\n" " sdutil ums enable - enables USB mass storage\n" " sdutil ums disable - disnables USB mass storage\n" ); diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml index 16e658a327a5d..a32f5907f391f 100644 --- a/media/tests/MediaFrameworkTest/AndroidManifest.xml +++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml @@ -42,5 +42,10 @@ android:targetPackage="com.android.mediaframeworktest" android:label="MediaFramework unit tests InstrumentationRunner"> + + + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java index 3d3878efd21f7..173194042c333 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java @@ -21,6 +21,7 @@ import com.android.mediaframeworktest.functional.SimTonesTest; import com.android.mediaframeworktest.functional.MediaMetadataTest; import com.android.mediaframeworktest.functional.CameraTest; import com.android.mediaframeworktest.functional.MediaRecorderTest; +import com.android.mediaframeworktest.functional.MediaAudioTrackTest; import junit.framework.TestSuite; @@ -48,6 +49,7 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner { suite.addTestSuite(MediaMetadataTest.class); suite.addTestSuite(CameraTest.class); suite.addTestSuite(MediaRecorderTest.class); + suite.addTestSuite(MediaAudioTrackTest.class); return suite; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java index 584300775a87e..5e9c4882fb104 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java @@ -25,16 +25,16 @@ package com.android.mediaframeworktest; public class MediaNames { //Audio files - public static final String MP3CBR = "/sdcard/music/MP3CBR.mp3"; - public static final String MP3VBR = "/sdcard/music/MP3VBR.mp3"; - public static final String SHORTMP3 = "/sdcard/music/SHORTMP3.mp3"; - public static final String MIDI = "/sdcard/music/MIDI.mid"; - public static final String WMA9 = "/sdcard/music/WMA9.wma"; - public static final String WMA10 = "/sdcard/music/WMA10.wma"; - public static final String WAV = "/sdcard/music/complicated_wav.wav"; - public static final String AMR = "/sdcard/music/AMRNB.amr"; - public static final String OGG = "/sdcard/music/Mists_of_Time-4T.ogg"; - public static final String OGGSHORT = "/sdcard/music/Skippy.ogg"; + public static final String MP3CBR = "/sdcard/media_api/music/MP3CBR.mp3"; + public static final String MP3VBR = "/sdcard/media_api/music/MP3VBR.mp3"; + public static final String SHORTMP3 = "/sdcard/media_api/music/SHORTMP3.mp3"; + public static final String MIDI = "/sdcard/media_api/music/MIDI.mid"; + public static final String WMA9 = "/sdcard/media_api/music/WMA9.wma"; + public static final String WMA10 = "/sdcard/media_api/music/WMA10.wma"; + public static final String WAV = "/sdcard/media_api/music/complicated_wav.wav"; + public static final String AMR = "/sdcard/media_api/music/AMRNB.amr"; + public static final String OGG = "/sdcard/media_api/music/Mists_of_Time-4T.ogg"; + public static final String OGGSHORT = "/sdcard/media_api/music/Skippy.ogg"; public static final int MP3CBR_LENGTH = 231116; public static final int MP3VBR_LENGTH = 126407; @@ -60,20 +60,20 @@ public class MediaNames { //public static final String VIDEO_RTSP3GP = "rtsp://193.159.241.21/sp/alizee05.3gp"; //local video - public static final String VIDEO_MP4 = "/sdcard/video/gingerkids.MP4"; - public static final String VIDEO_LONG_3GP = "/sdcard/video/radiohead.3gp"; - public static final String VIDEO_SHORT_3GP = "/sdcard/video/short.3gp"; - public static final String VIDEO_LARGE_SIZE_3GP = "/sdcard/video/border_large.3gp"; - public static final String VIDEO_H263_AAC = "/sdcard/video/H263_AAC.3gp"; - public static final String VIDEO_H263_AMR = "/sdcard/video/H263_AMR.3gp"; - public static final String VIDEO_H264_AAC = "/sdcard/video/H264_AAC.3gp"; - public static final String VIDEO_H264_AMR = "/sdcard/video/H264_AMR.3gp"; - public static final String VIDEO_WMV = "/sdcard/video/bugs.wmv"; - public static final String VIDEO_HIGHRES_H263 = "/sdcard/video/h263_qcif_30fps.3gp"; - public static final String VIDEO_HIGHRES_MP4 = "/sdcard/video/mpeg4_qvga_24fps.3gp"; + public static final String VIDEO_MP4 = "/sdcard/media_api/video/gingerkids.MP4"; + public static final String VIDEO_LONG_3GP = "/sdcard/media_api/video/radiohead.3gp"; + public static final String VIDEO_SHORT_3GP = "/sdcard/media_api/video/short.3gp"; + public static final String VIDEO_LARGE_SIZE_3GP = "/sdcard/media_api/video/border_large.3gp"; + public static final String VIDEO_H263_AAC = "/sdcard/media_api/video/H263_AAC.3gp"; + public static final String VIDEO_H263_AMR = "/sdcard/media_api/video/H263_AMR.3gp"; + public static final String VIDEO_H264_AAC = "/sdcard/media_api/video/H264_AAC.3gp"; + public static final String VIDEO_H264_AMR = "/sdcard/media_api/video/H264_AMR.3gp"; + public static final String VIDEO_WMV = "/sdcard/media_api/video/bugs.wmv"; + public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/h263_qcif_30fps.3gp"; + public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/mpeg4_qvga_24fps.3gp"; //ringtone - public static final String ringtone = "/sdcard/ringtones/F1_NewVoicemail.mp3"; + public static final String ringtone = "/sdcard/media_api/ringtones/F1_NewVoicemail.mp3"; //streaming mp3 public static final String STREAM_LARGE_MP3 = @@ -110,264 +110,266 @@ public class MediaNames { "http://wms.pv.com:7070/MediaDownloadContent/UserUploads/beefcake.mp3"; //Sonivox - public static String MIDIFILES[] = { "/sdcard/music/Leadsol.mxmf", - "/sdcard/music/abba.imy", "/sdcard/music/ants.mid", - "/sdcard/music/greensleeves.rtttl", "/sdcard/music/test.ota"}; + public static String MIDIFILES[] = { + "/sdcard/media_api/music/Leadsol.mxmf", + "/sdcard/media_api/music/abba.imy", "/sdcard/media_api/music/ants.mid", + "/sdcard/media_api/music/greensleeves.rtttl", "/sdcard/media_api/music/test.ota"}; //Performance measurement - public static String[] WAVFILES = { "/sdcard/music_perf/WAV/M1F1-AlawWE-AFsp.wav", - "/sdcard/music_perf/WAV/M1F1-float64-AFsp.wav", - "/sdcard/music_perf/WAV/song.wav", - "/sdcard/music_perf/WAV/WAVEtest.wav", - "/sdcard/music_perf/WAV/WAVEtest_out.wav", - "/sdcard/music_perf/WAV/test_out.wav"}; - + public static String[] WAVFILES = { + "/sdcard/media_api/music_perf/WAV/M1F1-AlawWE-AFsp.wav", + "/sdcard/media_api/music_perf/WAV/M1F1-float64-AFsp.wav", + "/sdcard/media_api/music_perf/WAV/song.wav", + "/sdcard/media_api/music_perf/WAV/WAVEtest.wav", + "/sdcard/media_api/music_perf/WAV/WAVEtest_out.wav", + "/sdcard/media_api/music_perf/WAV/test_out.wav"}; + public static String[] AMRNBFILES = { - "/sdcard/music_perf/AMR/AI_AMR-NB_5.9kbps_6.24kbps_8khz_mono_NMC.amr", - "/sdcard/music_perf/AMR/AI_AMR-NB_5.15kbps_5.46kbps_8khz_mono_NMC.amr", - "/sdcard/music_perf/AMR/AI_AMR-NB_7.4kbps_7.80kbps_8khz_mono_NMC.amr", - "/sdcard/music_perf/AMR/AI_AMR-NB_7.95kbps_9.6kbps_8khz_mono_NMC.amr", - "/sdcard/music_perf/AMR/AI_AMR-NB_10.2kbps_10.48kbps_8khz_mono_NMC.amr"}; + "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.9kbps_6.24kbps_8khz_mono_NMC.amr", + "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.15kbps_5.46kbps_8khz_mono_NMC.amr", + "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.4kbps_7.80kbps_8khz_mono_NMC.amr", + "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.95kbps_9.6kbps_8khz_mono_NMC.amr", + "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_10.2kbps_10.48kbps_8khz_mono_NMC.amr"}; public static String[] AMRWBFILES = { - "/sdcard/music_perf/AMRWB/NIN_AMR-WB_15.85kbps_16kbps.amr", - "/sdcard/music_perf/AMRWB/NIN_AMR-WB_18.25kbps_18kbps.amr", - "/sdcard/music_perf/AMRWB/NIN_AMR-WB_19.85kbps_20kbps.amr", - "/sdcard/music_perf/AMRWB/NIN_AMR-WB_23.05kbps_23kbps.amr", - "/sdcard/music_perf/AMRWB/NIN_AMR-WB_23.85kbps_24kbps.amr", - "/sdcard/music_perf/AMRWB/PD_AMR-WB_19.85kbps_20kbps.amr", - "/sdcard/music_perf/AMRWB/PD_AMR-WB_23.05kbps_23kbps.amr", - "/sdcard/music_perf/AMRWB/PD_AMR-WB_23.85kbps_24kbps.amr", - "/sdcard/music_perf/AMRWB/WC_AMR-WB_23.05kbps_23kbps.amr", - "/sdcard/music_perf/AMRWB/WC_AMR-WB_23.85kbps_24kbps.amr", }; + "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_15.85kbps_16kbps.amr", + "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_18.25kbps_18kbps.amr", + "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_19.85kbps_20kbps.amr", + "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.05kbps_23kbps.amr", + "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.85kbps_24kbps.amr", + "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_19.85kbps_20kbps.amr", + "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.05kbps_23kbps.amr", + "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.85kbps_24kbps.amr", + "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.05kbps_23kbps.amr", + "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.85kbps_24kbps.amr", }; public static String[] MP3FILES = { - "/sdcard/music_perf/MP3/NIN_56kbps_32khz_stereo_VBR_MCA.MP3", - "/sdcard/music_perf/MP3/NIN_80kbps_32khz_stereo_VBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_80kbps_44.1khz_stereo_VBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_80kbps_48khz_stereo_VBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_112kbps_32khz_stereo_VBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_112kbps_44.1khz_stereo_VBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_112kbps_48khz_stereo_VBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_192kbps_32khz_mono_CBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_192kbps_44.1khz_mono_CBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_192kbps_48khz_mono_CBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_256kbps_44.1khz_mono_CBR_MCA.mp3", - "/sdcard/music_perf/MP3/NIN_256kbps_48khz_mono_CBR_MCA.mp3", - "/sdcard/music_perf/MP3/PD_112kbps_32khz_stereo_VBR_MCA.mp3", - "/sdcard/music_perf/MP3/PD_112kbps_44.1khz_stereo_VBR_MCA.mp3", - "/sdcard/music_perf/MP3/PD_112kbps_48khz_stereo_VBR_MCA.mp3", - "/sdcard/music_perf/MP3/PD_192kbps_32khz_mono_CBR_DPA.mp3", - "/sdcard/music_perf/MP3/PD_256kbps_44.1khz_mono_CBR_DPA.mp3", - "/sdcard/music_perf/MP3/PD_256kbps_48khz_mono_CBR_MCA.mp3", - "/sdcard/music_perf/MP3/WC_256kbps_44.1khz_mono_CBR_DPA.mp3", - "/sdcard/music_perf/MP3/WC_256kbps_48khz_mono_CBR_DPA.mp3", - "/sdcard/music_perf/regular_album_photo/Apologize.mp3", - "/sdcard/music_perf/regular_album_photo/Because_Of_You.mp3", - "/sdcard/music_perf/regular_album_photo/Complicated.mp3", - "/sdcard/music_perf/regular_album_photo/Glamorous.mp3", - "/sdcard/music_perf/regular_album_photo/Im_With_You.mp3", - "/sdcard/music_perf/regular_album_photo/Smile.mp3", - "/sdcard/music_perf/regular_album_photo/Suddenly_I_See.mp3", - "/sdcard/music_perf/regular_album_photo/When You Say Nothing At All.mp3", - "/sdcard/music_perf/regular_album_photo/my_happy_ending.mp3"}; + "/sdcard/media_api/music_perf/MP3/NIN_56kbps_32khz_stereo_VBR_MCA.MP3", + "/sdcard/media_api/music_perf/MP3/NIN_80kbps_32khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_80kbps_44.1khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_80kbps_48khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_112kbps_32khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_112kbps_44.1khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_112kbps_48khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_192kbps_32khz_mono_CBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_192kbps_44.1khz_mono_CBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_192kbps_48khz_mono_CBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_256kbps_44.1khz_mono_CBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/NIN_256kbps_48khz_mono_CBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/PD_112kbps_32khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/PD_112kbps_44.1khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/PD_112kbps_48khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/PD_192kbps_32khz_mono_CBR_DPA.mp3", + "/sdcard/media_api/music_perf/MP3/PD_256kbps_44.1khz_mono_CBR_DPA.mp3", + "/sdcard/media_api/music_perf/MP3/PD_256kbps_48khz_mono_CBR_MCA.mp3", + "/sdcard/media_api/music_perf/MP3/WC_256kbps_44.1khz_mono_CBR_DPA.mp3", + "/sdcard/media_api/music_perf/MP3/WC_256kbps_48khz_mono_CBR_DPA.mp3", + "/sdcard/media_api/music_perf/regular_album_photo/Apologize.mp3", + "/sdcard/media_api/music_perf/regular_album_photo/Because_Of_You.mp3", + "/sdcard/media_api/music_perf/regular_album_photo/Complicated.mp3", + "/sdcard/media_api/music_perf/regular_album_photo/Glamorous.mp3", + "/sdcard/media_api/music_perf/regular_album_photo/Im_With_You.mp3", + "/sdcard/media_api/music_perf/regular_album_photo/Smile.mp3", + "/sdcard/media_api/music_perf/regular_album_photo/Suddenly_I_See.mp3", + "/sdcard/media_api/music_perf/regular_album_photo/When You Say Nothing At All.mp3", + "/sdcard/media_api/music_perf/regular_album_photo/my_happy_ending.mp3"}; public static String[] AACFILES = { - "/sdcard/music_perf/AAC/AI_AAC_24kbps_12khz_Mono_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/AI_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/AI_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/music_perf/AAC/AI_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/music_perf/AAC/NIN_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/NIN_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/music_perf/AAC/NIN_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/music_perf/AAC/PD_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/PD_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/music_perf/AAC/PD_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/music_perf/AAC/PV_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/PV_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/music_perf/AAC/PV_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/music_perf/AAC/WC_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/WC_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/music_perf/AAC/WC_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/AI_AAC_24kbps_12khz_Mono_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", + "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", }; - public static String[] VIDEOFILES = { "/sdcard/video_perf/AI_CTO_Mpeg4_32kbps_10fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/video_perf/AI_CTO_Mpeg4_32kbps_12fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/video_perf/AI_CTO_Mpeg4_32kbps_15fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_SSE.mp4", - "/sdcard/video_perf/AI_CTO_Mpeg4_32kbps_7.5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/video_perf/AI_WMV_1024kbps_20fps_QCIF_176x144_noaudio_SSE.wmv", - "/sdcard/video_perf/AI_WMV_1024kbps_25fps_QCIF_176x144_noaudio_SSE.wmv", - "/sdcard/video_perf/Chicken.wmv", - "/sdcard/video_perf/MP_qcif_15fps_100kbps_48kHz_192kbps_30secs.wmv", - "/sdcard/video_perf/NIN_CTO_H264_123kbps_5fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", - "/sdcard/video_perf/NIN_CTO_H264_96kbps_10.2fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", - "/sdcard/video_perf/NIN_CTO_H264_96kbps_12fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", - "/sdcard/video_perf/NIN_CTO_H264_96kbps_15fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", - "/sdcard/video_perf/NIN_CTO_Mpeg4_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_mono_SSE.3gp", - "/sdcard/video_perf/NIN_CTO_Mpeg4_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp", - "/sdcard/video_perf/NIN_CTO_Mpeg4_128kbps_10fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/video_perf/NIN_CTO_Mpeg4_128kbps_12fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/video_perf/NIN_CTO_Mpeg4_128kbps_15fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/video_perf/NIN_CTO_Mpeg4_128kbps_5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/video_perf/NIN_CTO_Mpeg4_128kbps_7.5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/video_perf/NIN_H263_128kbps_10fps_QCIF_174x144_noaudio_SSE.mp4", - "/sdcard/video_perf/NIN_H263_128kbps_15fps_QCIF_174x144_noaudio_SSE.mp4", - "/sdcard/video_perf/NIN_H263_48kbps_10fps_QCIF_174x144_noaudio_SSE.3gp", - "/sdcard/video_perf/NIN_H263_48kbps_12fps_QCIF_174x144_noaudio_SSE.3gp", - "/sdcard/video_perf/NIN_H264_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp", - "/sdcard/video_perf/NIN_H264_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp", - "/sdcard/video_perf/PV_H264_2000kbps_20fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4", - "/sdcard/video_perf/PV_H264_2000kbps_25fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4", - "/sdcard/video_perf/PV_H264_2000kbps_30fps_CIF_352x288+AAC_128kbps_48khz_stereo_SSE.mp4", - "/sdcard/video_perf/Stevie-1.wmv", - "/sdcard/video_perf/WC_H264_1600kbps_20fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4", - "/sdcard/video_perf/WC_H264_1600kbps_25fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4", - "/sdcard/video_perf/WC_H264_1600kbps_30fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4", - "/sdcard/video_perf/bugs.wmv", - "/sdcard/video_perf/niceday.wmv", - "/sdcard/video_perf/eaglesatopnflpe.wmv", + public static String[] VIDEOFILES = { "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_10fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", + "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_12fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", + "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_15fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", + "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", + "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_SSE.mp4", + "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_7.5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", + "/sdcard/media_api/video_perf/AI_WMV_1024kbps_20fps_QCIF_176x144_noaudio_SSE.wmv", + "/sdcard/media_api/video_perf/AI_WMV_1024kbps_25fps_QCIF_176x144_noaudio_SSE.wmv", + "/sdcard/media_api/video_perf/Chicken.wmv", + "/sdcard/media_api/video_perf/MP_qcif_15fps_100kbps_48kHz_192kbps_30secs.wmv", + "/sdcard/media_api/video_perf/NIN_CTO_H264_123kbps_5fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_10.2fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_12fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_15fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_mono_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_10fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_12fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_15fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_7.5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_H263_128kbps_10fps_QCIF_174x144_noaudio_SSE.mp4", + "/sdcard/media_api/video_perf/NIN_H263_128kbps_15fps_QCIF_174x144_noaudio_SSE.mp4", + "/sdcard/media_api/video_perf/NIN_H263_48kbps_10fps_QCIF_174x144_noaudio_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_H263_48kbps_12fps_QCIF_174x144_noaudio_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_H264_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp", + "/sdcard/media_api/video_perf/NIN_H264_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp", + "/sdcard/media_api/video_perf/PV_H264_2000kbps_20fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4", + "/sdcard/media_api/video_perf/PV_H264_2000kbps_25fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4", + "/sdcard/media_api/video_perf/PV_H264_2000kbps_30fps_CIF_352x288+AAC_128kbps_48khz_stereo_SSE.mp4", + "/sdcard/media_api/video_perf/Stevie-1.wmv", + "/sdcard/media_api/video_perf/WC_H264_1600kbps_20fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4", + "/sdcard/media_api/video_perf/WC_H264_1600kbps_25fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4", + "/sdcard/media_api/video_perf/WC_H264_1600kbps_30fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4", + "/sdcard/media_api/video_perf/bugs.wmv", + "/sdcard/media_api/video_perf/niceday.wmv", + "/sdcard/media_api/video_perf/eaglesatopnflpe.wmv", }; //wma - only support up to wma 9 public static String[] WMASUPPORTED = { - "/sdcard/music_perf/WMASUPPORTED/AI_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/music_perf/WMASUPPORTED/AI_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/music_perf/WMASUPPORTED/NIN_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/music_perf/WMASUPPORTED/NIN_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/music_perf/WMASUPPORTED/PD_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/music_perf/WMASUPPORTED/PD_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/music_perf/WMASUPPORTED/PV_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/music_perf/WMASUPPORTED/PV_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/music_perf/WMASUPPORTED/WC_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/music_perf/WMASUPPORTED/WC_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma" + "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma" }; public static String[] WMAUNSUPPORTED = { - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_127kbps_48khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_44.1khz_stereo_2pVBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_48khz_stereo_2pVBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_88khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_96khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_44.1khz_stereo_2pVBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_88khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_96khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_44khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_48khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_88khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_96khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_44khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_48khz_stereo_CBR_DPA.wma", - "/sdcard/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_88khz_stereo_CBR_DPA.wma" + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_127kbps_48khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_44.1khz_stereo_2pVBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_48khz_stereo_2pVBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_88khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_96khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_44.1khz_stereo_2pVBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_88khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_96khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_44khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_48khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_88khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_96khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_44khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_48khz_stereo_CBR_DPA.wma", + "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_88khz_stereo_CBR_DPA.wma" }; //Media Recorder - public static final String RECORDER_OUTPUT = "/sdcard/recorderOutput.amr"; + public static final String RECORDER_OUTPUT = "/sdcard/media_api/recorderOutput.amr"; //video thumbnail - public static final String THUMBNAIL_OUTPUT = "/sdcard/videoThumbnail.png"; - public static final String GOLDEN_THUMBNAIL_OUTPUT = "/sdcard/goldenThumbnail.png"; + public static final String THUMBNAIL_OUTPUT = "/sdcard/media_api/videoThumbnail.png"; + public static final String GOLDEN_THUMBNAIL_OUTPUT = "/sdcard/media_api/goldenThumbnail.png"; //Metadata Utility public static final String[] THUMBNAIL_CAPTURE_TEST_FILES = { - "/sdcard/metadata/test.mp4", - "/sdcard/metadata/test1.3gp", - "/sdcard/metadata/test2.3gp", - "/sdcard/metadata/test3.3gp", - "/sdcard/metadata/test4.3gp", - "/sdcard/metadata/test5.3gp", - "/sdcard/metadata/test6.3gp", - "/sdcard/metadata/test7.3gp", - "/sdcard/metadata/test8.3gp", - "/sdcard/metadata/test9.3gp", - "/sdcard/metadata/test10.3gp", - "/sdcard/metadata/test11.3gp", - "/sdcard/metadata/test12.3gp", - "/sdcard/metadata/test13.3gp", - "/sdcard/metadata/test14.3gp", - "/sdcard/metadata/test15.3gp", - "/sdcard/metadata/test16.3gp", - "/sdcard/metadata/test17.3gp", - "/sdcard/metadata/test18.3gp", - "/sdcard/metadata/test19.3gp", - "/sdcard/metadata/test20.3gp", - "/sdcard/metadata/test21.3gp", - "/sdcard/metadata/test22.3gp", - "/sdcard/metadata/test23.3gp", - "/sdcard/metadata/test24.3gp", - "/sdcard/metadata/test25.3gp", - "/sdcard/metadata/test26.3gp", - "/sdcard/metadata/test27.3gp", - "/sdcard/metadata/test28.3gp", - "/sdcard/metadata/test29.3gp", - "/sdcard/metadata/test30.3gp", - "/sdcard/metadata/test31.3gp", - "/sdcard/metadata/test32.3gp", - "/sdcard/metadata/test33.3gp", - "/sdcard/metadata/test35.mp4", - "/sdcard/metadata/test36.m4v", - "/sdcard/metadata/test34.wmv", - "/sdcard/metadata/test_metadata.mp4", + "/sdcard/media_api/metadata/test.mp4", + "/sdcard/media_api/metadata/test1.3gp", + "/sdcard/media_api/metadata/test2.3gp", + "/sdcard/media_api/metadata/test3.3gp", + "/sdcard/media_api/metadata/test4.3gp", + "/sdcard/media_api/metadata/test5.3gp", + "/sdcard/media_api/metadata/test6.3gp", + "/sdcard/media_api/metadata/test7.3gp", + "/sdcard/media_api/metadata/test8.3gp", + "/sdcard/media_api/metadata/test9.3gp", + "/sdcard/media_api/metadata/test10.3gp", + "/sdcard/media_api/metadata/test11.3gp", + "/sdcard/media_api/metadata/test12.3gp", + "/sdcard/media_api/metadata/test13.3gp", + "/sdcard/media_api/metadata/test14.3gp", + "/sdcard/media_api/metadata/test15.3gp", + "/sdcard/media_api/metadata/test16.3gp", + "/sdcard/media_api/metadata/test17.3gp", + "/sdcard/media_api/metadata/test18.3gp", + "/sdcard/media_api/metadata/test19.3gp", + "/sdcard/media_api/metadata/test20.3gp", + "/sdcard/media_api/metadata/test21.3gp", + "/sdcard/media_api/metadata/test22.3gp", + "/sdcard/media_api/metadata/test23.3gp", + "/sdcard/media_api/metadata/test24.3gp", + "/sdcard/media_api/metadata/test25.3gp", + "/sdcard/media_api/metadata/test26.3gp", + "/sdcard/media_api/metadata/test27.3gp", + "/sdcard/media_api/metadata/test28.3gp", + "/sdcard/media_api/metadata/test29.3gp", + "/sdcard/media_api/metadata/test30.3gp", + "/sdcard/media_api/metadata/test31.3gp", + "/sdcard/media_api/metadata/test32.3gp", + "/sdcard/media_api/metadata/test33.3gp", + "/sdcard/media_api/metadata/test35.mp4", + "/sdcard/media_api/metadata/test36.m4v", + "/sdcard/media_api/metadata/test34.wmv", + "/sdcard/media_api/metadata/test_metadata.mp4", }; public static final String[] METADATA_RETRIEVAL_TEST_FILES = { // Raw AAC is not supported - // "/sdcard/test_raw.aac", - // "/sdcard/test_adts.aac", - // "/sdcard/test_adif.aac", - "/sdcard/metadata/test_metadata.mp4", - "/sdcard/metadata/WMA10.wma", - "/sdcard/metadata/Leadsol_out.wav", - "/sdcard/metadata/test_aac.mp4", - "/sdcard/metadata/test_amr.mp4", - "/sdcard/metadata/test_avc_amr.mp4", - "/sdcard/metadata/test_metadata.mp4", - "/sdcard/metadata/test_vbr.mp3", - "/sdcard/metadata/test_cbr.mp3", - "/sdcard/metadata/metadata_test1.mp3", - "/sdcard/metadata/test33.3gp", - "/sdcard/metadata/test35.mp4", - "/sdcard/metadata/test36.m4v", - "/sdcard/metadata/test_m4v_amr.mp4", - "/sdcard/metadata/test_h263_amr.mp4", - "/sdcard/metadata/test34.wmv", + // "/sdcard/media_api/test_raw.aac", + // "/sdcard/media_api/test_adts.aac", + // "/sdcard/media_api/test_adif.aac", + "/sdcard/media_api/metadata/test_metadata.mp4", + "/sdcard/media_api/metadata/WMA10.wma", + "/sdcard/media_api/metadata/Leadsol_out.wav", + "/sdcard/media_api/metadata/test_aac.mp4", + "/sdcard/media_api/metadata/test_amr.mp4", + "/sdcard/media_api/metadata/test_avc_amr.mp4", + "/sdcard/media_api/metadata/test_metadata.mp4", + "/sdcard/media_api/metadata/test_vbr.mp3", + "/sdcard/media_api/metadata/test_cbr.mp3", + "/sdcard/media_api/metadata/metadata_test1.mp3", + "/sdcard/media_api/metadata/test33.3gp", + "/sdcard/media_api/metadata/test35.mp4", + "/sdcard/media_api/metadata/test36.m4v", + "/sdcard/media_api/metadata/test_m4v_amr.mp4", + "/sdcard/media_api/metadata/test_h263_amr.mp4", + "/sdcard/media_api/metadata/test34.wmv", }; public static final String[] ALBUMART_TEST_FILES = { - "/sdcard/album_photo/test_22_16_mp3.mp3", - "/sdcard/album_photo/PD_256kbps_48khz_mono_CBR_MCA.mp3", - "/sdcard/album_photo/PD_256kbps_44.1khz_mono_CBR_DPA.mp3", - "/sdcard/album_photo/PD_192kbps_32khz_mono_CBR_DPA.mp3", - "/sdcard/album_photo/NIN_256kbps_48khz_mono_CBR_MCA.mp3", - "/sdcard/album_photo/NIN_256kbps_44.1khz_mono_CBR_MCA.mp3", - "/sdcard/album_photo/NIN_112kbps(96kbps)_48khz_stereo_VBR_MCA.mp3", - "/sdcard/album_photo/NIN_112kbps(96kbps)_44.1khz_stereo_VBR_MCA.mp3", - "/sdcard/album_photo/lightGreen1.mp3", - "/sdcard/album_photo/babyBlue2 1.mp3", - "/sdcard/album_photo/2-01 01 NIN_56kbps(64kbps)_32khz_stereo_VBR_MCA.mp3", - "/sdcard/album_photo/02_NIN_112kbps(80kbps)_32khz_stereo_VBR_MCA.mp3", - "/sdcard/album_photo/No_Woman_No_Cry_128K.wma", - "/sdcard/album_photo/Beethoven_2.wma", + "/sdcard/media_api/album_photo/test_22_16_mp3.mp3", + "/sdcard/media_api/album_photo/PD_256kbps_48khz_mono_CBR_MCA.mp3", + "/sdcard/media_api/album_photo/PD_256kbps_44.1khz_mono_CBR_DPA.mp3", + "/sdcard/media_api/album_photo/PD_192kbps_32khz_mono_CBR_DPA.mp3", + "/sdcard/media_api/album_photo/NIN_256kbps_48khz_mono_CBR_MCA.mp3", + "/sdcard/media_api/album_photo/NIN_256kbps_44.1khz_mono_CBR_MCA.mp3", + "/sdcard/media_api/album_photo/NIN_112kbps(96kbps)_48khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/album_photo/NIN_112kbps(96kbps)_44.1khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/album_photo/lightGreen1.mp3", + "/sdcard/media_api/album_photo/babyBlue2 1.mp3", + "/sdcard/media_api/album_photo/2-01 01 NIN_56kbps(64kbps)_32khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/album_photo/02_NIN_112kbps(80kbps)_32khz_stereo_VBR_MCA.mp3", + "/sdcard/media_api/album_photo/No_Woman_No_Cry_128K.wma", + "/sdcard/media_api/album_photo/Beethoven_2.wma", }; //TEST_PATH_1: is a video and contains metadata for key "num-tracks" // TEST_PATH_2: any valid media file. // TEST_PATH_3: invalid media file - public static final String TEST_PATH_1 = "/sdcard/metadata/test.mp4"; - public static final String TEST_PATH_3 = "/sdcard/data.txt"; + public static final String TEST_PATH_1 = "/sdcard/media_api/metadata/test.mp4"; + public static final String TEST_PATH_3 = "/sdcard/media_api/data.txt"; public static final String TEST_PATH_4 = "somenonexistingpathname"; public static final String TEST_PATH_5 = "mem://012345"; @@ -376,81 +378,81 @@ public class MediaNames { //cd_track_number, album, artist, author, composer, date, genre //title, years, duration public static final String META_DATA_MP3 [][] = { - {"/sdcard/metaDataTestMedias/MP3/ID3V1_ID3V2.mp3", "1/10", "ID3V2.3 Album", "ID3V2.3 Artist", + {"/sdcard/media_api/metaDataTestMedias/MP3/ID3V1_ID3V2.mp3", "1/10", "ID3V2.3 Album", "ID3V2.3 Artist", "ID3V2.3 Lyricist", "ID3V2.3 Composer", null, "Blues", "ID3V2.3 Title", "1234", "321", "1"}, - {"/sdcard/metaDataTestMedias/MP3/ID3V2.mp3", "1/10", "ID3V2.3 Album", "ID3V2.3 Artist", + {"/sdcard/media_api/metaDataTestMedias/MP3/ID3V2.mp3", "1/10", "ID3V2.3 Album", "ID3V2.3 Artist", "ID3V2.3 Lyricist", "ID3V2.3 Composer", null, "Blues", "ID3V2.3 Title", "1234", "313", "1"}, - {"/sdcard/metaDataTestMedias/MP3/ID3V1.mp3", null, "test ID3V1 Album", "test ID3V1 Artist", + {"/sdcard/media_api/metaDataTestMedias/MP3/ID3V1.mp3", null, "test ID3V1 Album", "test ID3V1 Artist", null, null, null, null, "test ID3V1 Title", "1234", "231332", "1"}, - {"/sdcard/metaDataTestMedias/MP3/Corrupted_ID3V1.mp3" , null, null, null, + {"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V1.mp3" , null, null, null, null, null, null, null, null, null, "231330", "1"}, //The corrupted TALB field in id3v2 would not switch to id3v1 tag automatically - {"/sdcard/metaDataTestMedias/MP3/Corrupted_ID3V2_TALB.mp3", "01", null, "ID3V2.3 Artist", + {"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TALB.mp3", "01", null, "ID3V2.3 Artist", "ID3V2.3 Lyricist", "ID3V2.3 Composer", null, "Blues", "ID3V2.3 Title", "1234", "321", "1"}, - {"/sdcard/metaDataTestMedias/MP3/Corrupted_ID3V2_TCOM.mp3", "01", "ID3V2.3 Album", + {"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TCOM.mp3", "01", "ID3V2.3 Album", "ID3V2.3 Artist", "ID3V2.3 Lyricist", null, null, "Blues", "ID3V2.3 Title", "1234", "321", "1"}, - {"/sdcard/metaDataTestMedias/MP3/Corrupted_ID3V2_TCOM_2.mp3", "01", "ID3V2.3 Album", + {"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TCOM_2.mp3", "01", "ID3V2.3 Album", "ID3V2.3 Artist", null, null, null, "Blues", "ID3V2.3 Title", "1234", "321", "1"}, - {"/sdcard/metaDataTestMedias/MP3/Corrupted_ID3V2_TRCK.mp3", "dd", "ID3V2.3 Album", + {"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TRCK.mp3", "dd", "ID3V2.3 Album", "ID3V2.3 Artist", "ID3V2.3 Lyricist", "ID3V2.3 Composer", null, "Blues", "ID3V2.3 Title", "1234", "321", "1"}, - {"/sdcard/metaDataTestMedias/MP3/Corrupted_ID3V2_TRCK_2.mp3", "01", "ID3V2.3 Album", + {"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TRCK_2.mp3", "01", "ID3V2.3 Album", "ID3V2.3 Artist", null, null, null, null, "ID3V2.3 Title", null, "321", "1"}, - {"/sdcard/metaDataTestMedias/MP3/Corrupted_ID3V2_TYER.mp3", "01", "ID3V2.3 Album", + {"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TYER.mp3", "01", "ID3V2.3 Album", "ID3V2.3 Artist", null, null, null, null, "ID3V2.3 Title", "9999", "321", "1"}, - {"/sdcard/metaDataTestMedias/MP3/Corrupted_ID3V2_TYER_2.mp3", "01", "ID3V2.3 Album", + {"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TYER_2.mp3", "01", "ID3V2.3 Album", "ID3V2.3 Artist", "ID3V2.3 Lyricist", "ID3V2.3 Composer", null, "Blues", "ID3V2.3 Title", null, "321", "1"}, - {"/sdcard/metaDataTestMedias/MP3/Corrupted_ID3V2_TIT.mp3", null, null, null, + {"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TIT.mp3", null, null, null, null, null, null, null, null, null, "577", "1"} }; public static final String META_DATA_OTHERS [][] = { - {"/sdcard/metaDataTestMedias/3GP/cat.3gp", null, null, null, + {"/sdcard/media_api/metaDataTestMedias/3GP/cat.3gp", null, null, null, null, null, "20080309T002415.000Z", null, null, null, "1404928", "2"}, - {"/sdcard/metaDataTestMedias/AMR/AMR_NB.amr", null, null, null, + {"/sdcard/media_api/metaDataTestMedias/AMR/AMR_NB.amr", null, null, null, null, null, null, null, null, null, "126540", "1"}, - {"/sdcard/metaDataTestMedias/AMRWB/AMR_WB.amr", null, null, null, + {"/sdcard/media_api/metaDataTestMedias/AMRWB/AMR_WB.amr", null, null, null, null, null, null, null, null, null, "231180", "1"}, - {"/sdcard/metaDataTestMedias/M4A/Jaws Of Life_ver1.m4a", null, "Suspended Animation", + {"/sdcard/media_api/metaDataTestMedias/M4A/Jaws Of Life_ver1.m4a", null, "Suspended Animation", "John Petrucci", null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1"}, - {"/sdcard/metaDataTestMedias/M4V/sample_iPod.m4v", null, null, + {"/sdcard/media_api/metaDataTestMedias/M4V/sample_iPod.m4v", null, null, null, null, null, "20051220T202015.000Z", null, null, null, "3771392", "2"}, - {"/sdcard/metaDataTestMedias/MIDI/MIDI.mid", null, "Suspended Animation", + {"/sdcard/media_api/metaDataTestMedias/MIDI/MIDI.mid", null, "Suspended Animation", "John Petrucci", null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1"}, - {"/sdcard/metaDataTestMedias/MP4/kung_fu_panda_h264.mp4", null, "mp4 album Kung Fu Panda", + {"/sdcard/media_api/metaDataTestMedias/MP4/kung_fu_panda_h264.mp4", null, "mp4 album Kung Fu Panda", "mp4 artist Kung Fu Panda", null, null, "20080517T091451.000Z", "Kung Fu Panda", "Kung Fu Panda", "2008", "5667840", "2"}, - {"/sdcard/metaDataTestMedias/OGG/Ring_Classic_02.ogg", null, "Suspended Animation", + {"/sdcard/media_api/metaDataTestMedias/OGG/Ring_Classic_02.ogg", null, "Suspended Animation", "John Petrucci", null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1"}, - {"/sdcard/metaDataTestMedias/OGG/When You Say Nothing At All.ogg", + {"/sdcard/media_api/metaDataTestMedias/OGG/When You Say Nothing At All.ogg", null, "Suspended Animation", "John Petrucci", null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1"}, - {"/sdcard/metaDataTestMedias/WAV/Im With You.wav", null, null, + {"/sdcard/media_api/metaDataTestMedias/WAV/Im With You.wav", null, null, null, null, null, null, null, null, null, "224000", "1"}, - {"/sdcard/metaDataTestMedias/WMA/WMA9.wma", "6", "Ten Songs in the Key of Betrayal", + {"/sdcard/media_api/metaDataTestMedias/WMA/WMA9.wma", "6", "Ten Songs in the Key of Betrayal", "Alien Crime Syndicate", "Alien Crime Syndicate", "wma 9 Composer", "20040521T175729.483Z", "Rock", "Run for the Money", "2004", "134479", "1"}, - {"/sdcard/metaDataTestMedias/WMA/WMA10.wma", "09", "wma 10 Album", + {"/sdcard/media_api/metaDataTestMedias/WMA/WMA10.wma", "09", "wma 10 Album", "wma 10 Album Artist", "wma 10 Artist", "wma 10 Composer", "20070705T063625.097Z", "Acid Jazz", "wma 10 Title", "2010", "126574", "1"}, - {"/sdcard/metaDataTestMedias/WMV/bugs.wmv", "8", "wmv 9 Album", + {"/sdcard/media_api/metaDataTestMedias/WMV/bugs.wmv", "8", "wmv 9 Album", null, "wmv 9 Artist ", null, "20051122T155247.540Z", null, "Looney Tunes - Hare-Breadth Hurry", "2005", "193482", "2"}, - {"/sdcard/metaDataTestMedias/WMV/clips_ver7.wmv", "50", "wmv 7 Album", + {"/sdcard/media_api/metaDataTestMedias/WMV/clips_ver7.wmv", "50", "wmv 7 Album", null, "Hallau Shoots & Company", null, "20020226T170045.891Z", null, "CODEC Shootout", "1986", "43709", "2"} }; @@ -471,9 +473,25 @@ public class MediaNames { public static final String RECORDED_VIDEO_3GP = "/sdcard/temp.3gp"; - - public static final long RECORDED_TIME = 3000; - public static final long VALID_VIDEO_DURATION = 2000; - + public static final String INVALD_VIDEO_PATH = "/sdcard/media_api/filepathdoesnotexist" + + "/filepathdoesnotexist/temp.3gp"; + + public static final long RECORDED_TIME = 5000; + public static final long VALID_VIDEO_DURATION = 2000; + + //Videos for the mediaplayer stress test + public static String[] H263_STRESS = { + "/sdcard/media_api/video_stress/h263/H263_CIF.3gp", + "/sdcard/media_api/video_stress/h263/H263_QCIF.3gp", + "/sdcard/media_api/video_stress/h263/H263_QVGA.3gp", + "/sdcard/media_api/video_stress/h263/H263_SQVGA.3gp" + }; + + public static String[] MPEG4_STRESS = { + "/sdcard/media_api/video_stress/h263/mpeg4_CIF.mp4", + "/sdcard/media_api/video_stress/h263/mpeg4_QCIF.3gp", + "/sdcard/media_api/video_stress/h263/mpeg4_QVGA.3gp", + "/sdcard/media_api/video_stress/h263/mpeg4_SQVGA.mp4" + }; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java new file mode 100755 index 0000000000000..12eacd3402b91 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest; + +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; +import com.android.mediaframeworktest.stress.MediaRecorderStressTest; + +import junit.framework.TestSuite; + +public class MediaRecorderStressTestRunner extends InstrumentationTestRunner { + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(MediaRecorderStressTest.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return MediaRecorderStressTestRunner.class.getClassLoader(); + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java new file mode 100644 index 0000000000000..ae6a834fa66f2 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; + +import android.media.AudioTrack; +import android.content.Context; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2 { + private String TAG = "MediaAudioTrack"; + + public MediaAudioTrackTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + //Test case 1: Set the invalid volume + @MediumTest + public void testGetMinVolume() throws Exception { + //To Do: Create the test case for GetMinVolume + assertTrue("testGetMinVolume", true); + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java index 20f213ee9558d..dd94164d95ce4 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java @@ -385,9 +385,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase { private String TAG = "MediaRecorderTest"; @@ -60,12 +59,12 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase { + + + private String TAG = "MediaRecorderStressTest"; + private MediaRecorder mRecorder; + private Camera mCamera; + + private static final int NUMBER_OF_CAMERA_STRESS_LOOPS = 100; + private static final int NUMBER_OF_RECORDER_STRESS_LOOPS = 100; + private static final int NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS = 50; + private static final int NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER = 200; + private static final long WAIT_TIME_CAMERA_TEST = 3000; // 3 second + private static final long WAIT_TIME_RECORDER_TEST = 60000; // 6 second + private static final long WAIT_TIME_RECORD = 100000; // 10 seconds + private static final long WAIT_TIME_PLAYBACK = 60000; // 6 second + private static final String OUTPUT_FILE = "/sdcard/temp"; + private static final String OUTPUT_FILE_EXT = ".3gp"; + + public MediaRecorderStressTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + protected void setUp() throws Exception { + getActivity(); + super.setUp(); + } + + //Test case for stressing the camera preview. + @LargeTest + public void testStressCamera() throws Exception { + SurfaceHolder mSurfaceHolder; + mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + try { + Log.v(TAG, "Start preview"); + for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++){ + mCamera = Camera.open(); + mCamera.setPreviewDisplay(mSurfaceHolder); + mCamera.startPreview(); + Thread.sleep(WAIT_TIME_CAMERA_TEST); + mCamera.stopPreview(); + mCamera.release(); + } + } catch (Exception e) { + Log.v(TAG, e.toString()); + } + } + + //Test case for stressing the camera preview. + @LargeTest + public void testStressRecorder() throws Exception { + String filename; + SurfaceHolder mSurfaceHolder; + mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + try { + Log.v(TAG, "Start preview"); + for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++){ + Log.v(TAG, "counter = " + i); + filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT; + Log.v(TAG, filename); + mRecorder = new MediaRecorder(); + mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); + mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + mRecorder.setOutputFile(filename); + mRecorder.setVideoFrameRate(20); + mRecorder.setVideoSize(176,144); + Log.v(TAG, "setEncoder"); + mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); + mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + Log.v(TAG, "setPreview"); + mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); + Log.v(TAG, "prepare"); + mRecorder.prepare(); + Log.v(TAG, "before release"); + Thread.sleep(WAIT_TIME_RECORDER_TEST); + mRecorder.reset(); + mRecorder.release(); + } + } catch (Exception e) { + Log.v(TAG, e.toString()); + } + } + + + //Stress test case for switching camera and video recorder preview. + @LargeTest + public void testStressCameraSwitchRecorder() throws Exception { + String filename; + SurfaceHolder mSurfaceHolder; + mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + try { + Log.v(TAG, "Start preview"); + for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++){ + mCamera = Camera.open(); + mCamera.setPreviewDisplay(mSurfaceHolder); + mCamera.startPreview(); + Thread.sleep(WAIT_TIME_CAMERA_TEST); + mCamera.stopPreview(); + mCamera.release(); + mCamera = null; + Log.v(TAG, "release camera"); + filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT; + Log.v(TAG, filename); + mRecorder = new MediaRecorder(); + mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); + mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + mRecorder.setOutputFile(filename); + mRecorder.setVideoFrameRate(20); + mRecorder.setVideoSize(176,144); + Log.v(TAG, "Media recorder setEncoder"); + mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); + Log.v(TAG, "mediaRecorder setPreview"); + mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); + Log.v(TAG, "prepare"); + mRecorder.prepare(); + Log.v(TAG, "before release"); + Thread.sleep(WAIT_TIME_CAMERA_TEST); + mRecorder.release(); + Log.v(TAG, "release video recorder"); + } + } catch (Exception e) { + Log.v(TAG, e.toString()); + } + } + + //Stress test case for record a video and play right away. + @LargeTest + public void testStressRecordVideoAndPlayback() throws Exception { + String filename; + SurfaceHolder mSurfaceHolder; + mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + try { + for (int i = 0; i < NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS; i++){ + filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT; + Log.v(TAG, filename); + mRecorder = new MediaRecorder(); + mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); + mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); + mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + mRecorder.setOutputFile(filename); + mRecorder.setVideoFrameRate(20); + mRecorder.setVideoSize(352,288); + mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); + mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + Log.v(TAG, "mediaRecorder setPreview"); + mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); + mRecorder.prepare(); + mRecorder.start(); + Thread.sleep(WAIT_TIME_RECORD); + Log.v(TAG, "Before stop"); + mRecorder.stop(); + mRecorder.release(); + //start the playback + MediaPlayer mp = new MediaPlayer(); + mp.setDataSource(filename); + mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); + mp.prepare(); + mp.start(); + Thread.sleep(WAIT_TIME_PLAYBACK); + mp.release(); + } + } catch (Exception e) { + Log.v(TAG, e.toString()); + } + } +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java index 366b6ffce7a1b..dfd544a45e1f2 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderPrepareStateUnitTest.java @@ -59,9 +59,6 @@ public class MediaRecorderPrepareStateUnitTest extends AndroidTestCase implement } } - //TODO(elaurent) - //reactivate the test until bug#1495237 fix - @Suppress @MediumTest public void testPrepare() { mTestTemplate.runTestOnMethod(this); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderResetStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderResetStateUnitTest.java index a45f7baa2e47f..cae9e31c3cadf 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderResetStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderResetStateUnitTest.java @@ -54,9 +54,6 @@ public class MediaRecorderResetStateUnitTest extends AndroidTestCase implements recorder.reset(); } - //TODO(elaurent) - //reactivate the test until bug#1495237 fix - @Suppress @MediumTest public void testReset() { mTestTemplate.runTestOnMethod(this); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioEncoderStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioEncoderStateUnitTest.java index f17d017d8325f..4b5a8183087b4 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioEncoderStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioEncoderStateUnitTest.java @@ -54,9 +54,6 @@ public class MediaRecorderSetAudioEncoderStateUnitTest extends AndroidTestCase i recorder.setAudioEncoder(MediaRecorderStateUnitTestTemplate.AUDIO_ENCODER); } - //TODO(elaurent) - //reactivate the test until bug#1495237 fix - @Suppress @MediumTest public void testSetAudioEncoder() { mTestTemplate.runTestOnMethod(this); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioSourceStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioSourceStateUnitTest.java index a972daeae4520..f8ab48cf178e2 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioSourceStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetAudioSourceStateUnitTest.java @@ -54,9 +54,6 @@ public class MediaRecorderSetAudioSourceStateUnitTest extends AndroidTestCase im recorder.setAudioSource(MediaRecorderStateUnitTestTemplate.AUDIO_SOURCE); } - //TODO(elaurent) - //reactivate the test until bug#1495237 fix - @Suppress @MediumTest public void testSetAudioSource() { mTestTemplate.runTestOnMethod(this); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java index b5e7bb79d6403..50e235b47ce94 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFileStateUnitTest.java @@ -53,9 +53,6 @@ public class MediaRecorderSetOutputFileStateUnitTest extends AndroidTestCase imp recorder.setOutputFile(MediaRecorderStateUnitTestTemplate.RECORD_OUTPUT_PATH); } - //TODO(elaurent) - //reactivate the test until bug#1495237 fix - @Suppress @MediumTest public void testSetOutputFile() { mTestTemplate.runTestOnMethod(this); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFormatStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFormatStateUnitTest.java index 3d6f87fa69230..cacdd87557c0c 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFormatStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderSetOutputFormatStateUnitTest.java @@ -54,9 +54,6 @@ public class MediaRecorderSetOutputFormatStateUnitTest extends AndroidTestCase i recorder.setOutputFormat(MediaRecorderStateUnitTestTemplate.OUTPUT_FORMAT); } - //TODO(elaurent) - //reactivate the test until bug#1495237 fix - @Suppress @MediumTest public void testSetOutputFormat() { mTestTemplate.runTestOnMethod(this); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStartStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStartStateUnitTest.java index 03180d56b3df5..d1232fcfba400 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStartStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStartStateUnitTest.java @@ -54,9 +54,6 @@ public class MediaRecorderStartStateUnitTest extends AndroidTestCase implements recorder.start(); } - //TODO(elaurent) - //reactivate the test until bug#1495237 fix - @Suppress @MediumTest public void testStart() { mTestTemplate.runTestOnMethod(this); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStopStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStopStateUnitTest.java index 330e8abeb3f3a..8737595406846 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStopStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaRecorderStopStateUnitTest.java @@ -54,9 +54,6 @@ public class MediaRecorderStopStateUnitTest extends AndroidTestCase implements M recorder.stop(); } - //TODO(elaurent) - //reactivate the test until bug#1495237 fix - @Suppress @MediumTest public void testStop() { mTestTemplate.runTestOnMethod(this); diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h new file mode 100644 index 0000000000000..c269976f4ad31 --- /dev/null +++ b/opengl/include/EGL/egl.h @@ -0,0 +1,330 @@ +/* -*- mode: c; tab-width: 8; -*- */ +/* vi: set sw=4 ts=8: */ +/* Reference version of egl.h for EGL 1.4. + * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ + */ + +/* +** Copyright (c) 2007-2009 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#ifndef __egl_h_ +#define __egl_h_ + +/* All platform-dependent types and macro boilerplate (such as EGLAPI + * and EGLAPIENTRY) should go in eglplatform.h. + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* EGL Types */ +/* EGLint is defined in eglplatform.h */ +typedef unsigned int EGLBoolean; +typedef unsigned int EGLenum; +typedef void *EGLConfig; +typedef void *EGLContext; +typedef void *EGLDisplay; +typedef void *EGLSurface; +typedef void *EGLClientBuffer; + +/* EGL Versioning */ +#define EGL_VERSION_1_0 1 +#define EGL_VERSION_1_1 1 +#define EGL_VERSION_1_2 1 +#define EGL_VERSION_1_3 1 +#define EGL_VERSION_1_4 1 + +/* EGL Enumerants. Bitmasks and other exceptional cases aside, most + * enums are assigned unique values starting at 0x3000. + */ + +/* EGL aliases */ +#define EGL_FALSE 0 +#define EGL_TRUE 1 + +/* Out-of-band handle values */ +#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0) +#define EGL_NO_CONTEXT ((EGLContext)0) +#define EGL_NO_DISPLAY ((EGLDisplay)0) +#define EGL_NO_SURFACE ((EGLSurface)0) + +/* Out-of-band attribute value */ +#define EGL_DONT_CARE ((EGLint)-1) + +/* Errors / GetError return values */ +#define EGL_SUCCESS 0x3000 +#define EGL_NOT_INITIALIZED 0x3001 +#define EGL_BAD_ACCESS 0x3002 +#define EGL_BAD_ALLOC 0x3003 +#define EGL_BAD_ATTRIBUTE 0x3004 +#define EGL_BAD_CONFIG 0x3005 +#define EGL_BAD_CONTEXT 0x3006 +#define EGL_BAD_CURRENT_SURFACE 0x3007 +#define EGL_BAD_DISPLAY 0x3008 +#define EGL_BAD_MATCH 0x3009 +#define EGL_BAD_NATIVE_PIXMAP 0x300A +#define EGL_BAD_NATIVE_WINDOW 0x300B +#define EGL_BAD_PARAMETER 0x300C +#define EGL_BAD_SURFACE 0x300D +#define EGL_CONTEXT_LOST 0x300E /* EGL 1.1 - IMG_power_management */ + +/* Reserved 0x300F-0x301F for additional errors */ + +/* Config attributes */ +#define EGL_BUFFER_SIZE 0x3020 +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_BLUE_SIZE 0x3022 +#define EGL_GREEN_SIZE 0x3023 +#define EGL_RED_SIZE 0x3024 +#define EGL_DEPTH_SIZE 0x3025 +#define EGL_STENCIL_SIZE 0x3026 +#define EGL_CONFIG_CAVEAT 0x3027 +#define EGL_CONFIG_ID 0x3028 +#define EGL_LEVEL 0x3029 +#define EGL_MAX_PBUFFER_HEIGHT 0x302A +#define EGL_MAX_PBUFFER_PIXELS 0x302B +#define EGL_MAX_PBUFFER_WIDTH 0x302C +#define EGL_NATIVE_RENDERABLE 0x302D +#define EGL_NATIVE_VISUAL_ID 0x302E +#define EGL_NATIVE_VISUAL_TYPE 0x302F +#define EGL_PRESERVED_RESOURCES 0x3030 +#define EGL_SAMPLES 0x3031 +#define EGL_SAMPLE_BUFFERS 0x3032 +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_TRANSPARENT_TYPE 0x3034 +#define EGL_TRANSPARENT_BLUE_VALUE 0x3035 +#define EGL_TRANSPARENT_GREEN_VALUE 0x3036 +#define EGL_TRANSPARENT_RED_VALUE 0x3037 +#define EGL_NONE 0x3038 /* Attrib list terminator */ +#define EGL_BIND_TO_TEXTURE_RGB 0x3039 +#define EGL_BIND_TO_TEXTURE_RGBA 0x303A +#define EGL_MIN_SWAP_INTERVAL 0x303B +#define EGL_MAX_SWAP_INTERVAL 0x303C +#define EGL_LUMINANCE_SIZE 0x303D +#define EGL_ALPHA_MASK_SIZE 0x303E +#define EGL_COLOR_BUFFER_TYPE 0x303F +#define EGL_RENDERABLE_TYPE 0x3040 +#define EGL_MATCH_NATIVE_PIXMAP 0x3041 /* Pseudo-attribute (not queryable) */ +#define EGL_CONFORMANT 0x3042 + +/* Reserved 0x3041-0x304F for additional config attributes */ + +/* Config attribute values */ +#define EGL_SLOW_CONFIG 0x3050 /* EGL_CONFIG_CAVEAT value */ +#define EGL_NON_CONFORMANT_CONFIG 0x3051 /* EGL_CONFIG_CAVEAT value */ +#define EGL_TRANSPARENT_RGB 0x3052 /* EGL_TRANSPARENT_TYPE value */ +#define EGL_RGB_BUFFER 0x308E /* EGL_COLOR_BUFFER_TYPE value */ +#define EGL_LUMINANCE_BUFFER 0x308F /* EGL_COLOR_BUFFER_TYPE value */ + +/* More config attribute values, for EGL_TEXTURE_FORMAT */ +#define EGL_NO_TEXTURE 0x305C +#define EGL_TEXTURE_RGB 0x305D +#define EGL_TEXTURE_RGBA 0x305E +#define EGL_TEXTURE_2D 0x305F + +/* Config attribute mask bits */ +#define EGL_PBUFFER_BIT 0x0001 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_PIXMAP_BIT 0x0002 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_WINDOW_BIT 0x0004 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_VG_COLORSPACE_LINEAR_BIT 0x0020 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_VG_ALPHA_FORMAT_PRE_BIT 0x0040 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400 /* EGL_SURFACE_TYPE mask bits */ + +#define EGL_OPENGL_ES_BIT 0x0001 /* EGL_RENDERABLE_TYPE mask bits */ +#define EGL_OPENVG_BIT 0x0002 /* EGL_RENDERABLE_TYPE mask bits */ +#define EGL_OPENGL_ES2_BIT 0x0004 /* EGL_RENDERABLE_TYPE mask bits */ +#define EGL_OPENGL_BIT 0x0008 /* EGL_RENDERABLE_TYPE mask bits */ + +/* QueryString targets */ +#define EGL_VENDOR 0x3053 +#define EGL_VERSION 0x3054 +#define EGL_EXTENSIONS 0x3055 +#define EGL_CLIENT_APIS 0x308D + +/* QuerySurface / SurfaceAttrib / CreatePbufferSurface targets */ +#define EGL_HEIGHT 0x3056 +#define EGL_WIDTH 0x3057 +#define EGL_LARGEST_PBUFFER 0x3058 +#define EGL_TEXTURE_FORMAT 0x3080 +#define EGL_TEXTURE_TARGET 0x3081 +#define EGL_MIPMAP_TEXTURE 0x3082 +#define EGL_MIPMAP_LEVEL 0x3083 +#define EGL_RENDER_BUFFER 0x3086 +#define EGL_VG_COLORSPACE 0x3087 +#define EGL_VG_ALPHA_FORMAT 0x3088 +#define EGL_HORIZONTAL_RESOLUTION 0x3090 +#define EGL_VERTICAL_RESOLUTION 0x3091 +#define EGL_PIXEL_ASPECT_RATIO 0x3092 +#define EGL_SWAP_BEHAVIOR 0x3093 +#define EGL_MULTISAMPLE_RESOLVE 0x3099 + +/* EGL_RENDER_BUFFER values / BindTexImage / ReleaseTexImage buffer targets */ +#define EGL_BACK_BUFFER 0x3084 +#define EGL_SINGLE_BUFFER 0x3085 + +/* OpenVG color spaces */ +#define EGL_VG_COLORSPACE_sRGB 0x3089 /* EGL_VG_COLORSPACE value */ +#define EGL_VG_COLORSPACE_LINEAR 0x308A /* EGL_VG_COLORSPACE value */ + +/* OpenVG alpha formats */ +#define EGL_VG_ALPHA_FORMAT_NONPRE 0x308B /* EGL_ALPHA_FORMAT value */ +#define EGL_VG_ALPHA_FORMAT_PRE 0x308C /* EGL_ALPHA_FORMAT value */ + +/* Constant scale factor by which fractional display resolutions & + * aspect ratio are scaled when queried as integer values. + */ +#define EGL_DISPLAY_SCALING 10000 + +/* Unknown display resolution/aspect ratio */ +#define EGL_UNKNOWN ((EGLint)-1) + +/* Back buffer swap behaviors */ +#define EGL_BUFFER_PRESERVED 0x3094 /* EGL_SWAP_BEHAVIOR value */ +#define EGL_BUFFER_DESTROYED 0x3095 /* EGL_SWAP_BEHAVIOR value */ + +/* CreatePbufferFromClientBuffer buffer types */ +#define EGL_OPENVG_IMAGE 0x3096 + +/* QueryContext targets */ +#define EGL_CONTEXT_CLIENT_TYPE 0x3097 + +/* CreateContext attributes */ +#define EGL_CONTEXT_CLIENT_VERSION 0x3098 + +/* Multisample resolution behaviors */ +#define EGL_MULTISAMPLE_RESOLVE_DEFAULT 0x309A /* EGL_MULTISAMPLE_RESOLVE value */ +#define EGL_MULTISAMPLE_RESOLVE_BOX 0x309B /* EGL_MULTISAMPLE_RESOLVE value */ + +/* BindAPI/QueryAPI targets */ +#define EGL_OPENGL_ES_API 0x30A0 +#define EGL_OPENVG_API 0x30A1 +#define EGL_OPENGL_API 0x30A2 + +/* GetCurrentSurface targets */ +#define EGL_DRAW 0x3059 +#define EGL_READ 0x305A + +/* WaitNative engines */ +#define EGL_CORE_NATIVE_ENGINE 0x305B + +/* EGL 1.2 tokens renamed for consistency in EGL 1.3 */ +#define EGL_COLORSPACE EGL_VG_COLORSPACE +#define EGL_ALPHA_FORMAT EGL_VG_ALPHA_FORMAT +#define EGL_COLORSPACE_sRGB EGL_VG_COLORSPACE_sRGB +#define EGL_COLORSPACE_LINEAR EGL_VG_COLORSPACE_LINEAR +#define EGL_ALPHA_FORMAT_NONPRE EGL_VG_ALPHA_FORMAT_NONPRE +#define EGL_ALPHA_FORMAT_PRE EGL_VG_ALPHA_FORMAT_PRE + +/* EGL extensions must request enum blocks from the Khronos + * API Registrar, who maintains the enumerant registry. Submit + * a bug in Khronos Bugzilla against task "Registry". + */ + + + +/* EGL Functions */ + +EGLAPI EGLint EGLAPIENTRY eglGetError(void); + +EGLAPI EGLDisplay EGLAPIENTRY eglGetDisplay(EGLNativeDisplayType display_id); +EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor); +EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy); + +EGLAPI const char * EGLAPIENTRY eglQueryString(EGLDisplay dpy, EGLint name); + +EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, + EGLint config_size, EGLint *num_config); +EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, + EGLint *num_config); +EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value); + +EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, + EGLNativeWindowType win, + const EGLint *attrib_list); +EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, + const EGLint *attrib_list); +EGLAPI EGLSurface EGLAPIENTRY eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, + EGLNativePixmapType pixmap, + const EGLint *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay dpy, EGLSurface surface); +EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface(EGLDisplay dpy, EGLSurface surface, + EGLint attribute, EGLint *value); + +EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api); +EGLAPI EGLenum EGLAPIENTRY eglQueryAPI(void); + +EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient(void); + +EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread(void); + +EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferFromClientBuffer( + EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint *attrib_list); + +EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, + EGLint attribute, EGLint value); +EGLAPI EGLBoolean EGLAPIENTRY eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); +EGLAPI EGLBoolean EGLAPIENTRY eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); + + +EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval); + + +EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, + EGLContext share_context, + const EGLint *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext ctx); +EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx); + +EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext(void); +EGLAPI EGLSurface EGLAPIENTRY eglGetCurrentSurface(EGLint readdraw); +EGLAPI EGLDisplay EGLAPIENTRY eglGetCurrentDisplay(void); +EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext(EGLDisplay dpy, EGLContext ctx, + EGLint attribute, EGLint *value); + +EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL(void); +EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative(EGLint engine); +EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers(EGLDisplay dpy, EGLSurface surface); +EGLAPI EGLBoolean EGLAPIENTRY eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, + EGLNativePixmapType target); + +/* This is a generic function pointer type, whose name indicates it must + * be cast to the proper type *and calling convention* before use. + */ +typedef void (*__eglMustCastToProperFunctionPointerType)(void); + +/* Now, define eglGetProcAddress using the generic function ptr. type */ +EGLAPI __eglMustCastToProperFunctionPointerType EGLAPIENTRY + eglGetProcAddress(const char *procname); + +#ifdef __cplusplus +} +#endif + +#endif /* __egl_h_ */ diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h new file mode 100644 index 0000000000000..25cfcb832fb29 --- /dev/null +++ b/opengl/include/EGL/eglext.h @@ -0,0 +1,138 @@ +#ifndef __eglext_h_ +#define __eglext_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2007-2009 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#include + +/*************************************************************/ + +/* Header file version number */ +/* Current version at http://www.khronos.org/registry/egl/ */ +/* $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ */ +#define EGL_EGLEXT_VERSION 3 + +#ifndef EGL_KHR_config_attribs +#define EGL_KHR_config_attribs 1 +#define EGL_CONFORMANT_KHR 0x3042 /* EGLConfig attribute */ +#define EGL_VG_COLORSPACE_LINEAR_BIT_KHR 0x0020 /* EGL_SURFACE_TYPE bitfield */ +#define EGL_VG_ALPHA_FORMAT_PRE_BIT_KHR 0x0040 /* EGL_SURFACE_TYPE bitfield */ +#endif + +#ifndef EGL_KHR_lock_surface +#define EGL_KHR_lock_surface 1 +#define EGL_READ_SURFACE_BIT_KHR 0x0001 /* EGL_LOCK_USAGE_HINT_KHR bitfield */ +#define EGL_WRITE_SURFACE_BIT_KHR 0x0002 /* EGL_LOCK_USAGE_HINT_KHR bitfield */ +#define EGL_LOCK_SURFACE_BIT_KHR 0x0080 /* EGL_SURFACE_TYPE bitfield */ +#define EGL_OPTIMAL_FORMAT_BIT_KHR 0x0100 /* EGL_SURFACE_TYPE bitfield */ +#define EGL_MATCH_FORMAT_KHR 0x3043 /* EGLConfig attribute */ +#define EGL_FORMAT_RGB_565_EXACT_KHR 0x30C0 /* EGL_MATCH_FORMAT_KHR value */ +#define EGL_FORMAT_RGB_565_KHR 0x30C1 /* EGL_MATCH_FORMAT_KHR value */ +#define EGL_FORMAT_RGBA_8888_EXACT_KHR 0x30C2 /* EGL_MATCH_FORMAT_KHR value */ +#define EGL_FORMAT_RGBA_8888_KHR 0x30C3 /* EGL_MATCH_FORMAT_KHR value */ +#define EGL_MAP_PRESERVE_PIXELS_KHR 0x30C4 /* eglLockSurfaceKHR attribute */ +#define EGL_LOCK_USAGE_HINT_KHR 0x30C5 /* eglLockSurfaceKHR attribute */ +#define EGL_BITMAP_POINTER_KHR 0x30C6 /* eglQuerySurface attribute */ +#define EGL_BITMAP_PITCH_KHR 0x30C7 /* eglQuerySurface attribute */ +#define EGL_BITMAP_ORIGIN_KHR 0x30C8 /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_RED_OFFSET_KHR 0x30C9 /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR 0x30CA /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR 0x30CB /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR 0x30CC /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR 0x30CD /* eglQuerySurface attribute */ +#define EGL_LOWER_LEFT_KHR 0x30CE /* EGL_BITMAP_ORIGIN_KHR value */ +#define EGL_UPPER_LEFT_KHR 0x30CF /* EGL_BITMAP_ORIGIN_KHR value */ +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglLockSurfaceKHR (EGLDisplay display, EGLSurface surface, const EGLint *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglUnlockSurfaceKHR (EGLDisplay display, EGLSurface surface); +#endif /* EGL_EGLEXT_PROTOTYPES */ +typedef EGLBoolean (EGLAPIENTRYP PFNEGLLOCKSURFACEKHRPROC) (EGLDisplay display, EGLSurface surface, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNLOCKSURFACEKHRPROC) (EGLDisplay display, EGLSurface surface); +#endif + +#ifndef EGL_KHR_image +#define EGL_KHR_image 1 +#define EGL_NATIVE_PIXMAP_KHR 0x30B0 /* eglCreateImageKHR target */ +typedef void *EGLImageKHR; +#define EGL_NO_IMAGE_KHR ((EGLImageKHR)0) +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImageKHR (EGLDisplay dpy, EGLImageKHR image); +#endif /* EGL_EGLEXT_PROTOTYPES */ +typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImageKHR image); +#endif + +#ifndef EGL_KHR_vg_parent_image +#define EGL_KHR_vg_parent_image 1 +#define EGL_VG_PARENT_IMAGE_KHR 0x30BA /* eglCreateImageKHR target */ +#endif + +#ifndef EGL_KHR_gl_texture_2D_image +#define EGL_KHR_gl_texture_2D_image 1 +#define EGL_GL_TEXTURE_2D_KHR 0x30B1 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_LEVEL_KHR 0x30BC /* eglCreateImageKHR attribute */ +#endif + +#ifndef EGL_KHR_gl_texture_cubemap_image +#define EGL_KHR_gl_texture_cubemap_image 1 +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR 0x30B3 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR 0x30B4 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR 0x30B5 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR 0x30B6 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR 0x30B7 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR 0x30B8 /* eglCreateImageKHR target */ +#endif + +#ifndef EGL_KHR_gl_texture_3D_image +#define EGL_KHR_gl_texture_3D_image 1 +#define EGL_GL_TEXTURE_3D_KHR 0x30B2 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_ZOFFSET_KHR 0x30BD /* eglCreateImageKHR attribute */ +#endif + +#ifndef EGL_KHR_gl_renderbuffer_image +#define EGL_KHR_gl_renderbuffer_image 1 +#define EGL_GL_RENDERBUFFER_KHR 0x30B9 /* eglCreateImageKHR target */ +#endif + +#ifndef EGL_KHR_image_base +#define EGL_KHR_image_base 1 +/* Most interfaces defined by EGL_KHR_image_pixmap above */ +#define EGL_IMAGE_PRESERVED_KHR 0x30D2 /* eglCreateImageKHR attribute */ +#endif + +#ifndef EGL_KHR_image_pixmap +#define EGL_KHR_image_pixmap 1 +/* Interfaces defined by EGL_KHR_image above */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/GLES/eglnatives.h b/opengl/include/EGL/eglnatives.h similarity index 86% rename from include/GLES/eglnatives.h rename to opengl/include/EGL/eglnatives.h index 1cd57d0684e8b..21622dc5a68c4 100644 --- a/include/GLES/eglnatives.h +++ b/opengl/include/EGL/eglnatives.h @@ -22,24 +22,9 @@ #ifdef __cplusplus extern "C" { #endif + /*****************************************************************************/ -struct egl_native_window_t; -struct egl_native_pixmap_t; - - -typedef struct egl_native_window_t* NativeWindowType; -typedef struct egl_native_pixmap_t* NativePixmapType; -typedef void* NativeDisplayType; - -/* - * This a convenience function to create a NativeWindowType surface - * that maps to the whole screen - * This function is actually implemented in libui.so - */ - -NativeWindowType android_createDisplaySurface(); - /* flags returned from swapBuffer */ #define EGL_NATIVES_FLAG_SIZE_CHANGED 0x00000001 @@ -151,48 +136,46 @@ struct egl_native_window_t /* * Hook called by EGL to hold a reference on this structure */ - void (*incRef)(NativeWindowType window); + void (*incRef)(struct egl_native_window_t* window); /* * Hook called by EGL to release a reference on this structure */ - void (*decRef)(NativeWindowType window); + void (*decRef)(struct egl_native_window_t* window); /* * Hook called by EGL to perform a page flip. This function * may update the size attributes above, in which case it returns * the EGL_NATIVES_FLAG_SIZE_CHANGED bit set. */ - uint32_t (*swapBuffers)(NativeWindowType window); + uint32_t (*swapBuffers)(struct egl_native_window_t* window); - /* - * Hook called by EGL to set the swap rectangle. this hook can be - * null (operation not supported) - */ - void (*setSwapRectangle)(NativeWindowType window, int l, int t, int w, int h); - /* * Reserved for future use. MUST BE ZERO. */ void (*reserved_proc_0)(void); - + + /* + * Reserved for future use. MUST BE ZERO. + */ + void (*reserved_proc_1)(void); /* - * Hook called by EGL to retrieve the next buffer to render into. - * This call updates this structure. + * Reserved for future use. MUST BE ZERO. */ - uint32_t (*nextBuffer)(NativeWindowType window); + void (*reserved_proc_2)(void); + /* * Hook called by EGL when the native surface is associated to EGL * (eglCreateWindowSurface). Can be NULL. */ - void (*connect)(NativeWindowType window); + void (*connect)(struct egl_native_window_t* window); /* * Hook called by EGL when eglDestroySurface is called. Can be NULL. */ - void (*disconnect)(NativeWindowType window); + void (*disconnect)(struct egl_native_window_t* window); /* * Reserved for future use. MUST BE ZERO. @@ -224,6 +207,17 @@ struct egl_native_pixmap_t /*****************************************************************************/ +/* + * This a convenience function to create a NativeWindowType surface + * that maps to the whole screen + * This function is actually implemented in libui.so + */ + +struct egl_native_window_t* android_createDisplaySurface(); + +/*****************************************************************************/ + + /* * OEM's egl's library (libhgl.so) must imlement these hooks to allocate * the GPU memory they need diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h new file mode 100644 index 0000000000000..ac0090102c31f --- /dev/null +++ b/opengl/include/EGL/eglplatform.h @@ -0,0 +1,117 @@ +#ifndef __eglplatform_h_ +#define __eglplatform_h_ + +/* +** Copyright (c) 2007-2009 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Platform-specific types and definitions for egl.h + * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ + * + * Adopters may modify khrplatform.h and this file to suit their platform. + * You are encouraged to submit all modifications to the Khronos group so that + * they can be included in future versions of this file. Please submit changes + * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) + * by filing a bug against product "EGL" component "Registry". + */ + +#include + +/* Macros used in EGL function prototype declarations. + * + * EGL functions should be prototyped as: + * + * EGLAPI return-type EGLAPIENTRY eglFunction(arguments); + * typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments); + * + * KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h + */ + +#ifndef EGLAPI +#define EGLAPI KHRONOS_APICALL +#endif + +#define EGLAPIENTRY KHRONOS_APIENTRY +#define EGLAPIENTRYP KHRONOS_APIENTRY* + +/* The types NativeDisplayType, NativeWindowType, and NativePixmapType + * are aliases of window-system-dependent types, such as X Display * or + * Windows Device Context. They must be defined in platform-specific + * code below. The EGL-prefixed versions of Native*Type are the same + * types, renamed in EGL 1.3 so all types in the API start with "EGL". + */ + +#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include + +typedef HDC EGLNativeDisplayType; +typedef HBITMAP EGLNativePixmapType; +typedef HWND EGLNativeWindowType; + +#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */ + +typedef int EGLNativeDisplayType; +typedef void *EGLNativeWindowType; +typedef void *EGLNativePixmapType; + +#elif defined(__unix__) && !defined(ANDROID) + +/* X11 (tentative) */ +#include +#include + +typedef Display *EGLNativeDisplayType; +typedef Pixmap EGLNativePixmapType; +typedef Window EGLNativeWindowType; + + +#elif defined(ANDROID) + +#include + +typedef struct egl_native_window_t* EGLNativeWindowType; +typedef struct egl_native_pixmap_t* EGLNativePixmapType; +typedef void* EGLNativeDisplayType; + +#else +#error "Platform not recognized" +#endif + +/* EGL 1.2 types, renamed for consistency in EGL 1.3 */ +typedef EGLNativeDisplayType NativeDisplayType; +typedef EGLNativePixmapType NativePixmapType; +typedef EGLNativeWindowType NativeWindowType; + + +/* Define EGLint. This must be a signed integral type large enough to contain + * all legal attribute names and values passed into and out of EGL, whether + * their type is boolean, bitmask, enumerant (symbolic constant), integer, + * handle, or other. While in general a 32-bit integer will suffice, if + * handles are 64 bit types, then EGLint should be defined as a signed 64-bit + * integer type. + */ +typedef khronos_int32_t EGLint; + +#endif /* __eglplatform_h */ diff --git a/opengl/include/GLES/egl.h b/opengl/include/GLES/egl.h new file mode 100644 index 0000000000000..5778e00509c27 --- /dev/null +++ b/opengl/include/GLES/egl.h @@ -0,0 +1,15 @@ +/* + * Skeleton egl.h to provide compatibility for early GLES 1.0 + * applications. Several early implementations included gl.h + * in egl.h leading applications to include only egl.h + * + * $Revision: 6252 $ on $Date:: 2008-08-06 16:35:08 -0700 #$ + */ + +#ifndef __legacy_egl_h_ +#define __legacy_egl_h_ + +#include +#include + +#endif /* __legacy_egl_h_ */ diff --git a/opengl/include/GLES/gl.h b/opengl/include/GLES/gl.h new file mode 100644 index 0000000000000..2e8b97107ac97 --- /dev/null +++ b/opengl/include/GLES/gl.h @@ -0,0 +1,769 @@ +#ifndef __gl_h_ +#define __gl_h_ + +/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +typedef void GLvoid; +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef khronos_int8_t GLbyte; +typedef short GLshort; +typedef int GLint; +typedef int GLsizei; +typedef khronos_uint8_t GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef khronos_int32_t GLfixed; +typedef khronos_int32_t GLclampx; + +typedef khronos_intptr_t GLintptr; +typedef khronos_ssize_t GLsizeiptr; + + +/*************************************************************/ + +/* OpenGL ES core versions */ +#define GL_VERSION_ES_CM_1_0 1 +#define GL_VERSION_ES_CL_1_0 1 +#define GL_VERSION_ES_CM_1_1 1 +#define GL_VERSION_ES_CL_1_1 1 + +/* ClearBufferMask */ +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 + +/* Boolean */ +#define GL_FALSE 0 +#define GL_TRUE 1 + +/* BeginMode */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 + +/* AlphaFunction */ +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 + +/* BlendingFactorDest */ +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 + +/* BlendingFactorSrc */ +/* GL_ZERO */ +/* GL_ONE */ +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +/* GL_SRC_ALPHA */ +/* GL_ONE_MINUS_SRC_ALPHA */ +/* GL_DST_ALPHA */ +/* GL_ONE_MINUS_DST_ALPHA */ + +/* ClipPlaneName */ +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 + +/* ColorMaterialFace */ +/* GL_FRONT_AND_BACK */ + +/* ColorMaterialParameter */ +/* GL_AMBIENT_AND_DIFFUSE */ + +/* ColorPointerType */ +/* GL_UNSIGNED_BYTE */ +/* GL_FLOAT */ +/* GL_FIXED */ + +/* CullFaceMode */ +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_FRONT_AND_BACK 0x0408 + +/* DepthFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* EnableCap */ +#define GL_FOG 0x0B60 +#define GL_LIGHTING 0x0B50 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_CULL_FACE 0x0B44 +#define GL_ALPHA_TEST 0x0BC0 +#define GL_BLEND 0x0BE2 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_DITHER 0x0BD0 +#define GL_STENCIL_TEST 0x0B90 +#define GL_DEPTH_TEST 0x0B71 +/* GL_LIGHT0 */ +/* GL_LIGHT1 */ +/* GL_LIGHT2 */ +/* GL_LIGHT3 */ +/* GL_LIGHT4 */ +/* GL_LIGHT5 */ +/* GL_LIGHT6 */ +/* GL_LIGHT7 */ +#define GL_POINT_SMOOTH 0x0B10 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_NORMALIZE 0x0BA1 +#define GL_RESCALE_NORMAL 0x803A +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 + +/* ErrorCode */ +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + +/* FogMode */ +/* GL_LINEAR */ +#define GL_EXP 0x0800 +#define GL_EXP2 0x0801 + +/* FogParameter */ +#define GL_FOG_DENSITY 0x0B62 +#define GL_FOG_START 0x0B63 +#define GL_FOG_END 0x0B64 +#define GL_FOG_MODE 0x0B65 +#define GL_FOG_COLOR 0x0B66 + +/* FrontFaceDirection */ +#define GL_CW 0x0900 +#define GL_CCW 0x0901 + +/* GetPName */ +#define GL_CURRENT_COLOR 0x0B00 +#define GL_CURRENT_NORMAL 0x0B02 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_LINE_WIDTH 0x0B21 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_SHADE_MODEL 0x0B54 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_MATRIX_MODE 0x0BA0 +#define GL_VIEWPORT 0x0BA2 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_ALPHA_TEST_FUNC 0x0BC1 +#define GL_ALPHA_TEST_REF 0x0BC2 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_LIGHTS 0x0D31 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 +#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 +#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_ALPHA_BITS 0x0D55 +#define GL_DEPTH_BITS 0x0D56 +#define GL_STENCIL_BITS 0x0D57 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_VERTEX_ARRAY_SIZE 0x807A +#define GL_VERTEX_ARRAY_TYPE 0x807B +#define GL_VERTEX_ARRAY_STRIDE 0x807C +#define GL_NORMAL_ARRAY_TYPE 0x807E +#define GL_NORMAL_ARRAY_STRIDE 0x807F +#define GL_COLOR_ARRAY_SIZE 0x8081 +#define GL_COLOR_ARRAY_TYPE 0x8082 +#define GL_COLOR_ARRAY_STRIDE 0x8083 +#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A +#define GL_VERTEX_ARRAY_POINTER 0x808E +#define GL_NORMAL_ARRAY_POINTER 0x808F +#define GL_COLOR_ARRAY_POINTER 0x8090 +#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB + +/* GetTextureParameter */ +/* GL_TEXTURE_MAG_FILTER */ +/* GL_TEXTURE_MIN_FILTER */ +/* GL_TEXTURE_WRAP_S */ +/* GL_TEXTURE_WRAP_T */ + +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 + +/* HintMode */ +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 + +/* HintTarget */ +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_FOG_HINT 0x0C54 +#define GL_GENERATE_MIPMAP_HINT 0x8192 + +/* LightModelParameter */ +#define GL_LIGHT_MODEL_AMBIENT 0x0B53 +#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 + +/* LightParameter */ +#define GL_AMBIENT 0x1200 +#define GL_DIFFUSE 0x1201 +#define GL_SPECULAR 0x1202 +#define GL_POSITION 0x1203 +#define GL_SPOT_DIRECTION 0x1204 +#define GL_SPOT_EXPONENT 0x1205 +#define GL_SPOT_CUTOFF 0x1206 +#define GL_CONSTANT_ATTENUATION 0x1207 +#define GL_LINEAR_ATTENUATION 0x1208 +#define GL_QUADRATIC_ATTENUATION 0x1209 + +/* DataType */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_FLOAT 0x1406 +#define GL_FIXED 0x140C + +/* LogicOp */ +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F + +/* MaterialFace */ +/* GL_FRONT_AND_BACK */ + +/* MaterialParameter */ +#define GL_EMISSION 0x1600 +#define GL_SHININESS 0x1601 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +/* GL_AMBIENT */ +/* GL_DIFFUSE */ +/* GL_SPECULAR */ + +/* MatrixMode */ +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +/* NormalPointerType */ +/* GL_BYTE */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ + +/* PixelFormat */ +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A + +/* PixelStoreParameter */ +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 + +/* PixelType */ +/* GL_UNSIGNED_BYTE */ +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 + +/* ShadingModel */ +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 + +/* StencilFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* StencilOp */ +/* GL_ZERO */ +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +/* GL_INVERT */ + +/* StringName */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +/* TexCoordPointerType */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ +/* GL_BYTE */ + +/* TextureEnvMode */ +#define GL_MODULATE 0x2100 +#define GL_DECAL 0x2101 +/* GL_BLEND */ +#define GL_ADD 0x0104 +/* GL_REPLACE */ + +/* TextureEnvParameter */ +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_ENV_COLOR 0x2201 + +/* TextureEnvTarget */ +#define GL_TEXTURE_ENV 0x2300 + +/* TextureMagFilter */ +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +/* TextureMinFilter */ +/* GL_NEAREST */ +/* GL_LINEAR */ +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 + +/* TextureParameterName */ +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_GENERATE_MIPMAP 0x8191 + +/* TextureTarget */ +/* GL_TEXTURE_2D */ + +/* TextureUnit */ +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 + +/* TextureWrapMode */ +#define GL_REPEAT 0x2901 +#define GL_CLAMP_TO_EDGE 0x812F + +/* VertexPointerType */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ +/* GL_BYTE */ + +/* LightName */ +#define GL_LIGHT0 0x4000 +#define GL_LIGHT1 0x4001 +#define GL_LIGHT2 0x4002 +#define GL_LIGHT3 0x4003 +#define GL_LIGHT4 0x4004 +#define GL_LIGHT5 0x4005 +#define GL_LIGHT6 0x4006 +#define GL_LIGHT7 0x4007 + +/* Buffer Objects */ +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 + +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A + +#define GL_STATIC_DRAW 0x88E4 +#define GL_DYNAMIC_DRAW 0x88E8 + +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 + +/* Texture combine + dot3 */ +#define GL_SUBTRACT 0x84E7 +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A + +#define GL_ALPHA_SCALE 0x0D1C + +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC1_ALPHA 0x8589 +#define GL_SRC2_ALPHA 0x858A + +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF + +/*------------------------------------------------------------------------* + * required OES extension tokens + *------------------------------------------------------------------------*/ + +/* OES_read_format */ +#ifndef GL_OES_read_format +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif + +/* GL_OES_compressed_paletted_texture */ +#ifndef GL_OES_compressed_paletted_texture +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif + +/* OES_point_size_array */ +#ifndef GL_OES_point_size_array +#define GL_POINT_SIZE_ARRAY_OES 0x8B9C +#define GL_POINT_SIZE_ARRAY_TYPE_OES 0x898A +#define GL_POINT_SIZE_ARRAY_STRIDE_OES 0x898B +#define GL_POINT_SIZE_ARRAY_POINTER_OES 0x898C +#define GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES 0x8B9F +#endif + +/* GL_OES_point_sprite */ +#ifndef GL_OES_point_sprite +#define GL_POINT_SPRITE_OES 0x8861 +#define GL_COORD_REPLACE_OES 0x8862 +#endif + +/*************************************************************/ + +/* Available only in Common profile */ +GL_API void GL_APIENTRY glAlphaFunc (GLenum func, GLclampf ref); +GL_API void GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GL_API void GL_APIENTRY glClearDepthf (GLclampf depth); +GL_API void GL_APIENTRY glClipPlanef (GLenum plane, const GLfloat *equation); +GL_API void GL_APIENTRY glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GL_API void GL_APIENTRY glDepthRangef (GLclampf zNear, GLclampf zFar); +GL_API void GL_APIENTRY glFogf (GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glFogfv (GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glFrustumf (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glGetClipPlanef (GLenum pname, GLfloat eqn[4]); +GL_API void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetLightfv (GLenum light, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetMaterialfv (GLenum face, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetTexEnvfv (GLenum env, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glLightModelf (GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glLightModelfv (GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glLightf (GLenum light, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glLightfv (GLenum light, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glLineWidth (GLfloat width); +GL_API void GL_APIENTRY glLoadMatrixf (const GLfloat *m); +GL_API void GL_APIENTRY glMaterialf (GLenum face, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glMaterialfv (GLenum face, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glMultMatrixf (const GLfloat *m); +GL_API void GL_APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GL_API void GL_APIENTRY glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz); +GL_API void GL_APIENTRY glOrthof (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glPointSize (GLfloat size); +GL_API void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); +GL_API void GL_APIENTRY glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GL_API void GL_APIENTRY glScalef (GLfloat x, GLfloat y, GLfloat z); +GL_API void GL_APIENTRY glTexEnvf (GLenum target, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glTranslatef (GLfloat x, GLfloat y, GLfloat z); + +/* Available in both Common and Common-Lite profiles */ +GL_API void GL_APIENTRY glActiveTexture (GLenum texture); +GL_API void GL_APIENTRY glAlphaFuncx (GLenum func, GLclampx ref); +GL_API void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GL_API void GL_APIENTRY glBindTexture (GLenum target, GLuint texture); +GL_API void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); +GL_API void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +GL_API void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +GL_API void GL_APIENTRY glClear (GLbitfield mask); +GL_API void GL_APIENTRY glClearColorx (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +GL_API void GL_APIENTRY glClearDepthx (GLclampx depth); +GL_API void GL_APIENTRY glClearStencil (GLint s); +GL_API void GL_APIENTRY glClientActiveTexture (GLenum texture); +GL_API void GL_APIENTRY glClipPlanex (GLenum plane, const GLfixed *equation); +GL_API void GL_APIENTRY glColor4ub (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +GL_API void GL_APIENTRY glColor4x (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GL_API void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GL_API void GL_APIENTRY glColorPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +GL_API void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +GL_API void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GL_API void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glCullFace (GLenum mode); +GL_API void GL_APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GL_API void GL_APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); +GL_API void GL_APIENTRY glDepthFunc (GLenum func); +GL_API void GL_APIENTRY glDepthMask (GLboolean flag); +GL_API void GL_APIENTRY glDepthRangex (GLclampx zNear, GLclampx zFar); +GL_API void GL_APIENTRY glDisable (GLenum cap); +GL_API void GL_APIENTRY glDisableClientState (GLenum array); +GL_API void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); +GL_API void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +GL_API void GL_APIENTRY glEnable (GLenum cap); +GL_API void GL_APIENTRY glEnableClientState (GLenum array); +GL_API void GL_APIENTRY glFinish (void); +GL_API void GL_APIENTRY glFlush (void); +GL_API void GL_APIENTRY glFogx (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glFogxv (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glFrontFace (GLenum mode); +GL_API void GL_APIENTRY glFrustumx (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean *params); +GL_API void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetClipPlanex (GLenum pname, GLfixed eqn[4]); +GL_API void GL_APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GL_API void GL_APIENTRY glGenTextures (GLsizei n, GLuint *textures); +GL_API GLenum GL_APIENTRY glGetError (void); +GL_API void GL_APIENTRY glGetFixedv (GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetIntegerv (GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetLightxv (GLenum light, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetMaterialxv (GLenum face, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetPointerv (GLenum pname, void **params); +GL_API const GLubyte * GL_APIENTRY glGetString (GLenum name); +GL_API void GL_APIENTRY glGetTexEnviv (GLenum env, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetTexEnvxv (GLenum env, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetTexParameterxv (GLenum target, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glHint (GLenum target, GLenum mode); +GL_API GLboolean GL_APIENTRY glIsBuffer (GLuint buffer); +GL_API GLboolean GL_APIENTRY glIsEnabled (GLenum cap); +GL_API GLboolean GL_APIENTRY glIsTexture (GLuint texture); +GL_API void GL_APIENTRY glLightModelx (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightModelxv (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLightx (GLenum light, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightxv (GLenum light, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLineWidthx (GLfixed width); +GL_API void GL_APIENTRY glLoadIdentity (void); +GL_API void GL_APIENTRY glLoadMatrixx (const GLfixed *m); +GL_API void GL_APIENTRY glLogicOp (GLenum opcode); +GL_API void GL_APIENTRY glMaterialx (GLenum face, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glMaterialxv (GLenum face, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glMatrixMode (GLenum mode); +GL_API void GL_APIENTRY glMultMatrixx (const GLfixed *m); +GL_API void GL_APIENTRY glMultiTexCoord4x (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GL_API void GL_APIENTRY glNormal3x (GLfixed nx, GLfixed ny, GLfixed nz); +GL_API void GL_APIENTRY glNormalPointer (GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glOrthox (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glPixelStorei (GLenum pname, GLint param); +GL_API void GL_APIENTRY glPointParameterx (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glPointParameterxv (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glPointSizex (GLfixed size); +GL_API void GL_APIENTRY glPolygonOffsetx (GLfixed factor, GLfixed units); +GL_API void GL_APIENTRY glPopMatrix (void); +GL_API void GL_APIENTRY glPushMatrix (void); +GL_API void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +GL_API void GL_APIENTRY glRotatex (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glSampleCoverage (GLclampf value, GLboolean invert); +GL_API void GL_APIENTRY glSampleCoveragex (GLclampx value, GLboolean invert); +GL_API void GL_APIENTRY glScalex (GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glShadeModel (GLenum mode); +GL_API void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); +GL_API void GL_APIENTRY glStencilMask (GLuint mask); +GL_API void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GL_API void GL_APIENTRY glTexCoordPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glTexEnvi (GLenum target, GLenum pname, GLint param); +GL_API void GL_APIENTRY glTexEnvx (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexEnviv (GLenum target, GLenum pname, const GLint *params); +GL_API void GL_APIENTRY glTexEnvxv (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GL_API void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); +GL_API void GL_APIENTRY glTexParameterx (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); +GL_API void GL_APIENTRY glTexParameterxv (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +GL_API void GL_APIENTRY glTranslatex (GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glVertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); + +/*------------------------------------------------------------------------* + * Required OES extension functions + *------------------------------------------------------------------------*/ + +/* GL_OES_read_format */ +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#endif + +/* GL_OES_compressed_paletted_texture */ +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#endif + +/* GL_OES_point_size_array */ +#ifndef GL_OES_point_size_array +#define GL_OES_point_size_array 1 +GL_API void GL_APIENTRY glPointSizePointerOES (GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +/* GL_OES_point_sprite */ +#ifndef GL_OES_point_sprite +#define GL_OES_point_sprite 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __gl_h_ */ + diff --git a/opengl/include/GLES/glext.h b/opengl/include/GLES/glext.h new file mode 100644 index 0000000000000..4c01871aeec58 --- /dev/null +++ b/opengl/include/GLES/glext.h @@ -0,0 +1,622 @@ +#ifndef __glext_h_ +#define __glext_h_ + +/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +#ifndef GL_APIENTRYP +# define GL_APIENTRYP GL_APIENTRY* +#endif + +/*------------------------------------------------------------------------* + * OES extension tokens + *------------------------------------------------------------------------*/ + +/* GL_OES_blend_equation_separate */ +#ifndef GL_OES_blend_equation_separate +/* BLEND_EQUATION_RGB_OES same as BLEND_EQUATION_OES */ +#define GL_BLEND_EQUATION_RGB_OES 0x8009 +#define GL_BLEND_EQUATION_ALPHA_OES 0x883D +#endif + +/* GL_OES_blend_func_separate */ +#ifndef GL_OES_blend_func_separate +#define GL_BLEND_DST_RGB_OES 0x80C8 +#define GL_BLEND_SRC_RGB_OES 0x80C9 +#define GL_BLEND_DST_ALPHA_OES 0x80CA +#define GL_BLEND_SRC_ALPHA_OES 0x80CB +#endif + +/* GL_OES_blend_subtract */ +#ifndef GL_OES_blend_subtract +#define GL_BLEND_EQUATION_OES 0x8009 +#define GL_FUNC_ADD_OES 0x8006 +#define GL_FUNC_SUBTRACT_OES 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_OES 0x800B +#endif + +/* GL_OES_compressed_ETC1_RGB8_texture */ +#ifndef GL_OES_compressed_ETC1_RGB8_texture +#define GL_ETC1_RGB8_OES 0x8D64 +#endif + +/* GL_OES_depth24 */ +#ifndef GL_OES_depth24 +#define GL_DEPTH_COMPONENT24_OES 0x81A6 +#endif + +/* GL_OES_depth32 */ +#ifndef GL_OES_depth32 +#define GL_DEPTH_COMPONENT32_OES 0x81A7 +#endif + +/* GL_OES_draw_texture */ +#ifndef GL_OES_draw_texture +#define GL_TEXTURE_CROP_RECT_OES 0x8B9D +#endif + +/* GL_OES_EGL_image */ +#ifndef GL_OES_EGL_image +typedef void* GLeglImageOES; +#endif + +/* GL_OES_fixed_point */ +#ifndef GL_OES_fixed_point +#define GL_FIXED_OES 0x140C +#endif + +/* GL_OES_framebuffer_object */ +#ifndef GL_OES_framebuffer_object +#define GL_NONE_OES 0 +#define GL_FRAMEBUFFER_OES 0x8D40 +#define GL_RENDERBUFFER_OES 0x8D41 +#define GL_RGBA4_OES 0x8056 +#define GL_RGB5_A1_OES 0x8057 +#define GL_RGB565_OES 0x8D62 +#define GL_DEPTH_COMPONENT16_OES 0x81A5 +#define GL_RENDERBUFFER_WIDTH_OES 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_OES 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_OES 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE_OES 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_OES 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_OES 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_OES 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_OES 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_OES 0x8D55 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES 0x8CD3 +#define GL_COLOR_ATTACHMENT0_OES 0x8CE0 +#define GL_DEPTH_ATTACHMENT_OES 0x8D00 +#define GL_STENCIL_ATTACHMENT_OES 0x8D20 +#define GL_FRAMEBUFFER_COMPLETE_OES 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES 0x8CDA +#define GL_FRAMEBUFFER_UNSUPPORTED_OES 0x8CDD +#define GL_FRAMEBUFFER_BINDING_OES 0x8CA6 +#define GL_RENDERBUFFER_BINDING_OES 0x8CA7 +#define GL_MAX_RENDERBUFFER_SIZE_OES 0x84E8 +#define GL_INVALID_FRAMEBUFFER_OPERATION_OES 0x0506 +#endif + +/* GL_OES_mapbuffer */ +#ifndef GL_OES_mapbuffer +#define GL_WRITE_ONLY_OES 0x88B9 +#define GL_BUFFER_ACCESS_OES 0x88BB +#define GL_BUFFER_MAPPED_OES 0x88BC +#define GL_BUFFER_MAP_POINTER_OES 0x88BD +#endif + +/* GL_OES_matrix_get */ +#ifndef GL_OES_matrix_get +#define GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES 0x898D +#define GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES 0x898E +#define GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES 0x898F +#endif + +/* GL_OES_matrix_palette */ +#ifndef GL_OES_matrix_palette +#define GL_MAX_VERTEX_UNITS_OES 0x86A4 +#define GL_MAX_PALETTE_MATRICES_OES 0x8842 +#define GL_MATRIX_PALETTE_OES 0x8840 +#define GL_MATRIX_INDEX_ARRAY_OES 0x8844 +#define GL_WEIGHT_ARRAY_OES 0x86AD +#define GL_CURRENT_PALETTE_MATRIX_OES 0x8843 +#define GL_MATRIX_INDEX_ARRAY_SIZE_OES 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_OES 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_OES 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_OES 0x8849 +#define GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES 0x8B9E +#define GL_WEIGHT_ARRAY_SIZE_OES 0x86AB +#define GL_WEIGHT_ARRAY_TYPE_OES 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_OES 0x86AA +#define GL_WEIGHT_ARRAY_POINTER_OES 0x86AC +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_OES 0x889E +#endif + +/* GL_OES_packed_depth_stencil */ +#ifndef GL_OES_packed_depth_stencil +#define GL_DEPTH_STENCIL_OES 0x84F9 +#define GL_UNSIGNED_INT_24_8_OES 0x84FA +#define GL_DEPTH24_STENCIL8_OES 0x88F0 +#endif + +/* GL_OES_rgb8_rgba8 */ +#ifndef GL_OES_rgb8_rgba8 +#define GL_RGB8_OES 0x8051 +#define GL_RGBA8_OES 0x8058 +#endif + +/* GL_OES_stencil1 */ +#ifndef GL_OES_stencil1 +#define GL_STENCIL_INDEX1_OES 0x8D46 +#endif + +/* GL_OES_stencil4 */ +#ifndef GL_OES_stencil4 +#define GL_STENCIL_INDEX4_OES 0x8D47 +#endif + +/* GL_OES_stencil8 */ +#ifndef GL_OES_stencil8 +#define GL_STENCIL_INDEX8_OES 0x8D48 +#endif + +/* GL_OES_stencil_wrap */ +#ifndef GL_OES_stencil_wrap +#define GL_INCR_WRAP_OES 0x8507 +#define GL_DECR_WRAP_OES 0x8508 +#endif + +/* GL_OES_texture_cube_map */ +#ifndef GL_OES_texture_cube_map +#define GL_NORMAL_MAP_OES 0x8511 +#define GL_REFLECTION_MAP_OES 0x8512 +#define GL_TEXTURE_CUBE_MAP_OES 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_OES 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES 0x851A +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_OES 0x851C +#define GL_TEXTURE_GEN_MODE_OES 0x2500 +#define GL_TEXTURE_GEN_STR_OES 0x8D60 +#endif + +/* GL_OES_texture_mirrored_repeat */ +#ifndef GL_OES_texture_mirrored_repeat +#define GL_MIRRORED_REPEAT_OES 0x8370 +#endif + +/*------------------------------------------------------------------------* + * AMD extension tokens + *------------------------------------------------------------------------*/ + +/* GL_AMD_compressed_3DC_texture */ +#ifndef GL_AMD_compressed_3DC_texture +#define GL_3DC_X_AMD 0x87F9 +#define GL_3DC_XY_AMD 0x87FA +#endif + +/* GL_AMD_compressed_ATC_texture */ +#ifndef GL_AMD_compressed_ATC_texture +#define GL_ATC_RGB_AMD 0x8C92 +#define GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93 +#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE +#endif + +/*------------------------------------------------------------------------* + * EXT extension tokens + *------------------------------------------------------------------------*/ + +/* GL_EXT_texture_filter_anisotropic */ +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +/*------------------------------------------------------------------------* + * OES extension functions + *------------------------------------------------------------------------*/ + +/* GL_OES_blend_equation_separate */ +#ifndef GL_OES_blend_equation_separate +#define GL_OES_blend_equation_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBlendEquationSeparateOES (GLenum modeRGB, GLenum modeAlpha); +#endif +typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONSEPARATEOESPROC) (GLenum modeRGB, GLenum modeAlpha); +#endif + +/* GL_OES_blend_func_separate */ +#ifndef GL_OES_blend_func_separate +#define GL_OES_blend_func_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBlendFuncSeparateOES (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +typedef void (GL_APIENTRYP PFNGLBLENDFUNCSEPARATEOESPROC) (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif + +/* GL_OES_blend_subtract */ +#ifndef GL_OES_blend_subtract +#define GL_OES_blend_subtract 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBlendEquationOES (GLenum mode); +#endif +typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONOESPROC) (GLenum mode); +#endif + +/* GL_OES_byte_coordinates */ +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 +#endif + +/* GL_OES_compressed_ETC1_RGB8_texture */ +#ifndef GL_OES_compressed_ETC1_RGB8_texture +#define GL_OES_compressed_ETC1_RGB8_texture 1 +#endif + +/* GL_OES_depth24 */ +#ifndef GL_OES_depth24 +#define GL_OES_depth24 1 +#endif + +/* GL_OES_depth32 */ +#ifndef GL_OES_depth32 +#define GL_OES_depth32 1 +#endif + +/* GL_OES_draw_texture */ +#ifndef GL_OES_draw_texture +#define GL_OES_draw_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDrawTexsOES (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height); +GL_API void GL_APIENTRY glDrawTexiOES (GLint x, GLint y, GLint z, GLint width, GLint height); +GL_API void GL_APIENTRY glDrawTexxOES (GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height); +GL_API void GL_APIENTRY glDrawTexsvOES (const GLshort *coords); +GL_API void GL_APIENTRY glDrawTexivOES (const GLint *coords); +GL_API void GL_APIENTRY glDrawTexxvOES (const GLfixed *coords); +GL_API void GL_APIENTRY glDrawTexfOES (GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height); +GL_API void GL_APIENTRY glDrawTexfvOES (const GLfloat *coords); +#endif +typedef void (GL_APIENTRYP PFNGLDRAWTEXSOESPROC) (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXIOESPROC) (GLint x, GLint y, GLint z, GLint width, GLint height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXXOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXSVOESPROC) (const GLshort *coords); +typedef void (GL_APIENTRYP PFNGLDRAWTEXIVOESPROC) (const GLint *coords); +typedef void (GL_APIENTRYP PFNGLDRAWTEXXVOESPROC) (const GLfixed *coords); +typedef void (GL_APIENTRYP PFNGLDRAWTEXFOESPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXFVOESPROC) (const GLfloat *coords); +#endif + +/* GL_OES_EGL_image */ +#ifndef GL_OES_EGL_image +#define GL_OES_EGL_image 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image); +GL_API void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES (GLenum target, GLeglImageOES image); +#endif +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image); +#endif + +/* GL_OES_element_index_uint */ +#ifndef GL_OES_element_index_uint +#define GL_OES_element_index_uint 1 +#endif + +/* GL_OES_extended_matrix_palette */ +#ifndef GL_OES_extended_matrix_palette +#define GL_OES_extended_matrix_palette 1 +#endif + +/* GL_OES_fbo_render_mipmap */ +#ifndef GL_OES_fbo_render_mipmap +#define GL_OES_fbo_render_mipmap 1 +#endif + +/* GL_OES_fixed_point */ +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glAlphaFuncxOES (GLenum func, GLclampx ref); +GL_API void GL_APIENTRY glClearColorxOES (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +GL_API void GL_APIENTRY glClearDepthxOES (GLclampx depth); +GL_API void GL_APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); +GL_API void GL_APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GL_API void GL_APIENTRY glDepthRangexOES (GLclampx zNear, GLclampx zFar); +GL_API void GL_APIENTRY glFogxOES (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glFogxvOES (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glFrustumxOES (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glGetClipPlanexOES (GLenum pname, GLfixed eqn[4]); +GL_API void GL_APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetLightxvOES (GLenum light, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetMaterialxvOES (GLenum face, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetTexEnvxvOES (GLenum env, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glLightModelxOES (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLineWidthxOES (GLfixed width); +GL_API void GL_APIENTRY glLoadMatrixxOES (const GLfixed *m); +GL_API void GL_APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glMultMatrixxOES (const GLfixed *m); +GL_API void GL_APIENTRY glMultiTexCoord4xOES (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GL_API void GL_APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); +GL_API void GL_APIENTRY glOrthoxOES (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glPointParameterxOES (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glPointSizexOES (GLfixed size); +GL_API void GL_APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); +GL_API void GL_APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glSampleCoveragexOES (GLclampx value, GLboolean invert); +GL_API void GL_APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); +#endif +typedef void (GL_APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLclampx ref); +typedef void (GL_APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +typedef void (GL_APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLclampx depth); +typedef void (GL_APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); +typedef void (GL_APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (GL_APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLclampx zNear, GLclampx zFar); +typedef void (GL_APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +typedef void (GL_APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum pname, GLfixed eqn[4]); +typedef void (GL_APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETLIGHTXVOESPROC) (GLenum light, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETMATERIALXVOESPROC) (GLenum face, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum env, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); +typedef void (GL_APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); +typedef void (GL_APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); +typedef void (GL_APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (GL_APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (GL_APIENTRYP PFNGLORTHOXOESPROC) (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +typedef void (GL_APIENTRYP PFNGLPOINTPARAMETERXOESPROC) (GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); +typedef void (GL_APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); +typedef void (GL_APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (GL_APIENTRYP PFNGLSAMPLECOVERAGEXOESPROC) (GLclampx value, GLboolean invert); +typedef void (GL_APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (GL_APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +#endif + +/* GL_OES_framebuffer_object */ +#ifndef GL_OES_framebuffer_object +#define GL_OES_framebuffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API GLboolean GL_APIENTRY glIsRenderbufferOES (GLuint renderbuffer); +GL_API void GL_APIENTRY glBindRenderbufferOES (GLenum target, GLuint renderbuffer); +GL_API void GL_APIENTRY glDeleteRenderbuffersOES (GLsizei n, const GLuint* renderbuffers); +GL_API void GL_APIENTRY glGenRenderbuffersOES (GLsizei n, GLuint* renderbuffers); +GL_API void GL_APIENTRY glRenderbufferStorageOES (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glGetRenderbufferParameterivOES (GLenum target, GLenum pname, GLint* params); +GL_API GLboolean GL_APIENTRY glIsFramebufferOES (GLuint framebuffer); +GL_API void GL_APIENTRY glBindFramebufferOES (GLenum target, GLuint framebuffer); +GL_API void GL_APIENTRY glDeleteFramebuffersOES (GLsizei n, const GLuint* framebuffers); +GL_API void GL_APIENTRY glGenFramebuffersOES (GLsizei n, GLuint* framebuffers); +GL_API GLenum GL_APIENTRY glCheckFramebufferStatusOES (GLenum target); +GL_API void GL_APIENTRY glFramebufferRenderbufferOES (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GL_API void GL_APIENTRY glFramebufferTexture2DOES (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GL_API void GL_APIENTRY glGetFramebufferAttachmentParameterivOES (GLenum target, GLenum attachment, GLenum pname, GLint* params); +GL_API void GL_APIENTRY glGenerateMipmapOES (GLenum target); +#endif +typedef GLboolean (GL_APIENTRYP PFNGLISRENDERBUFFEROESPROC) (GLuint renderbuffer); +typedef void (GL_APIENTRYP PFNGLBINDRENDERBUFFEROESPROC) (GLenum target, GLuint renderbuffer); +typedef void (GL_APIENTRYP PFNGLDELETERENDERBUFFERSOESPROC) (GLsizei n, const GLuint* renderbuffers); +typedef void (GL_APIENTRYP PFNGLGENRENDERBUFFERSOESPROC) (GLsizei n, GLuint* renderbuffers); +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVOESPROC) (GLenum target, GLenum pname, GLint* params); +typedef GLboolean (GL_APIENTRYP PFNGLISFRAMEBUFFEROESPROC) (GLuint framebuffer); +typedef void (GL_APIENTRYP PFNGLBINDFRAMEBUFFEROESPROC) (GLenum target, GLuint framebuffer); +typedef void (GL_APIENTRYP PFNGLDELETEFRAMEBUFFERSOESPROC) (GLsizei n, const GLuint* framebuffers); +typedef void (GL_APIENTRYP PFNGLGENFRAMEBUFFERSOESPROC) (GLsizei n, GLuint* framebuffers); +typedef GLenum (GL_APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSOESPROC) (GLenum target); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEROESPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DOESPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GL_APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVOESPROC) (GLenum target, GLenum attachment, GLenum pname, GLint* params); +typedef void (GL_APIENTRYP PFNGLGENERATEMIPMAPOESPROC) (GLenum target); +#endif + +/* GL_OES_mapbuffer */ +#ifndef GL_OES_mapbuffer +#define GL_OES_mapbuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void* GL_APIENTRY glMapBufferOES (GLenum target, GLenum access); +GL_API GLboolean GL_APIENTRY glUnmapBufferOES (GLenum target); +GL_API void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, void** params); +#endif +typedef void* (GL_APIENTRYP PFNGLMAPBUFFEROESPROC) (GLenum target, GLenum access); +typedef GLboolean (GL_APIENTRYP PFNGLUNMAPBUFFEROESPROC) (GLenum target); +typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, void** params); +#endif + +/* GL_OES_matrix_get */ +#ifndef GL_OES_matrix_get +#define GL_OES_matrix_get 1 +#endif + +/* GL_OES_matrix_palette */ +#ifndef GL_OES_matrix_palette +#define GL_OES_matrix_palette 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glCurrentPaletteMatrixOES (GLuint matrixpaletteindex); +GL_API void GL_APIENTRY glLoadPaletteFromModelViewMatrixOES (void); +GL_API void GL_APIENTRY glMatrixIndexPointerOES (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glWeightPointerOES (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif +typedef void (GL_APIENTRYP PFNGLCURRENTPALETTEMATRIXOESPROC) (GLuint matrixpaletteindex); +typedef void (GL_APIENTRYP PFNGLLOADPALETTEFROMMODELVIEWMATRIXOESPROC) (void); +typedef void (GL_APIENTRYP PFNGLMATRIXINDEXPOINTEROESPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (GL_APIENTRYP PFNGLWEIGHTPOINTEROESPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +/* GL_OES_packed_depth_stencil */ +#ifndef GL_OES_packed_depth_stencil +#define GL_OES_packed_depth_stencil 1 +#endif + +/* GL_OES_query_matrix */ +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API GLbitfield GL_APIENTRY glQueryMatrixxOES (GLfixed mantissa[16], GLint exponent[16]); +#endif +typedef GLbitfield (GL_APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed mantissa[16], GLint exponent[16]); +#endif + +/* GL_OES_rgb8_rgba8 */ +#ifndef GL_OES_rgb8_rgba8 +#define GL_OES_rgb8_rgba8 1 +#endif + +/* GL_OES_single_precision */ +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDepthRangefOES (GLclampf zNear, GLclampf zFar); +GL_API void GL_APIENTRY glFrustumfOES (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glOrthofOES (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); +GL_API void GL_APIENTRY glGetClipPlanefOES (GLenum pname, GLfloat eqn[4]); +GL_API void GL_APIENTRY glClearDepthfOES (GLclampf depth); +#endif +typedef void (GL_APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf zNear, GLclampf zFar); +typedef void (GL_APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +typedef void (GL_APIENTRYP PFNGLORTHOFOESPROC) (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +typedef void (GL_APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); +typedef void (GL_APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum pname, GLfloat eqn[4]); +typedef void (GL_APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); +#endif + +/* GL_OES_stencil1 */ +#ifndef GL_OES_stencil1 +#define GL_OES_stencil1 1 +#endif + +/* GL_OES_stencil4 */ +#ifndef GL_OES_stencil4 +#define GL_OES_stencil4 1 +#endif + +/* GL_OES_stencil8 */ +#ifndef GL_OES_stencil8 +#define GL_OES_stencil8 1 +#endif + +/* GL_OES_stencil_wrap */ +#ifndef GL_OES_stencil_wrap +#define GL_OES_stencil_wrap 1 +#endif + +/* GL_OES_texture_cube_map */ +#ifndef GL_OES_texture_cube_map +#define GL_OES_texture_cube_map 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glTexGenfOES (GLenum coord, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glTexGenfvOES (GLenum coord, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glTexGeniOES (GLenum coord, GLenum pname, GLint param); +GL_API void GL_APIENTRY glTexGenivOES (GLenum coord, GLenum pname, const GLint *params); +GL_API void GL_APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glGetTexGenfvOES (GLenum coord, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetTexGenivOES (GLenum coord, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); +#endif +typedef void (GL_APIENTRYP PFNGLTEXGENFOESPROC) (GLenum coord, GLenum pname, GLfloat param); +typedef void (GL_APIENTRYP PFNGLTEXGENFVOESPROC) (GLenum coord, GLenum pname, const GLfloat *params); +typedef void (GL_APIENTRYP PFNGLTEXGENIOESPROC) (GLenum coord, GLenum pname, GLint param); +typedef void (GL_APIENTRYP PFNGLTEXGENIVOESPROC) (GLenum coord, GLenum pname, const GLint *params); +typedef void (GL_APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETTEXGENFVOESPROC) (GLenum coord, GLenum pname, GLfloat *params); +typedef void (GL_APIENTRYP PFNGLGETTEXGENIVOESPROC) (GLenum coord, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); +#endif + +/* GL_OES_texture_env_crossbar */ +#ifndef GL_OES_texture_env_crossbar +#define GL_OES_texture_env_crossbar 1 +#endif + +/* GL_OES_texture_mirrored_repeat */ +#ifndef GL_OES_texture_mirrored_repeat +#define GL_OES_texture_mirrored_repeat 1 +#endif + +/*------------------------------------------------------------------------* + * AMD extension functions + *------------------------------------------------------------------------*/ + +/* GL_AMD_compressed_3DC_texture */ +#ifndef GL_AMD_compressed_3DC_texture +#define GL_AMD_compressed_3DC_texture 1 +#endif + +/* GL_AMD_compressed_ATC_texture */ +#ifndef GL_AMD_compressed_ATC_texture +#define GL_AMD_compressed_ATC_texture 1 +#endif + +/*------------------------------------------------------------------------* + * EXT extension functions + *------------------------------------------------------------------------*/ + +/* GL_EXT_texture_filter_anisotropic */ +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#endif + +/*------------------------------------------------------------------------* + * dalvik extension functions + *------------------------------------------------------------------------*/ +#ifdef ANDROID +void glColorPointerBounds(GLint size, GLenum type, GLsizei stride, + const GLvoid *ptr, GLsizei count); +void glNormalPointerBounds(GLenum type, GLsizei stride, + const GLvoid *pointer, GLsizei count); +void glTexCoordPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count); +void glVertexPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* __glext_h_ */ + diff --git a/opengl/include/GLES/glplatform.h b/opengl/include/GLES/glplatform.h new file mode 100644 index 0000000000000..0924caeaffe92 --- /dev/null +++ b/opengl/include/GLES/glplatform.h @@ -0,0 +1,39 @@ +#ifndef __glplatform_h_ +#define __glplatform_h_ + +/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */ + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +/* Platform-specific types and definitions for OpenGL ES 1.X gl.h + * Last modified on 2008/12/19 + * + * Adopters may modify khrplatform.h and this file to suit their platform. + * You are encouraged to submit all modifications to the Khronos group so that + * they can be included in future versions of this file. Please submit changes + * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) + * by filing a bug against product "OpenGL-ES" component "Registry". + */ + +#include + +#ifndef GL_API +#define GL_API KHRONOS_APICALL +#endif + +#if defined(ANDROID) + +#define GL_APIENTRY KHRONOS_APIENTRY + +// XXX: this should probably not be here +#define GL_DIRECT_TEXTURE_2D_QUALCOMM 0x7E80 + +// XXX: not sure how this is intended to be used +#define GL_GLEXT_PROTOTYPES + +#endif + +#endif /* __glplatform_h_ */ diff --git a/opengl/include/KHR/khrplatform.h b/opengl/include/KHR/khrplatform.h new file mode 100644 index 0000000000000..4cc27c52da8ff --- /dev/null +++ b/opengl/include/KHR/khrplatform.h @@ -0,0 +1,241 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2009 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Platform-specific types and definitions. + * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by sending them to the public Khronos Bugzilla + * (http://khronos.org/bugzilla) by filing a bug against product + * "Khronos (general)" component "Registry". + * + * A predefined template which fills in some of the bug fields can be + * reached using http://tinyurl.com/khrplatform-h-bugreport, but you + * must create a Bugzilla login first. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system. + * http://www.khronos.org/registry/implementers_guide.pdf + * + * + * This file should be included as + * #include + * by the Khronos API header file that uses its types and defines. + * + * The types in this file should only be used to define API-specific types. + * Types defined in this file: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * + * Macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * These may be used in function prototypes as: + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(_WIN32) && !defined(__SCITECH_SNAP__) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_APIENTRY __stdcall +#else +# define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + + +#endif /* __khrplatform_h_ */ diff --git a/opengl/libGLES_CM/Android.mk b/opengl/libGLES_CM/Android.mk deleted file mode 100644 index e350e0225c434..0000000000000 --- a/opengl/libGLES_CM/Android.mk +++ /dev/null @@ -1,24 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# -# Build the wrapper OpenGL ES library -# - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= gl_wrapper.cpp.arm gl_logger.cpp - -LOCAL_SHARED_LIBRARIES += libcutils libutils libui -LOCAL_LDLIBS := -lpthread -ldl -LOCAL_MODULE:= libGLES_CM - -# needed on sim build because of weird logging issues -ifeq ($(TARGET_SIMULATOR),true) -else - LOCAL_SHARED_LIBRARIES += libdl - # we need to access the Bionic private header - LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private -endif - -include $(BUILD_SHARED_LIBRARY) - diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 3e8dca958dc92..1446fb2658acd 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -33,7 +33,10 @@ #include -#include +#include +#include +#include +#include #include #include @@ -149,7 +152,6 @@ struct egl_surface_t virtual EGLint getRefreshRate() const; virtual EGLint getSwapBehavior() const; virtual EGLBoolean swapBuffers(); - virtual EGLBoolean swapRectangle(EGLint l, EGLint t, EGLint w, EGLint h); protected: GGLSurface depth; }; @@ -171,10 +173,6 @@ egl_surface_t::~egl_surface_t() EGLBoolean egl_surface_t::swapBuffers() { return EGL_FALSE; } -EGLBoolean egl_surface_t::swapRectangle( - EGLint l, EGLint t, EGLint w, EGLint h) { - return EGL_FALSE; -} EGLint egl_surface_t::getHorizontalResolution() const { return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); } @@ -201,7 +199,6 @@ struct egl_window_surface_t : public egl_surface_t virtual bool isValid() const { return nativeWindow->magic == 0x600913; } virtual EGLBoolean swapBuffers(); - virtual EGLBoolean swapRectangle(EGLint l, EGLint t, EGLint w, EGLint h); virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); virtual EGLBoolean bindReadSurface(ogles_context_t* gl); virtual EGLint getWidth() const { return nativeWindow->width; } @@ -243,7 +240,6 @@ EGLBoolean egl_window_surface_t::swapBuffers() if (flags & EGL_NATIVES_FLAG_SIZE_CHANGED) { // TODO: we probably should reset the swap rect here // if the window size has changed - // window->setSwapRectangle(Rect(info.w, info.h)); if (depth.data) { free(depth.data); depth.width = nativeWindow->width; @@ -259,12 +255,6 @@ EGLBoolean egl_window_surface_t::swapBuffers() return EGL_TRUE; } -EGLBoolean egl_window_surface_t::swapRectangle( - EGLint l, EGLint t, EGLint w, EGLint h) -{ - nativeWindow->setSwapRectangle(nativeWindow, l, t, w, h); - return EGL_TRUE; -} EGLBoolean egl_window_surface_t::bindDrawSurface(ogles_context_t* gl) { GGLSurface buffer; @@ -476,21 +466,16 @@ struct config_management_t { static char const * const gVendorString = "Google Inc."; static char const * const gVersionString = "1.2 Android Driver"; static char const * const gClientApiString = "OpenGL ES"; -static char const * const gExtensionsString = - "EGL_ANDROID_swap_rectangle" " " - "EGL_ANDROID_copy_front_to_back" " " - "EGL_ANDROID_get_render_buffer_address" - ; +static char const * const gExtensionsString = ""; // ---------------------------------------------------------------------------- struct extention_map_t { const char * const name; - void (*address)(void); + __eglMustCastToProperFunctionPointerType address; }; static const extention_map_t gExtentionMap[] = { - { "eglSwapRectangleANDROID", (void(*)())&eglSwapRectangleANDROID }, { "glDrawTexsOES", (void(*)())&glDrawTexsOES }, { "glDrawTexiOES", (void(*)())&glDrawTexiOES }, { "glDrawTexfOES", (void(*)())&glDrawTexfOES }, @@ -1554,15 +1539,3 @@ void (*eglGetProcAddress (const char *procname))() } return NULL; } - -EGLBoolean eglSwapRectangleANDROID( - EGLDisplay dpy, EGLSurface draw, - EGLint l, EGLint t, EGLint w, EGLint h) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - egl_surface_t* surface = (egl_surface_t*)draw; - if (surface->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - return surface->swapRectangle(l, t, w, h); -} diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp index c2f9f12356ba1..5cbabea1f8a8e 100644 --- a/opengl/libagl/state.cpp +++ b/opengl/libagl/state.cpp @@ -403,12 +403,6 @@ void glGetIntegerv(GLenum pname, GLint *params) case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES: params[0] = GL_UNSIGNED_SHORT_5_6_5; break; - case GL_MAX_ELEMENTS_INDICES: - params[0] = 65536; - break; - case GL_MAX_ELEMENTS_VERTICES: - params[0] = 0x7FFFFFFF; - break; case GL_MAX_LIGHTS: params[0] = OGLES_MAX_LIGHTS; break; diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp index 6b2640a720d35..b6f534b7ea506 100644 --- a/opengl/libagl/texture.cpp +++ b/opengl/libagl/texture.cpp @@ -526,7 +526,7 @@ void generateMipmap(ogles_context_t* c, GLint level) static void texParameterx( GLenum target, GLenum pname, GLfixed param, ogles_context_t* c) { - if (target != GGL_TEXTURE_2D) { + if (target != GL_TEXTURE_2D) { ogles_error(c, GL_INVALID_ENUM); return; } @@ -534,8 +534,7 @@ static void texParameterx( EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture; switch (pname) { case GL_TEXTURE_WRAP_S: - if ((param == GL_CLAMP) || - (param == GL_REPEAT) || + if ((param == GL_REPEAT) || (param == GL_CLAMP_TO_EDGE)) { textureObject->wraps = param; } else { @@ -543,9 +542,8 @@ static void texParameterx( } break; case GL_TEXTURE_WRAP_T: - if ((param == GGL_CLAMP) || - (param == GGL_REPEAT) || - (param == GGL_CLAMP_TO_EDGE)) { + if ((param == GL_REPEAT) || + (param == GL_CLAMP_TO_EDGE)) { textureObject->wrapt = param; } else { goto invalid_enum; @@ -1006,7 +1004,7 @@ void glCompressedTexImage2D( void glTexImage2D( - GLenum target, GLint level, GLenum internalformat, + GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) { diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk new file mode 100644 index 0000000000000..2ecc7768cac25 --- /dev/null +++ b/opengl/libs/Android.mk @@ -0,0 +1,53 @@ +LOCAL_PATH:= $(call my-dir) + +# +# Build META EGL library +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + EGL/egl.cpp \ + EGL/gpu.cpp \ +# + +LOCAL_SHARED_LIBRARIES += libcutils libutils libui +LOCAL_LDLIBS := -lpthread -ldl +LOCAL_MODULE:= libEGL + +# needed on sim build because of weird logging issues +ifeq ($(TARGET_SIMULATOR),true) +else + LOCAL_SHARED_LIBRARIES += libdl + # we need to access the Bionic private header + LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private +endif + +include $(BUILD_SHARED_LIBRARY) + + + +# +# Build the wrapper OpenGL ES library +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + GLES_CM/gl.cpp.arm \ + GLES_CM/gl_logger.cpp \ +# + +LOCAL_SHARED_LIBRARIES += libcutils libutils libui libEGL +LOCAL_LDLIBS := -lpthread -ldl +LOCAL_MODULE:= libGLESv1_CM + +# needed on sim build because of weird logging issues +ifeq ($(TARGET_SIMULATOR),true) +else + LOCAL_SHARED_LIBRARIES += libdl + # we need to access the Bionic private header + LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/opengl/libGLES_CM/gl_wrapper.cpp b/opengl/libs/EGL/egl.cpp similarity index 72% rename from opengl/libGLES_CM/gl_wrapper.cpp rename to opengl/libs/EGL/egl.cpp index 319753570d1a2..3f33b675a5f06 100644 --- a/opengl/libGLES_CM/gl_wrapper.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -27,31 +27,22 @@ #include #endif -#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include -#include -#include +#include -#include -#include +#include "hooks.h" +#include "egl_impl.h" -#include "gl_logger.h" -#undef NELEM - -#define GL_LOGGER 0 -#define USE_SLOW_BINDING 0 -#define NELEM(x) (sizeof(x)/sizeof(*(x))) -#define MAX_NUMBER_OF_GL_EXTENSIONS 32 #define MAKE_CONFIG(_impl, _index) ((EGLConfig)(((_impl)<<24) | (_index))) #define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r) @@ -59,26 +50,12 @@ namespace android { // ---------------------------------------------------------------------------- -// EGLDisplay are global, not attached to a given thread -static const unsigned int NUM_DISPLAYS = 1; -static const unsigned int IMPL_HARDWARE = 0; -static const unsigned int IMPL_SOFTWARE = 1; -static const unsigned int IMPL_HARDWARE_CONTEXT_LOST = 2; -static const unsigned int IMPL_SOFTWARE_CONTEXT_LOST = 3; -static const unsigned int IMPL_NO_CONTEXT = 4; - -// ---------------------------------------------------------------------------- - -struct gl_hooks_t; - -struct egl_connection_t -{ - void volatile * dso; - gl_hooks_t * hooks; - EGLint major; - EGLint minor; - int unavailable; -}; +#define VERSION_MINOR 1 +#define VERSION_MAJOR 4 +static char const * const gVendorString = "Android"; +static char const * const gVersionString = "1.31 Android META-EGL"; +static char const * const gClientApiString = "OpenGL ES"; +static char const * const gExtensionString = ""; template struct egl_object_t @@ -103,7 +80,6 @@ struct egl_display_t : public egl_object_t<'_dpy'> char const * version; char const * clientApi; char const * extensions; - char const * extensions_config; }; strings_t queryString[2]; }; @@ -152,82 +128,45 @@ struct tls_t EGLContext ctx; }; - -// GL / EGL hooks - -typedef void(*proc_t)(); - -struct gl_hooks_t { - struct gl_t { - #define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__); - #include "gl_entries.cpp" - #undef GL_ENTRY - } gl; - struct egl_t { - #define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__); - #include "egl_entries.cpp" - #undef EGL_ENTRY - } egl; - struct gl_ext_t { - void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void); - } ext; -}; - -static char const * const gl_names[] = { - #define GL_ENTRY(_r, _api, ...) #_api, - #include "gl_entries.cpp" - #undef GL_ENTRY - NULL -}; - -static char const * const egl_names[] = { - #define EGL_ENTRY(_r, _api, ...) #_api, - #include "egl_entries.cpp" - #undef EGL_ENTRY - NULL -}; - static void gl_unimplemented() { LOGE("called unimplemented OpenGL ES API"); } +// ---------------------------------------------------------------------------- +// GL / EGL hooks // ---------------------------------------------------------------------------- -static egl_connection_t gEGLImpl[2]; +#undef GL_ENTRY +#undef EGL_ENTRY +#define GL_ENTRY(_r, _api, ...) #_api, +#define EGL_ENTRY(_r, _api, ...) #_api, + +static char const * const gl_names[] = { + #include "gl_entries.in" + NULL +}; + +static char const * const egl_names[] = { + #include "egl_entries.in" + NULL +}; + +#undef GL_ENTRY +#undef EGL_ENTRY + +// ---------------------------------------------------------------------------- + +egl_connection_t gEGLImpl[2]; static egl_display_t gDisplay[NUM_DISPLAYS]; -static gl_hooks_t gHooks[5]; static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER; static pthread_key_t gEGLThreadLocalStorageKey = -1; // ---------------------------------------------------------------------------- -#if defined(HAVE_ANDROID_OS) && !USE_SLOW_BINDING && !GL_LOGGER +gl_hooks_t gHooks[IMPL_NUM_IMPLEMENTATIONS]; +pthread_key_t gGLWrapperKey = -1; -/* special private C library header */ -#include -// We have a dedicated TLS slot in bionic -static inline void setGlThreadSpecific(gl_hooks_t const *value) { - ((uint32_t *)__get_tls())[TLS_SLOT_OPENGL_API] = (uint32_t)value; -} -static gl_hooks_t const* getGlThreadSpecific() { - gl_hooks_t const* hooks = (gl_hooks_t const *)(((unsigned const *)__get_tls())[TLS_SLOT_OPENGL_API]); - if (hooks) return hooks; - return &gHooks[IMPL_NO_CONTEXT]; -} - -#else - -static pthread_key_t gGLWrapperKey = -1; -static inline void setGlThreadSpecific(gl_hooks_t const *value) { - pthread_setspecific(gGLWrapperKey, value); -} -static gl_hooks_t const* getGlThreadSpecific() { - gl_hooks_t const* hooks = static_cast(pthread_getspecific(gGLWrapperKey)); - if (hooks) return hooks; - return &gHooks[IMPL_NO_CONTEXT]; -} - -#endif +// ---------------------------------------------------------------------------- static __attribute__((noinline)) const char *egl_strerror(EGLint err) @@ -322,170 +261,14 @@ EGLContext getContext() { return tls->ctx; } -/*****************************************************************************/ - -/* - * we provide our own allocators for the GPU regions, these - * allocators go through surfaceflinger - */ - -static Mutex gRegionsLock; -static request_gpu_t gRegions; -static sp gSurfaceManager; -ISurfaceComposer* GLES_localSurfaceManager = 0; - -const sp& getSurfaceFlinger() -{ - Mutex::Autolock _l(gRegionsLock); - - /* - * There is a little bit of voodoo magic here. We want to access - * surfaceflinger for allocating GPU regions, however, when we are - * running as part of surfaceflinger, we want to bypass the - * service manager because surfaceflinger might not be registered yet. - * SurfaceFlinger will populate "GLES_localSurfaceManager" with its - * own address, so we can just use that. - */ - if (gSurfaceManager == 0) { - if (GLES_localSurfaceManager) { - // we're running in SurfaceFlinger's context - gSurfaceManager = GLES_localSurfaceManager; - } else { - // we're a remote process or not part of surfaceflinger, - // go through the service manager - sp sm = defaultServiceManager(); - if (sm != NULL) { - sp binder = sm->getService(String16("SurfaceFlinger")); - gSurfaceManager = interface_cast(binder); - } - } - } - return gSurfaceManager; -} - -class GPURevokeRequester : public BnGPUCallback -{ -public: - virtual void gpuLost() { - LOGD("CONTEXT_LOST: Releasing GPU upon request from SurfaceFlinger."); - gEGLImpl[IMPL_HARDWARE].hooks = &gHooks[IMPL_HARDWARE_CONTEXT_LOST]; - } -}; - -static sp gRevokerCallback; - - -static request_gpu_t* gpu_acquire(void* user) -{ - sp server( getSurfaceFlinger() ); - - Mutex::Autolock _l(gRegionsLock); - if (server == NULL) { - return 0; - } - - ISurfaceComposer::gpu_info_t info; - - if (gRevokerCallback == 0) - gRevokerCallback = new GPURevokeRequester(); - - status_t err = server->requestGPU(gRevokerCallback, &info); - if (err != NO_ERROR) { - LOGD("requestGPU returned %d", err); - return 0; - } - - bool failed = false; - request_gpu_t* gpu = &gRegions; - memset(gpu, 0, sizeof(*gpu)); - - if (info.regs != 0) { - sp heap(info.regs->getMemory()); - if (heap != 0) { - int fd = heap->heapID(); - gpu->regs.fd = fd; - gpu->regs.base = info.regs->pointer(); - gpu->regs.size = info.regs->size(); - gpu->regs.user = info.regs.get(); -#if HAVE_ANDROID_OS - struct pmem_region region; - if (ioctl(fd, PMEM_GET_PHYS, ®ion) >= 0) - gpu->regs.phys = (void*)region.offset; -#endif - info.regs->incStrong(gpu); - } else { - LOGE("GPU register handle %p is invalid!", info.regs.get()); - failed = true; - } - } - - for (size_t i=0 ; i& region(info.regions[i].region); - if (region != 0) { - sp heap(region->getMemory()); - if (heap != 0) { - const int fd = heap->heapID(); - gpu->gpu[i].fd = fd; - gpu->gpu[i].base = region->pointer(); - gpu->gpu[i].size = region->size(); - gpu->gpu[i].user = region.get(); - gpu->gpu[i].offset = info.regions[i].reserved; -#if HAVE_ANDROID_OS - struct pmem_region reg; - if (ioctl(fd, PMEM_GET_PHYS, ®) >= 0) - gpu->gpu[i].phys = (void*)reg.offset; -#endif - region->incStrong(gpu); - } else { - LOGE("GPU region handle [%d, %p] is invalid!", i, region.get()); - failed = true; - } - } - } - - if (failed) { - // something went wrong, clean up everything! - if (gpu->regs.user) { - static_cast(gpu->regs.user)->decStrong(gpu); - for (size_t i=0 ; igpu[i].user) { - static_cast(gpu->gpu[i].user)->decStrong(gpu); - } - } - } - } - - gpu->count = info.count; - return gpu; -} - -static int gpu_release(void*, request_gpu_t* gpu) -{ - sp regs; - - { // scope for lock - Mutex::Autolock _l(gRegionsLock); - regs = static_cast(gpu->regs.user); - gpu->regs.user = 0; - if (regs != 0) regs->decStrong(gpu); - - for (int i=0 ; icount ; i++) { - sp r(static_cast(gpu->gpu[i].user)); - gpu->gpu[i].user = 0; - if (r != 0) r->decStrong(gpu); - } - } - - // there is a special transaction to relinquish the GPU - // (it will happen automatically anyway if we don't do this) - Parcel data, reply; - // NOTE: this transaction does not require an interface token - regs->asBinder()->transact(1000, data, &reply); - return 1; -} /*****************************************************************************/ +class ISurfaceComposer; +const sp& getSurfaceFlinger(); +request_gpu_t* gpu_acquire(void* user); +int gpu_release(void*, request_gpu_t* gpu); + static __attribute__((noinline)) void *load_driver(const char* driver, gl_hooks_t* hooks) { @@ -576,18 +359,12 @@ static int cmp_configs(const void* a, const void *b) return c0c1 ? 1 : 0); } -static char const * const gVendorString = "Android"; -static char const * const gVersionString = "1.3 Android META-EGL"; -static char const * const gClientApiString = "OpenGL ES"; - struct extention_map_t { const char* name; - void (*address)(void); + __eglMustCastToProperFunctionPointerType address; }; static const extention_map_t gExtentionMap[] = { - { "eglSwapRectangleANDROID", (void(*)())&eglSwapRectangleANDROID }, - { "eglQueryStringConfigANDROID", (void(*)())&eglQueryStringConfigANDROID }, }; static extention_map_t gGLExtentionMap[MAX_NUMBER_OF_GL_EXTENSIONS]; @@ -603,110 +380,19 @@ static void(*findProcAddress(const char* name, return NULL; } -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- - -using namespace android; - - -// ---------------------------------------------------------------------------- -// extensions for the framework -// ---------------------------------------------------------------------------- - -void glColorPointerBounds(GLint size, GLenum type, GLsizei stride, - const GLvoid *ptr, GLsizei count) { - glColorPointer(size, type, stride, ptr); -} -void glNormalPointerBounds(GLenum type, GLsizei stride, - const GLvoid *pointer, GLsizei count) { - glNormalPointer(type, stride, pointer); -} -void glTexCoordPointerBounds(GLint size, GLenum type, - GLsizei stride, const GLvoid *pointer, GLsizei count) { - glTexCoordPointer(size, type, stride, pointer); -} -void glVertexPointerBounds(GLint size, GLenum type, - GLsizei stride, const GLvoid *pointer, GLsizei count) { - glVertexPointer(size, type, stride, pointer); -} - - -// ---------------------------------------------------------------------------- -// Actual GL wrappers -// ---------------------------------------------------------------------------- - -#if __OPTIMIZE__ && defined(__arm__) && !defined(__thumb__) && !USE_SLOW_BINDING && !GL_LOGGER - - #define API_ENTRY(_api) __attribute__((naked)) _api - #define CALL_GL_API(_api, ...) \ - asm volatile( \ - "mov r12, #0xFFFF0FFF \n" \ - "ldr r12, [r12, #-15] \n" \ - "ldr r12, [r12, %[tls]] \n" \ - "cmp r12, #0 \n" \ - "ldrne pc, [r12, %[api]] \n" \ - "bx lr \n" \ - : \ - : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ - [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \ - : \ - ); - - #define CALL_GL_API_RETURN(_api, ...) \ - CALL_GL_API(_api, __VA_ARGS__) \ - return 0; // placate gcc's warnings. never reached. - -#else - - #define API_ENTRY(_api) _api - #if GL_LOGGER - - #define CALL_GL_API(_api, ...) \ - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ - log_##_api(__VA_ARGS__); \ - _c->_api(__VA_ARGS__); - - #define CALL_GL_API_RETURN(_api, ...) \ - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ - log_##_api(__VA_ARGS__); \ - return _c->_api(__VA_ARGS__) - - #else - - #define CALL_GL_API(_api, ...) \ - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ - _c->_api(__VA_ARGS__); - - #define CALL_GL_API_RETURN(_api, ...) \ - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ - return _c->_api(__VA_ARGS__) - - #endif - -#endif - -#include "gl_api.cpp" - -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN - -// ---------------------------------------------------------------------------- -namespace android { // ---------------------------------------------------------------------------- static int gl_context_lost() { - setGlThreadSpecific(&gHooks[IMPL_HARDWARE_CONTEXT_LOST]); + setGlThreadSpecific(&gHooks[IMPL_CONTEXT_LOST]); return 0; } static int egl_context_lost() { - setGlThreadSpecific(&gHooks[IMPL_HARDWARE_CONTEXT_LOST]); + setGlThreadSpecific(&gHooks[IMPL_CONTEXT_LOST]); return EGL_FALSE; } static EGLBoolean egl_context_lost_swap_buffers(void*, void*) { usleep(100000); // don't use all the CPU - setGlThreadSpecific(&gHooks[IMPL_HARDWARE_CONTEXT_LOST]); + setGlThreadSpecific(&gHooks[IMPL_CONTEXT_LOST]); return EGL_FALSE; } static GLint egl_context_lost_get_error() { @@ -721,11 +407,14 @@ static void gl_no_context() { } static void early_egl_init(void) { -#if !defined(HAVE_ANDROID_OS) || USE_SLOW_BINDING || GL_LOGGER +#if !USE_FAST_TLS_KEY pthread_key_create(&gGLWrapperKey, NULL); #endif uint32_t addr = (uint32_t)((void*)gl_no_context); - android_memset32((uint32_t*)(void*)&gHooks[IMPL_NO_CONTEXT], addr, sizeof(gHooks[IMPL_NO_CONTEXT])); + android_memset32( + (uint32_t*)(void*)&gHooks[IMPL_NO_CONTEXT], + addr, + sizeof(gHooks[IMPL_NO_CONTEXT])); setGlThreadSpecific(&gHooks[IMPL_NO_CONTEXT]); } @@ -802,25 +491,12 @@ static EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface) return EGL_TRUE; } -static void add_extension(egl_display_t* dp, char const*& p, const char* ext) -{ - if (!strstr(p, ext)) { - p = (char const*)realloc((void*)p, strlen(p) + 1 + strlen(ext) + 1); - strcat((char*)p, " "); - strcat((char*)p, ext); - } - if (!strstr(dp->extensionsString, ext)) { - char const*& es = dp->extensionsString; - es = (char const*)realloc((void*)es, strlen(es) + 1 + strlen(ext) + 1); - strcat((char*)es, " "); - strcat((char*)es, ext); - } -} - // ---------------------------------------------------------------------------- }; // namespace android // ---------------------------------------------------------------------------- +using namespace android; + EGLDisplay eglGetDisplay(NativeDisplayType display) { if (sEarlyInitState) { @@ -867,25 +543,25 @@ EGLDisplay eglGetDisplay(NativeDisplayType display) } if (cnx->dso && d->dpys[IMPL_HARDWARE]==EGL_NO_DISPLAY) { android_memset32( - (uint32_t*)(void*)&gHooks[IMPL_HARDWARE_CONTEXT_LOST].gl, + (uint32_t*)(void*)&gHooks[IMPL_CONTEXT_LOST].gl, (uint32_t)((void*)gl_context_lost), - sizeof(gHooks[IMPL_HARDWARE_CONTEXT_LOST].gl)); + sizeof(gHooks[IMPL_CONTEXT_LOST].gl)); android_memset32( - (uint32_t*)(void*)&gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl, + (uint32_t*)(void*)&gHooks[IMPL_CONTEXT_LOST].egl, (uint32_t)((void*)egl_context_lost), - sizeof(gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl)); + sizeof(gHooks[IMPL_CONTEXT_LOST].egl)); android_memset32( - (uint32_t*)(void*)&gHooks[IMPL_HARDWARE_CONTEXT_LOST].ext, + (uint32_t*)(void*)&gHooks[IMPL_CONTEXT_LOST].ext, (uint32_t)((void*)ext_context_lost), - sizeof(gHooks[IMPL_HARDWARE_CONTEXT_LOST].ext)); + sizeof(gHooks[IMPL_CONTEXT_LOST].ext)); - gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl.eglSwapBuffers = + gHooks[IMPL_CONTEXT_LOST].egl.eglSwapBuffers = egl_context_lost_swap_buffers; - gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl.eglGetError = + gHooks[IMPL_CONTEXT_LOST].egl.eglGetError = egl_context_lost_get_error; - gHooks[IMPL_HARDWARE_CONTEXT_LOST].egl.eglTerminate = + gHooks[IMPL_CONTEXT_LOST].egl.eglTerminate = gHooks[IMPL_HARDWARE].egl.eglTerminate; d->dpys[IMPL_HARDWARE] = cnx->hooks->egl.eglGetDisplay(display); @@ -913,8 +589,8 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); if (android_atomic_inc(&dp->refs) > 0) { - if (major != NULL) *major = 1; - if (minor != NULL) *minor = 2; + if (major != NULL) *major = VERSION_MAJOR; + if (minor != NULL) *minor = VERSION_MINOR; return EGL_TRUE; } @@ -923,7 +599,7 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) // initialize each EGL and // build our own extension string first, based on the extension we know // and the extension supported by our client implementation - dp->extensionsString = strdup("EGL_ANDROID_query_string_config"); + dp->extensionsString = strdup(gExtensionString); for (int i=0 ; i<2 ; i++) { egl_connection_t* const cnx = &gEGLImpl[i]; cnx->major = -1; @@ -947,54 +623,16 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) dp->queryString[i].clientApi = cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_CLIENT_APIS); - // Dynamically insert extensions we know about - if (cnx->hooks->egl.eglSwapRectangleANDROID) - add_extension(dp, dp->queryString[i].extensions, - "EGL_ANDROID_swap_rectangle"); - - if (cnx->hooks->egl.eglQueryStringConfigANDROID) - add_extension(dp, dp->queryString[i].extensions, - "EGL_ANDROID_query_string_config"); } else { LOGD("%d: eglInitialize() failed (%s)", i, egl_strerror(cnx->hooks->egl.eglGetError())); } } - // Build the extension list that depends on the current config. - // It is the intersection of our extension list and the - // underlying EGL's extensions list EGLBoolean res = EGL_FALSE; for (int i=0 ; i<2 ; i++) { egl_connection_t* const cnx = &gEGLImpl[i]; if (cnx->dso && cnx->major>=0 && cnx->minor>=0) { - char const* const their_extensions = dp->queryString[i].extensions; - char* our_extensions = strdup(dp->extensionsString); - char* const our_extensions_org = our_extensions; - char* extensions_config = (char*)calloc(strlen(our_extensions)+2, 1); - char* p; - do { - p = strchr(our_extensions, ' '); - if (p) *p++ = 0; - else p = strchr(our_extensions, 0); - if (strstr(their_extensions, our_extensions)) { - strcat(extensions_config, our_extensions); - strcat(extensions_config, " "); - } - our_extensions = p; - } while (*p); - free((void*)our_extensions_org); - - // remove the trailing white space - if (extensions_config[0] != 0) { - size_t l = strlen(extensions_config) - 1; // new size - extensions_config[l] = 0; // remove the trailing white space - extensions_config = (char*)realloc(extensions_config, l+1); - } else { - extensions_config = (char*)realloc(extensions_config, 1); - } - dp->queryString[i].extensions_config = extensions_config; - EGLint n; if (cnx->hooks->egl.eglGetConfigs(dp->dpys[i], 0, 0, &n)) { dp->configs[i] = (EGLConfig*)malloc(sizeof(EGLConfig)*n); @@ -1042,7 +680,6 @@ EGLBoolean eglTerminate(EGLDisplay dpy) * threads around). */ free(dp->configs[i]); - free((void*)dp->queryString[i].extensions_config); free((void*)dp->queryString[i].extensions); dp->numConfigs[i] = 0; dp->dpys[i] = EGL_NO_DISPLAY; @@ -1486,7 +1123,7 @@ EGLint eglGetError(void) void (*eglGetProcAddress(const char *procname))() { - void (*addr)(); + __eglMustCastToProperFunctionPointerType addr; addr = findProcAddress(procname, gExtentionMap, NELEM(gExtentionMap)); if (addr) return addr; @@ -1570,7 +1207,7 @@ const char* eglQueryString(EGLDisplay dpy, EGLint name) case EGL_VERSION: return gVersionString; case EGL_EXTENSIONS: - return dp->extensionsString; + return gExtensionString; case EGL_CLIENT_APIS: return gClientApiString; } @@ -1730,34 +1367,3 @@ EGLSurface eglCreatePbufferFromClientBuffer( } return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); } - -// ---------------------------------------------------------------------------- -// Android extentions -// ---------------------------------------------------------------------------- - -EGLBoolean eglSwapRectangleANDROID( - EGLDisplay dpy, EGLSurface draw, - EGLint l, EGLint t, EGLint w, EGLint h) -{ - if (!validate_display_surface(dpy, draw)) - return EGL_FALSE; - egl_display_t const * const dp = get_display(dpy); - egl_surface_t const * const s = get_surface(draw); - if (s->cnx->hooks->egl.eglSwapRectangleANDROID) { - return s->cnx->hooks->egl.eglSwapRectangleANDROID( - dp->dpys[s->impl], s->surface, l, t, w, h); - } - return setError(EGL_BAD_SURFACE, EGL_FALSE); -} - -const char* eglQueryStringConfigANDROID( - EGLDisplay dpy, EGLConfig config, EGLint name) -{ - egl_display_t const* dp = 0; - int i=0, index=0; - egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); - if (cnx) { - return dp->queryString[i].extensions_config; - } - return setError(EGL_BAD_PARAMETER, (const char *)0); -} diff --git a/opengl/libs/EGL/gpu.cpp b/opengl/libs/EGL/gpu.cpp new file mode 100644 index 0000000000000..3f9fd63405871 --- /dev/null +++ b/opengl/libs/EGL/gpu.cpp @@ -0,0 +1,212 @@ +/* + ** Copyright 2007, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#define LOG_TAG "EGL" + +#include +#include +#include + +#include + +#if HAVE_ANDROID_OS +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "hooks.h" +#include "egl_impl.h" + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +/* + * we provide our own allocators for the GPU regions, these + * allocators go through surfaceflinger + */ + +static Mutex gRegionsLock; +static request_gpu_t gRegions; +static sp gSurfaceManager; +ISurfaceComposer* GLES_localSurfaceManager = 0; + +extern egl_connection_t gEGLImpl[2]; + +const sp& getSurfaceFlinger() +{ + Mutex::Autolock _l(gRegionsLock); + + /* + * There is a little bit of voodoo magic here. We want to access + * surfaceflinger for allocating GPU regions, however, when we are + * running as part of surfaceflinger, we want to bypass the + * service manager because surfaceflinger might not be registered yet. + * SurfaceFlinger will populate "GLES_localSurfaceManager" with its + * own address, so we can just use that. + */ + if (gSurfaceManager == 0) { + if (GLES_localSurfaceManager) { + // we're running in SurfaceFlinger's context + gSurfaceManager = GLES_localSurfaceManager; + } else { + // we're a remote process or not part of surfaceflinger, + // go through the service manager + sp sm = defaultServiceManager(); + if (sm != NULL) { + sp binder = sm->getService(String16("SurfaceFlinger")); + gSurfaceManager = interface_cast(binder); + } + } + } + return gSurfaceManager; +} + +class GPURevokeRequester : public BnGPUCallback +{ +public: + virtual void gpuLost() { + LOGD("CONTEXT_LOST: Releasing GPU upon request from SurfaceFlinger."); + gEGLImpl[IMPL_HARDWARE].hooks = &gHooks[IMPL_CONTEXT_LOST]; + } +}; + +static sp gRevokerCallback; + + +request_gpu_t* gpu_acquire(void* user) +{ + sp server( getSurfaceFlinger() ); + + Mutex::Autolock _l(gRegionsLock); + if (server == NULL) { + return 0; + } + + ISurfaceComposer::gpu_info_t info; + + if (gRevokerCallback == 0) + gRevokerCallback = new GPURevokeRequester(); + + status_t err = server->requestGPU(gRevokerCallback, &info); + if (err != NO_ERROR) { + LOGD("requestGPU returned %d", err); + return 0; + } + + bool failed = false; + request_gpu_t* gpu = &gRegions; + memset(gpu, 0, sizeof(*gpu)); + + if (info.regs != 0) { + sp heap(info.regs->getMemory()); + if (heap != 0) { + int fd = heap->heapID(); + gpu->regs.fd = fd; + gpu->regs.base = info.regs->pointer(); + gpu->regs.size = info.regs->size(); + gpu->regs.user = info.regs.get(); +#if HAVE_ANDROID_OS + struct pmem_region region; + if (ioctl(fd, PMEM_GET_PHYS, ®ion) >= 0) + gpu->regs.phys = (void*)region.offset; +#endif + info.regs->incStrong(gpu); + } else { + LOGE("GPU register handle %p is invalid!", info.regs.get()); + failed = true; + } + } + + for (size_t i=0 ; i& region(info.regions[i].region); + if (region != 0) { + sp heap(region->getMemory()); + if (heap != 0) { + const int fd = heap->heapID(); + gpu->gpu[i].fd = fd; + gpu->gpu[i].base = region->pointer(); + gpu->gpu[i].size = region->size(); + gpu->gpu[i].user = region.get(); + gpu->gpu[i].offset = info.regions[i].reserved; +#if HAVE_ANDROID_OS + struct pmem_region reg; + if (ioctl(fd, PMEM_GET_PHYS, ®) >= 0) + gpu->gpu[i].phys = (void*)reg.offset; +#endif + region->incStrong(gpu); + } else { + LOGE("GPU region handle [%d, %p] is invalid!", i, region.get()); + failed = true; + } + } + } + + if (failed) { + // something went wrong, clean up everything! + if (gpu->regs.user) { + static_cast(gpu->regs.user)->decStrong(gpu); + for (size_t i=0 ; igpu[i].user) { + static_cast(gpu->gpu[i].user)->decStrong(gpu); + } + } + } + } + + gpu->count = info.count; + return gpu; +} + +int gpu_release(void*, request_gpu_t* gpu) +{ + sp regs; + + { // scope for lock + Mutex::Autolock _l(gRegionsLock); + regs = static_cast(gpu->regs.user); + gpu->regs.user = 0; + if (regs != 0) regs->decStrong(gpu); + + for (int i=0 ; icount ; i++) { + sp r(static_cast(gpu->gpu[i].user)); + gpu->gpu[i].user = 0; + if (r != 0) r->decStrong(gpu); + } + } + + // there is a special transaction to relinquish the GPU + // (it will happen automatically anyway if we don't do this) + Parcel data, reply; + // NOTE: this transaction does not require an interface token + regs->asBinder()->transact(1000, data, &reply); + return 1; +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp new file mode 100644 index 0000000000000..865cf44362f0a --- /dev/null +++ b/opengl/libs/GLES_CM/gl.cpp @@ -0,0 +1,116 @@ +/* + ** Copyright 2007, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#define LOG_TAG "GLES_CM" + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "hooks.h" + +using namespace android; + +// ---------------------------------------------------------------------------- +// extensions for the framework +// ---------------------------------------------------------------------------- + +void glColorPointerBounds(GLint size, GLenum type, GLsizei stride, + const GLvoid *ptr, GLsizei count) { + glColorPointer(size, type, stride, ptr); +} +void glNormalPointerBounds(GLenum type, GLsizei stride, + const GLvoid *pointer, GLsizei count) { + glNormalPointer(type, stride, pointer); +} +void glTexCoordPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count) { + glTexCoordPointer(size, type, stride, pointer); +} +void glVertexPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count) { + glVertexPointer(size, type, stride, pointer); +} + +// ---------------------------------------------------------------------------- +// Actual GL entry-points +// ---------------------------------------------------------------------------- + +#if GL_LOGGER +# include "gl_logger.h" +# define GL_LOGGER_IMPL(_x) _x +#else +# define GL_LOGGER_IMPL(_x) +#endif + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_RETURN + +#if USE_FAST_TLS_KEY + + #define API_ENTRY(_api) __attribute__((naked)) _api + + #define CALL_GL_API(_api, ...) \ + asm volatile( \ + "mov r12, #0xFFFF0FFF \n" \ + "ldr r12, [r12, #-15] \n" \ + "ldr r12, [r12, %[tls]] \n" \ + "cmp r12, #0 \n" \ + "ldrne pc, [r12, %[api]] \n" \ + "bx lr \n" \ + : \ + : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ + [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \ + : \ + ); + + #define CALL_GL_API_RETURN(_api, ...) \ + CALL_GL_API(_api, __VA_ARGS__) \ + return 0; // placate gcc's warnings. never reached. + +#else + + #define API_ENTRY(_api) _api + + #define CALL_GL_API(_api, ...) \ + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ + GL_LOGGER_IMPL( log_##_api(__VA_ARGS__); ) \ + _c->_api(__VA_ARGS__) + + #define CALL_GL_API_RETURN(_api, ...) \ + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ + GL_LOGGER_IMPL( log_##_api(__VA_ARGS__); ) \ + return _c->_api(__VA_ARGS__) + +#endif + +extern "C" { +#include "gl_api.in" +} + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_RETURN + diff --git a/opengl/libGLES_CM/gl_api.cpp b/opengl/libs/GLES_CM/gl_api.in similarity index 99% rename from opengl/libGLES_CM/gl_api.cpp rename to opengl/libs/GLES_CM/gl_api.in index ed3bdaa8388bd..9234ef27387fb 100644 --- a/opengl/libGLES_CM/gl_api.cpp +++ b/opengl/libs/GLES_CM/gl_api.in @@ -415,7 +415,7 @@ void API_ENTRY(glTexEnvxv)(GLenum target, GLenum pname, const GLfixed *params) { CALL_GL_API(glTexEnvxv, target, pname, params); } -void API_ENTRY(glTexImage2D)( GLenum target, GLint level, GLenum internalformat, +void API_ENTRY(glTexImage2D)( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) { CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, diff --git a/opengl/libGLES_CM/gl_logger.cpp b/opengl/libs/GLES_CM/gl_logger.cpp similarity index 99% rename from opengl/libGLES_CM/gl_logger.cpp rename to opengl/libs/GLES_CM/gl_logger.cpp index 14b5a399e2937..27be5c9bac88f 100644 --- a/opengl/libGLES_CM/gl_logger.cpp +++ b/opengl/libs/GLES_CM/gl_logger.cpp @@ -23,7 +23,10 @@ #include -#include +#include +#include +#include +#include #include #include @@ -33,13 +36,13 @@ #include "gl_logger.h" +#undef NELEM +#define NELEM(x) (sizeof(x)/sizeof(*(x))) + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- -#undef NELEM -#define NELEM(x) (sizeof(x)/sizeof(*(x))) - template static int binarySearch(T const sortedArray[], int first, int last, EGLint key) { @@ -226,12 +229,6 @@ private: int mNumParams; }; -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- - -using namespace android; - #define API_ENTRY(api) log_##api #define CALL_GL_API(_x, ...) #define CALL_GL_API_RETURN(_x, ...) return(0); @@ -785,7 +782,7 @@ void API_ENTRY(glTexEnvxv)(GLenum target, GLenum pname, const GLfixed *params) { GLLog("glTexEnvxv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); } -void API_ENTRY(glTexImage2D)( GLenum target, GLint level, GLenum internalformat, +void API_ENTRY(glTexImage2D)( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) { CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, @@ -1057,3 +1054,7 @@ GLbitfield API_ENTRY(glQueryMatrixxOES)(GLfixed* mantissa, GLint* exponent) { GLLog("glQueryMatrixxOES") << GLLogBuffer(mantissa, 16) << GLLogBuffer(exponent, 16); CALL_GL_API_RETURN(glQueryMatrixxOES, mantissa, exponent); } + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/opengl/libGLES_CM/egl_entries.cpp b/opengl/libs/egl_entries.in similarity index 89% rename from opengl/libGLES_CM/egl_entries.cpp rename to opengl/libs/egl_entries.in index ba46f40647a2c..33b4c654d207f 100644 --- a/opengl/libGLES_CM/egl_entries.cpp +++ b/opengl/libs/egl_entries.in @@ -23,7 +23,7 @@ EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface) EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType) EGL_ENTRY(EGLint, eglGetError, void) EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint) -EGL_ENTRY(proc_t, eglGetProcAddress, const char *) +EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char *) /* EGL 1.1 */ @@ -40,7 +40,6 @@ EGL_ENTRY(EGLBoolean, eglWaitClient, void) EGL_ENTRY(EGLBoolean, eglReleaseThread, void) EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint *) -/* Android extentions */ +/* EGL 1.3 */ -EGL_ENTRY(EGLBoolean, eglSwapRectangleANDROID, EGLDisplay, EGLSurface , EGLint, EGLint, EGLint, EGLint) -EGL_ENTRY(const char*, eglQueryStringConfigANDROID, EGLDisplay, EGLConfig, EGLint) +/* EGL 1.4 */ diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h new file mode 100644 index 0000000000000..62ce3fce17009 --- /dev/null +++ b/opengl/libs/egl_impl.h @@ -0,0 +1,43 @@ +/* + ** Copyright 2007, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#ifndef ANDROID_EGL_IMPL_H +#define ANDROID_EGL_IMPL_H + +#include + +#include + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +struct gl_hooks_t; + +struct egl_connection_t +{ + void volatile * dso; + gl_hooks_t * hooks; + EGLint major; + EGLint minor; + int unavailable; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +#endif /* ANDROID_EGL_IMPL_H */ diff --git a/opengl/libGLES_CM/gl_entries.cpp b/opengl/libs/gl_entries.in similarity index 98% rename from opengl/libGLES_CM/gl_entries.cpp rename to opengl/libs/gl_entries.in index 3279322961b69..b97e8fe3c479d 100644 --- a/opengl/libGLES_CM/gl_entries.cpp +++ b/opengl/libs/gl_entries.in @@ -101,7 +101,7 @@ GL_ENTRY(void, glCompressedTexImage2D, GLenum, GLint, GLenum, GLsizei, GLsize GL_ENTRY(void, glCompressedTexSubImage2D, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*) GL_ENTRY(void, glCopyTexImage2D, GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint) GL_ENTRY(void, glCopyTexSubImage2D, GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei) -GL_ENTRY(void, glTexImage2D, GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*) +GL_ENTRY(void, glTexImage2D, GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*) GL_ENTRY(void, glTexSubImage2D, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*) GL_ENTRY(void, glReadPixels, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *) diff --git a/opengl/libGLES_CM/gl_enums.in b/opengl/libs/gl_enums.in similarity index 100% rename from opengl/libGLES_CM/gl_enums.in rename to opengl/libs/gl_enums.in diff --git a/opengl/libGLES_CM/gl_logger.h b/opengl/libs/gl_logger.h similarity index 92% rename from opengl/libGLES_CM/gl_logger.h rename to opengl/libs/gl_logger.h index 59e31c7ecea61..ce85dd1ad0d74 100644 --- a/opengl/libGLES_CM/gl_logger.h +++ b/opengl/libs/gl_logger.h @@ -17,10 +17,10 @@ #ifndef ANDROID_GL_LOGGER_H #define ANDROID_GL_LOGGER_H -extern "C" { +namespace android { #define GL_ENTRY(r, api, ...) r log_##api(__VA_ARGS__); -#include "gl_entries.cpp" +#include "gl_entries.in" #undef GL_ENTRY -}; +}; // namespace android #endif /* ANDROID_GL_LOGGER_H */ diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h new file mode 100644 index 0000000000000..63fb017675bf1 --- /dev/null +++ b/opengl/libs/hooks.h @@ -0,0 +1,134 @@ +/* + ** Copyright 2007, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#ifndef ANDROID_GLES_CM_HOOKS_H +#define ANDROID_GLES_CM_HOOKS_H + +#include +#include +#include + +#include +#include + +#define GL_LOGGER 0 +#if !defined(__arm__) +#define USE_SLOW_BINDING 1 +#else +#define USE_SLOW_BINDING 0 +#endif +#undef NELEM +#define NELEM(x) (sizeof(x)/sizeof(*(x))) +#define MAX_NUMBER_OF_GL_EXTENSIONS 32 + + +#if defined(HAVE_ANDROID_OS) && !USE_SLOW_BINDING && !GL_LOGGER && __OPTIMIZE__ +#define USE_FAST_TLS_KEY 1 +#else +#define USE_FAST_TLS_KEY 0 +#endif + +#if USE_FAST_TLS_KEY +# include /* special private C library header */ +#endif + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +// EGLDisplay are global, not attached to a given thread +const unsigned int NUM_DISPLAYS = 1; + +enum { + IMPL_HARDWARE = 0, + IMPL_SOFTWARE, + IMPL_CONTEXT_LOST, + IMPL_NO_CONTEXT, + + IMPL_NUM_IMPLEMENTATIONS +}; + +// ---------------------------------------------------------------------------- + +// GL / EGL hooks + +#undef GL_ENTRY +#undef EGL_ENTRY +#define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__); +#define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__); + +struct gl_hooks_t { + struct gl_t { + #include "gl_entries.in" + } gl; + struct egl_t { + #include "egl_entries.in" + } egl; + struct gl_ext_t { + void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void); + } ext; +}; +#undef GL_ENTRY +#undef EGL_ENTRY + + +// ---------------------------------------------------------------------------- + +extern gl_hooks_t gHooks[IMPL_NUM_IMPLEMENTATIONS]; +extern pthread_key_t gGLWrapperKey; + +#if USE_FAST_TLS_KEY + +// We have a dedicated TLS slot in bionic +static inline gl_hooks_t const * volatile * get_tls_hooks() { + volatile void *tls_base = __get_tls(); + gl_hooks_t const * volatile * tls_hooks = + reinterpret_cast(tls_base); + return tls_hooks; +} + +static inline void setGlThreadSpecific(gl_hooks_t const *value) { + gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); + tls_hooks[TLS_SLOT_OPENGL_API] = value; +} + +static gl_hooks_t const* getGlThreadSpecific() { + gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); + gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API]; + if (hooks) return hooks; + return &gHooks[IMPL_NO_CONTEXT]; +} + +#else + +static inline void setGlThreadSpecific(gl_hooks_t const *value) { + pthread_setspecific(gGLWrapperKey, value); +} + +static gl_hooks_t const* getGlThreadSpecific() { + gl_hooks_t const* hooks = static_cast(pthread_getspecific(gGLWrapperKey)); + if (hooks) return hooks; + return &gHooks[IMPL_NO_CONTEXT]; +} + +#endif + + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +#endif /* ANDROID_GLES_CM_HOOKS_H */ diff --git a/opengl/libGLES_CM/enumextract.sh b/opengl/libs/tools/enumextract.sh similarity index 100% rename from opengl/libGLES_CM/enumextract.sh rename to opengl/libs/tools/enumextract.sh diff --git a/opengl/tests/angeles/Android.mk b/opengl/tests/angeles/Android.mk index 41673cbe41b7d..46958d3c3c552 100644 --- a/opengl/tests/angeles/Android.mk +++ b/opengl/tests/angeles/Android.mk @@ -3,7 +3,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= app-linux.c demo.c.arm -LOCAL_SHARED_LIBRARIES := libGLES_CM libui +LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM libui LOCAL_MODULE:= angeles LOCAL_MODULE_TAGS := tests include $(BUILD_EXECUTABLE) @@ -11,7 +11,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES:= gpustate.c -LOCAL_SHARED_LIBRARIES := libGLES_CM +LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM LOCAL_MODULE:= gpustate LOCAL_MODULE_TAGS := tests include $(BUILD_EXECUTABLE) diff --git a/opengl/tests/angeles/app-linux.c b/opengl/tests/angeles/app-linux.c index d439eb2fbfb74..7d0d320a32416 100644 --- a/opengl/tests/angeles/app-linux.c +++ b/opengl/tests/angeles/app-linux.c @@ -49,7 +49,8 @@ #include #include -#include +#include +#include #include "app.h" diff --git a/opengl/tests/filter/Android.mk b/opengl/tests/filter/Android.mk index 1c4253c2c0cc2..a448f0d461611 100644 --- a/opengl/tests/filter/Android.mk +++ b/opengl/tests/filter/Android.mk @@ -6,7 +6,8 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libcutils \ - libGLES_CM \ + libEGL \ + libGLESv1_CM \ libui LOCAL_MODULE:= test-opengl-filter diff --git a/opengl/tests/filter/filter.c b/opengl/tests/filter/filter.c index c8bac06cd5621..de9711963e654 100644 --- a/opengl/tests/filter/filter.c +++ b/opengl/tests/filter/filter.c @@ -1,7 +1,9 @@ #include #include -#include +#include +#include +#include int main(int argc, char** argv) { @@ -40,6 +42,9 @@ int main(int argc, char** argv) printf("using pbuffer\n"); EGLint attribs[] = { EGL_WIDTH, 320, EGL_HEIGHT, 480, EGL_NONE }; surface = eglCreatePbufferSurface(dpy, config, attribs); + if (surface == EGL_NO_SURFACE) { + printf("eglCreatePbufferSurface error %x\n", eglGetError()); + } } context = eglCreateContext(dpy, config, NULL, NULL); eglMakeCurrent(dpy, surface, surface, context); diff --git a/opengl/tests/finish/Android.mk b/opengl/tests/finish/Android.mk index f7b95edccb914..26836c1caf4cb 100644 --- a/opengl/tests/finish/Android.mk +++ b/opengl/tests/finish/Android.mk @@ -6,7 +6,8 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libcutils \ - libGLES_CM \ + libEGL \ + libGLESv1_CM \ libui LOCAL_MODULE:= test-opengl-finish diff --git a/opengl/tests/finish/finish.c b/opengl/tests/finish/finish.c index 3afe227681bc3..45fc7587befb0 100644 --- a/opengl/tests/finish/finish.c +++ b/opengl/tests/finish/finish.c @@ -20,7 +20,10 @@ #include #include -#include +#include +#include +#include + long long systemTime() { diff --git a/opengl/tests/sfsim/Android.mk b/opengl/tests/sfsim/Android.mk deleted file mode 100644 index 8a1a03cc42c1c..0000000000000 --- a/opengl/tests/sfsim/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - egl_surface.cpp \ - sfsim.c - -LOCAL_SHARED_LIBRARIES := \ - libGLES_CM - -LOCAL_MODULE:= test-opengl-sfsim - -LOCAL_MODULE_TAGS := tests - -include $(BUILD_EXECUTABLE) diff --git a/opengl/tests/sfsim/egl_surface.cpp b/opengl/tests/sfsim/egl_surface.cpp deleted file mode 100644 index b0777f8c1c02e..0000000000000 --- a/opengl/tests/sfsim/egl_surface.cpp +++ /dev/null @@ -1,346 +0,0 @@ -/* - ** - ** Copyright 2008 The Android Open Source Project - ** - ** Licensed under the Apache License Version 2.0(the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing software - ** distributed under the License is distributed on an "AS IS" BASIS - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "egl_surface.h" - -#define LOGI(x...) do { printf("INFO: " x); } while (0) -#define LOGW(x...) do { printf("WARN: " x); } while (0) -#define LOGE(x...) do { printf("ERR: " x); } while (0) - -// ---------------------------------------------------------------------------- - -egl_native_window_t* android_createDisplaySurface() -{ - egl_native_window_t* s = new android::EGLDisplaySurface(); - s->memory_type = NATIVE_MEMORY_TYPE_GPU; - return s; -} - -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) - -// ---------------------------------------------------------------------------- -namespace android { -// ---------------------------------------------------------------------------- - -EGLDisplaySurface::EGLDisplaySurface() - : EGLNativeSurface() -{ - egl_native_window_t::version = sizeof(egl_native_window_t); - egl_native_window_t::ident = 0; - egl_native_window_t::incRef = &EGLDisplaySurface::hook_incRef; - egl_native_window_t::decRef = &EGLDisplaySurface::hook_decRef; - egl_native_window_t::swapBuffers = &EGLDisplaySurface::hook_swapBuffers; - egl_native_window_t::setSwapRectangle = &EGLDisplaySurface::hook_setSwapRectangle; - egl_native_window_t::nextBuffer = &EGLDisplaySurface::hook_nextBuffer; - egl_native_window_t::connect = 0; - egl_native_window_t::disconnect = 0; - - mFb[0].data = 0; - mFb[1].data = 0; - egl_native_window_t::fd = mapFrameBuffer(); - if (egl_native_window_t::fd >= 0) { - const float in2mm = 25.4f; - float refreshRate = 1000000000000000LLU / ( - float( mInfo.upper_margin + mInfo.lower_margin + mInfo.yres ) - * ( mInfo.left_margin + mInfo.right_margin + mInfo.xres ) - * mInfo.pixclock); - - const GGLSurface& buffer = mFb[1 - mIndex]; - egl_native_window_t::width = buffer.width; - egl_native_window_t::height = buffer.height; - egl_native_window_t::stride = buffer.stride; - egl_native_window_t::format = buffer.format; - egl_native_window_t::base = intptr_t(mFb[0].data); - egl_native_window_t::offset = - intptr_t(buffer.data) - egl_native_window_t::base; - egl_native_window_t::flags = 0; - egl_native_window_t::xdpi = (mInfo.xres * in2mm) / mInfo.width; - egl_native_window_t::ydpi = (mInfo.yres * in2mm) / mInfo.height; - egl_native_window_t::fps = refreshRate; - egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_FB; - // no error, set the magic word - egl_native_window_t::magic = 0x600913; - } - mSwapCount = -1; - mPageFlipCount = 0; -} - -EGLDisplaySurface::~EGLDisplaySurface() -{ - magic = 0; - close(egl_native_window_t::fd); - munmap(mFb[0].data, mSize); - if (!(mFlags & PAGE_FLIP)) - free((void*)mFb[1].data); -} - -void EGLDisplaySurface::hook_incRef(NativeWindowType window) { - EGLDisplaySurface* that = static_cast(window); - that->incStrong(that); -} -void EGLDisplaySurface::hook_decRef(NativeWindowType window) { - EGLDisplaySurface* that = static_cast(window); - that->decStrong(that); -} -uint32_t EGLDisplaySurface::hook_swapBuffers(NativeWindowType window) { - EGLDisplaySurface* that = static_cast(window); - return that->swapBuffers(); -} -uint32_t EGLDisplaySurface::hook_nextBuffer(NativeWindowType window) { - EGLDisplaySurface* that = static_cast(window); - return that->nextBuffer(); -} -void EGLDisplaySurface::hook_setSwapRectangle(NativeWindowType window, - int l, int t, int w, int h) { - EGLDisplaySurface* that = static_cast(window); - that->setSwapRectangle(l, t, w, h); -} - -void EGLDisplaySurface::setSwapRectangle(int l, int t, int w, int h) -{ - mInfo.reserved[0] = 0x54445055; // "UPDT"; - mInfo.reserved[1] = (uint16_t)l | ((uint32_t)t << 16); - mInfo.reserved[2] = (uint16_t)(l+w) | ((uint32_t)(t+h) << 16); -} - -uint32_t EGLDisplaySurface::swapBuffers() -{ - if (!(mFlags & PAGE_FLIP)) - return 0; - - // do the actual flip - mIndex = 1 - mIndex; - mInfo.activate = FB_ACTIVATE_VBL; - mInfo.yoffset = mIndex ? mInfo.yres : 0; - if (ioctl(egl_native_window_t::fd, FBIOPUT_VSCREENINFO, &mInfo) == -1) { - LOGE("FBIOPUT_VSCREENINFO failed"); - return 0; - } - - /* - * this is a monstruous hack: Because the h/w accelerator is not able - * to render directly into the framebuffer, we need to copy its - * internal framebuffer out to the fb. the base address of the internal fb - * is given in oem[0]. - * All this is needed only in standalone mode, in SurfaceFlinger mode - * we control where the GPU renders. - */ - if (egl_native_window_t::memory_type == NATIVE_MEMORY_TYPE_GPU && oem[0]) { - // could use MDP here, but that's tricky because we need - // /dev/pmem_gpu* filedescriptor - const GGLSurface& buffer = mFb[mIndex]; - memcpy( buffer.data, - (void*)(oem[0] + egl_native_window_t::offset), - buffer.stride*buffer.height*2); - } - - // update the address of the buffer to draw to next - const GGLSurface& buffer = mFb[1 - mIndex]; - egl_native_window_t::offset = - intptr_t(buffer.data) - egl_native_window_t::base; - - mPageFlipCount++; - - // We don't support screen-size changes for now - return 0; -} - -int32_t EGLDisplaySurface::getPageFlipCount() const -{ - return mPageFlipCount; -} - -uint32_t EGLDisplaySurface::nextBuffer() -{ - // update the address of the buffer to draw to next - const GGLSurface& buffer = mFb[mIndex]; - egl_native_window_t::offset = - intptr_t(buffer.data) - egl_native_window_t::base; - return 0; -} - -int EGLDisplaySurface::mapFrameBuffer() -{ - char const * const device_template[] = { - "/dev/graphics/fb%u", - "/dev/fb%u", - 0 }; - int fd = -1; - int i=0; - char name[64]; - while ((fd==-1) && device_template[i]) { - snprintf(name, 64, device_template[i], 0); - fd = open(name, O_RDWR, 0); - i++; - } - if (fd < 0) - return -errno; - - struct fb_fix_screeninfo finfo; - if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) - return -errno; - - struct fb_var_screeninfo info; - if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) - return -errno; - - info.reserved[0] = 0; - info.reserved[1] = 0; - info.reserved[2] = 0; - info.xoffset = 0; - info.yoffset = 0; - info.yres_virtual = info.yres * 2; - info.bits_per_pixel = 16; - info.activate = FB_ACTIVATE_NOW; - - uint32_t flags = PAGE_FLIP; - if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { - info.yres_virtual = info.yres; - flags &= ~PAGE_FLIP; - LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); - } - - if (info.yres_virtual < info.yres * 2) { - info.yres_virtual = info.yres; - flags &= ~PAGE_FLIP; - LOGW("page flipping not supported (yres_virtual=%d, requested=%d)", - info.yres_virtual, info.yres*2); - } - - if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) - return -errno; - - int refreshRate = 1000000000000000LLU / - ( - uint64_t( info.upper_margin + info.lower_margin + info.yres ) - * ( info.left_margin + info.right_margin + info.xres ) - * info.pixclock - ); - - if (refreshRate == 0) { - // bleagh, bad info from the driver - refreshRate = 60*1000; // 60 Hz - } - - if (int(info.width) <= 0 || int(info.height) <= 0) { - // stupid driver, doesn't return that information - // default to Sooner's screen size (160 dpi) - info.width = 51; - info.height = 38; - } - - float xdpi = (info.xres * 25.4f) / info.width; - float ydpi = (info.yres * 25.4f) / info.height; - float fps = refreshRate / 1000.0f; - - LOGI( "using (fd=%d)\n" - "id = %s\n" - "xres = %d px\n" - "yres = %d px\n" - "xres_virtual = %d px\n" - "yres_virtual = %d px\n" - "bpp = %d\n" - "r = %2u:%u\n" - "g = %2u:%u\n" - "b = %2u:%u\n", - fd, - finfo.id, - info.xres, - info.yres, - info.xres_virtual, - info.yres_virtual, - info.bits_per_pixel, - info.red.offset, info.red.length, - info.green.offset, info.green.length, - info.blue.offset, info.blue.length - ); - - LOGI( "width = %d mm (%f dpi)\n" - "height = %d mm (%f dpi)\n" - "refresh rate = %.2f Hz\n", - info.width, xdpi, - info.height, ydpi, - fps - ); - - - if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) - return -errno; - - if (finfo.smem_len <= 0) - return -errno; - - /* - * Open and map the display. - */ - - void* buffer = (uint16_t*) mmap( - 0, finfo.smem_len, - PROT_READ | PROT_WRITE, - MAP_SHARED, - fd, 0); - - if (buffer == MAP_FAILED) - return -errno; - - // at least for now, always clear the fb - memset(buffer, 0, finfo.smem_len); - - uint8_t* offscreen[2]; - offscreen[0] = (uint8_t*)buffer; - if (flags & PAGE_FLIP) { - offscreen[1] = (uint8_t*)buffer + finfo.line_length*info.yres; - } else { - offscreen[1] = (uint8_t*)malloc(finfo.smem_len); - if (offscreen[1] == 0) { - munmap(buffer, finfo.smem_len); - return -ENOMEM; - } - } - - mFlags = flags; - mInfo = info; - mFinfo = finfo; - mSize = finfo.smem_len; - mIndex = 0; - for (int i=0 ; i<2 ; i++) { - mFb[i].version = sizeof(GGLSurface); - mFb[i].width = info.xres; - mFb[i].height = info.yres; - mFb[i].stride = finfo.line_length / (info.bits_per_pixel >> 3); - mFb[i].data = (uint8_t*)(offscreen[i]); - mFb[i].format = NATIVE_PIXEL_FORMAT_RGB_565; - } - return fd; -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- diff --git a/opengl/tests/sfsim/egl_surface.h b/opengl/tests/sfsim/egl_surface.h deleted file mode 100644 index 70a94fcab9940..0000000000000 --- a/opengl/tests/sfsim/egl_surface.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_SIM_EGL_SURFACE_H -#define ANDROID_SIM_EGL_SURFACE_H - -#include -#include -#include - -#include - -#include - -typedef struct { - ssize_t version; // always set to sizeof(GGLSurface) - uint32_t width; // width in pixels - uint32_t height; // height in pixels - int32_t stride; // stride in pixels - uint8_t* data; // pointer to the bits - uint8_t format; // pixel format - uint8_t rfu[3]; // must be zero - void* reserved; -} GGLSurface; - -// --------------------------------------------------------------------------- -namespace android { -// --------------------------------------------------------------------------- - -template -class EGLNativeSurface : public egl_native_window_t -{ -public: - EGLNativeSurface() : mCount(0) { - memset(egl_native_window_t::reserved, 0, - sizeof(egl_native_window_t::reserved)); - memset(egl_native_window_t::reserved_proc, 0, - sizeof(egl_native_window_t::reserved_proc)); - memset(egl_native_window_t::oem, 0, - sizeof(egl_native_window_t::oem)); - } - inline void incStrong(void*) const { - /* in a real implementation, the inc must be atomic */ - mCount++; - } - inline void decStrong(void*) const { - /* in a real implementation, the dec must be atomic */ - if (--mCount == 1) { - delete static_cast(this); - } - } -protected: - EGLNativeSurface& operator = (const EGLNativeSurface& rhs); - EGLNativeSurface(const EGLNativeSurface& rhs); - inline ~EGLNativeSurface() { }; - mutable volatile int32_t mCount; -}; - - -class EGLDisplaySurface : public EGLNativeSurface -{ -public: - EGLDisplaySurface(); - ~EGLDisplaySurface(); - - int32_t getPageFlipCount() const; - -private: - static void hook_incRef(NativeWindowType window); - static void hook_decRef(NativeWindowType window); - static uint32_t hook_swapBuffers(NativeWindowType window); - static void hook_setSwapRectangle(NativeWindowType window, int l, int t, int w, int h); - static uint32_t hook_nextBuffer(NativeWindowType window); - - uint32_t swapBuffers(); - uint32_t nextBuffer(); - void setSwapRectangle(int l, int t, int w, int h); - - int mapFrameBuffer(); - - enum { - PAGE_FLIP = 0x00000001 - }; - GGLSurface mFb[2]; - int mIndex; - uint32_t mFlags; - size_t mSize; - fb_var_screeninfo mInfo; - fb_fix_screeninfo mFinfo; - int32_t mPageFlipCount; - int32_t mSwapCount; - uint32_t mFeatureFlags; -}; - -// --------------------------------------------------------------------------- -}; // namespace android -// --------------------------------------------------------------------------- - -#endif // ANDROID_SIM_EGL_SURFACE_H - diff --git a/opengl/tests/sfsim/sfsim.c b/opengl/tests/sfsim/sfsim.c deleted file mode 100644 index 14ba490d70501..0000000000000 --- a/opengl/tests/sfsim/sfsim.c +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include - -#include - -int main(int argc, char** argv) -{ - if (argc != 2) { - printf("usage: %s <0-6>\n", argv[0]); - return 0; - } - - const int test = atoi(argv[1]); - - EGLint s_configAttribs[] = { - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, - EGL_NONE - }; - - EGLint numConfigs = -1; - EGLint majorVersion; - EGLint minorVersion; - EGLConfig config; - EGLContext context; - EGLSurface surface; - EGLint w, h; - - EGLDisplay dpy; - - dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(dpy, &majorVersion, &minorVersion); - eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs); - surface = eglCreateWindowSurface(dpy, config, - android_createDisplaySurface(), NULL); - context = eglCreateContext(dpy, config, NULL, NULL); - eglMakeCurrent(dpy, surface, surface, context); - eglQuerySurface(dpy, surface, EGL_WIDTH, &w); - eglQuerySurface(dpy, surface, EGL_HEIGHT, &h); - GLint dim = w #include -#include +#include +#include +#include int main(int argc, char** argv) { diff --git a/opengl/tests/tritex/Android.mk b/opengl/tests/tritex/Android.mk index 4edac4cb4f96c..5cd1f048a309e 100644 --- a/opengl/tests/tritex/Android.mk +++ b/opengl/tests/tritex/Android.mk @@ -6,7 +6,8 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libcutils \ - libGLES_CM \ + libEGL \ + libGLESv1_CM \ libui LOCAL_MODULE:= test-opengl-tritex diff --git a/opengl/tests/tritex/tritex.c b/opengl/tests/tritex/tritex.c index 8ebe7d4d72cfa..60a7feb9b8404 100644 --- a/opengl/tests/tritex/tritex.c +++ b/opengl/tests/tritex/tritex.c @@ -4,7 +4,9 @@ // // Ported from a Java version by Google. -#include +#include +#include + #include #include #include diff --git a/packages/SettingsProvider/etc/bookmarks.xml b/packages/SettingsProvider/etc/bookmarks.xml index 235e2edf60001..5fb6608be4003 100644 --- a/packages/SettingsProvider/etc/bookmarks.xml +++ b/packages/SettingsProvider/etc/bookmarks.xml @@ -15,14 +15,44 @@ --> - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml new file mode 100644 index 0000000000000..72d88e252a46f --- /dev/null +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -0,0 +1,41 @@ + + + + true + 60000 + false + + cell,bluetooth,wifi + true + true + + 102 + 0% + 0% + + false + false + + network + + 1 + true + false + true + diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index e2ea85be54cc2..90acd41c6a1a5 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; @@ -63,7 +64,7 @@ class DatabaseHelper extends SQLiteOpenHelper { private static final String TAG = "SettingsProvider"; private static final String DATABASE_NAME = "settings.db"; - private static final int DATABASE_VERSION = 31; + private static final int DATABASE_VERSION = 32; private Context mContext; @@ -332,6 +333,27 @@ class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 31; } + if (upgradeVersion == 31) { + /* + * Animations are now turned off by default. + */ + db.beginTransaction(); + try { + db.execSQL("DELETE FROM system WHERE name='" + + Settings.System.WINDOW_ANIMATION_SCALE + "'"); + db.execSQL("DELETE FROM system WHERE name='" + + Settings.System.TRANSITION_ANIMATION_SCALE + "'"); + SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)" + + " VALUES(?,?);"); + loadDefaultAnimationSettings(stmt); + stmt.close(); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + upgradeVersion = 32; + } + if (upgradeVersion != currentVersion) { Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion + ", must wipe the settings provider"); @@ -529,28 +551,31 @@ class DatabaseHelper extends SQLiteOpenHelper { SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + " VALUES(?,?);"); - loadSetting(stmt, Settings.System.DIM_SCREEN, 1); + Resources r = mContext.getResources(); + loadBooleanSetting(stmt, Settings.System.DIM_SCREEN, + R.bool.def_dim_screen); loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN, "1".equals(SystemProperties.get("ro.kernel.qemu")) ? 1 : 0); - loadSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT, 60000); - // Allow airplane mode to turn off cell radio - loadSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS, - Settings.System.RADIO_CELL + "," - + Settings.System.RADIO_BLUETOOTH + "," + Settings.System.RADIO_WIFI); + loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT, + R.integer.def_screen_off_timeout); - loadSetting(stmt, Settings.System.AIRPLANE_MODE_ON, 0); + loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON, + R.bool.def_airplane_mode_on); - loadSetting(stmt, Settings.System.AUTO_TIME, 1); // Sync time to NITZ + loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS, + R.string.def_airplane_mode_radios); - // Set default brightness to 40% - loadSetting(stmt, Settings.System.SCREEN_BRIGHTNESS, - (int) (android.os.Power.BRIGHTNESS_ON * 0.4f)); + loadBooleanSetting(stmt, Settings.System.AUTO_TIME, + R.bool.def_auto_time); // Sync time to NITZ - // Enable normal window animations (menus, toasts); disable - // activity transition animations. - loadSetting(stmt, Settings.System.WINDOW_ANIMATION_SCALE, "1"); - loadSetting(stmt, Settings.System.TRANSITION_ANIMATION_SCALE, "1"); + loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS, + R.integer.def_screen_brightness); + + loadDefaultAnimationSettings(stmt); + loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION, + R.bool.def_accelerometer_rotation); + // Default date format based on build loadSetting(stmt, Settings.System.DATE_FORMAT, SystemProperties.get("ro.com.android.dateformat", @@ -558,12 +583,19 @@ class DatabaseHelper extends SQLiteOpenHelper { stmt.close(); } + private void loadDefaultAnimationSettings(SQLiteStatement stmt) { + loadFractionSetting(stmt, Settings.System.WINDOW_ANIMATION_SCALE, + R.fraction.def_window_animation_scale, 1); + loadFractionSetting(stmt, Settings.System.TRANSITION_ANIMATION_SCALE, + R.fraction.def_window_transition_scale, 1); + } + private void loadSecureSettings(SQLiteDatabase db) { SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)" + " VALUES(?,?);"); - // Bluetooth off - loadSetting(stmt, Settings.Secure.BLUETOOTH_ON, 0); + loadBooleanSetting(stmt, Settings.Secure.BLUETOOTH_ON, + R.bool.def_bluetooth_on); // Data roaming default, based on build loadSetting(stmt, Settings.Secure.DATA_ROAMING, @@ -571,22 +603,22 @@ class DatabaseHelper extends SQLiteOpenHelper { SystemProperties.get("ro.com.android.dataroaming", "false")) ? 1 : 0); - // Don't allow non-market apps to be installed - loadSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS, 0); + loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS, + R.bool.def_install_non_market_apps); - // Set the default location providers to network based (cell-id) - loadSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - LocationManager.NETWORK_PROVIDER); + loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + R.string.def_location_providers_allowed); - loadSetting(stmt, Settings.Secure.NETWORK_PREFERENCE, - ConnectivityManager.DEFAULT_NETWORK_PREFERENCE); - - // USB mass storage on by default - loadSetting(stmt, Settings.Secure.USB_MASS_STORAGE_ENABLED, 1); + loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE, + R.integer.def_network_preference); - // WIFI on, notify about available networks - loadSetting(stmt, Settings.Secure.WIFI_ON, 0); - loadSetting(stmt, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1); + loadBooleanSetting(stmt, Settings.Secure.USB_MASS_STORAGE_ENABLED, + R.bool.def_usb_mass_storage_enabled); + + loadBooleanSetting(stmt, Settings.Secure.WIFI_ON, + R.bool.def_wifi_on); + loadBooleanSetting(stmt, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + R.bool.def_networks_available_notification_on); // Don't do this. The SystemServer will initialize ADB_ENABLED from a // persistent system property instead. @@ -600,4 +632,23 @@ class DatabaseHelper extends SQLiteOpenHelper { stmt.bindString(2, value.toString()); stmt.execute(); } + + private void loadStringSetting(SQLiteStatement stmt, String key, int resid) { + loadSetting(stmt, key, mContext.getResources().getString(resid)); + } + + private void loadBooleanSetting(SQLiteStatement stmt, String key, int resid) { + loadSetting(stmt, key, + mContext.getResources().getBoolean(resid) ? "1" : "0"); + } + + private void loadIntegerSetting(SQLiteStatement stmt, String key, int resid) { + loadSetting(stmt, key, + Integer.toString(mContext.getResources().getInteger(resid))); + } + + private void loadFractionSetting(SQLiteStatement stmt, String key, int resid, int base) { + loadSetting(stmt, key, + Float.toString(mContext.getResources().getFraction(resid, base, base))); + } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 35bf6b011bfb9..333a450e0a80e 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -40,6 +40,9 @@ public class SettingsProvider extends ContentProvider { private static final String TAG = "SettingsProvider"; private static final boolean LOCAL_LOGV = false; + private static final String TABLE_FAVORITES = "favorites"; + private static final String TABLE_OLD_FAVORITES = "old_favorites"; + private DatabaseHelper mOpenHelper; /** @@ -47,7 +50,7 @@ public class SettingsProvider extends ContentProvider { * used to access the corresponding database rows. */ private static class SqlArguments { - public final String table; + public String table; public final String where; public final String[] args; @@ -175,17 +178,30 @@ public class SettingsProvider extends ContentProvider { @Override public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) { SqlArguments args = new SqlArguments(url, where, whereArgs); + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + // The favorites table was moved from this provider to a provider inside Home // Home still need to query this table to upgrade from pre-cupcake builds // However, a cupcake+ build with no data does not contain this table which will // cause an exception in the SQL stack. The following line is a special case to // let the caller of the query have a chance to recover and avoid the exception - if ("favorites".equals(args.table)) return null; + if (TABLE_FAVORITES.equals(args.table)) { + return null; + } else if (TABLE_OLD_FAVORITES.equals(args.table)) { + args.table = TABLE_FAVORITES; + Cursor cursor = db.rawQuery("PRAGMA table_info(favorites);", null); + if (cursor != null) { + boolean exists = cursor.getCount() > 0; + cursor.close(); + if (!exists) return null; + } else { + return null; + } + } SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(args.table); - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor ret = qb.query(db, select, args.where, args.args, null, null, sort); ret.setNotificationUri(getContext().getContentResolver(), url); return ret; @@ -206,6 +222,9 @@ public class SettingsProvider extends ContentProvider { @Override public int bulkInsert(Uri uri, ContentValues[] values) { SqlArguments args = new SqlArguments(uri); + if (TABLE_FAVORITES.equals(args.table)) { + return 0; + } checkWritePermissions(args); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -228,6 +247,9 @@ public class SettingsProvider extends ContentProvider { @Override public Uri insert(Uri url, ContentValues initialValues) { SqlArguments args = new SqlArguments(url); + if (TABLE_FAVORITES.equals(args.table)) { + return null; + } checkWritePermissions(args); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -243,6 +265,11 @@ public class SettingsProvider extends ContentProvider { @Override public int delete(Uri url, String where, String[] whereArgs) { SqlArguments args = new SqlArguments(url, where, whereArgs); + if (TABLE_FAVORITES.equals(args.table)) { + return 0; + } else if (TABLE_OLD_FAVORITES.equals(args.table)) { + args.table = TABLE_FAVORITES; + } checkWritePermissions(args); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -255,6 +282,9 @@ public class SettingsProvider extends ContentProvider { @Override public int update(Uri url, ContentValues initialValues, String where, String[] whereArgs) { SqlArguments args = new SqlArguments(url, where, whereArgs); + if (TABLE_FAVORITES.equals(args.table)) { + return 0; + } checkWritePermissions(args); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); diff --git a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsService.java b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsService.java index 4a1de596f4e92..8d6e2723cc9b5 100644 --- a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsService.java +++ b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsService.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; +import android.database.sqlite.SQLiteFullException; import android.net.Uri; import android.os.Bundle; import android.provider.SubscribedFeeds; @@ -189,11 +190,15 @@ public class SubscribedFeedsService extends BroadcastReceiver { // mark the feeds dirty, by setting the accounts to the same value, // which will trigger a sync. - ContentValues values = new ContentValues(); - for (String account : accounts) { - values.put(SyncConstValue._SYNC_ACCOUNT, account); - contentResolver.update(SubscribedFeeds.Feeds.CONTENT_URI, values, - SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?", new String[] {account}); + try { + ContentValues values = new ContentValues(); + for (String account : accounts) { + values.put(SyncConstValue._SYNC_ACCOUNT, account); + contentResolver.update(SubscribedFeeds.Feeds.CONTENT_URI, values, + SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?", new String[] {account}); + } + } catch (SQLiteFullException e) { + Log.w(TAG, "disk full while trying to mark the feeds as dirty, skipping"); } // Schedule a refresh. diff --git a/preloaded-classes b/preloaded-classes index 160ebef826d2b..1ace628942e87 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -160,7 +160,7 @@ android.graphics.drawable.DrawableContainer android.graphics.drawable.GradientDrawable android.graphics.drawable.LayerDrawable android.graphics.drawable.LayerDrawable$LayerState -android.graphics.drawable.LayerDrawable$Rec +android.graphics.drawable.LayerDrawable$ChildDrawable android.graphics.drawable.NinePatchDrawable android.graphics.drawable.NinePatchDrawable$NinePatchState android.graphics.drawable.PaintDrawable @@ -366,7 +366,6 @@ android.view.animation.Transformation android.view.inputmethod.BaseInputConnection android.view.inputmethod.CompletionInfo$1 android.view.inputmethod.CompletionInfo -android.view.inputmethod.DefaultInputMethod android.view.inputmethod.EditorInfo$1 android.view.inputmethod.EditorInfo android.view.inputmethod.ExtractedText$1 @@ -376,23 +375,16 @@ android.view.inputmethod.ExtractedTextRequest android.view.inputmethod.InputBinding$1 android.view.inputmethod.InputBinding android.view.inputmethod.InputConnection -android.view.inputmethod.InputConnectionWrapper android.view.inputmethod.InputMethod$SessionCallback android.view.inputmethod.InputMethod android.view.inputmethod.InputMethodInfo$1 android.view.inputmethod.InputMethodInfo android.view.inputmethod.InputMethodManager$1 android.view.inputmethod.InputMethodManager$2 -android.view.inputmethod.InputMethodManager$3 -android.view.inputmethod.InputMethodManager$NoOpInputConnection +android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper android.view.inputmethod.InputMethodManager android.view.inputmethod.InputMethodSession$EventCallback android.view.inputmethod.InputMethodSession -android.view.inputmethod.MutableInputConnectionWrapper -android.view.inputmethod.SimpleInputMethod$InputMethodSessionCallbackWrapper -android.view.inputmethod.SimpleInputMethod$Session$InputMethodEventCallbackWrapper -android.view.inputmethod.SimpleInputMethod$Session -android.view.inputmethod.SimpleInputMethod android.webkit.BrowserFrame android.webkit.CacheManager android.webkit.CallbackProxy diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 162585395d2e5..d8012b2c794a1 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -382,7 +382,7 @@ class AlarmManagerService extends IAlarmManager.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission("android.permission.DUMP") + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump AlarmManager from from pid=" + Binder.getCallingPid() diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index dba8fcac78b24..e4fdd0c1eb312 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager; import android.os.BatteryManager; import android.os.Binder; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UEventObserver; import android.util.EventLog; import android.util.Log; @@ -61,9 +62,13 @@ class BatteryService extends Binder { static final int LOG_BATTERY_LEVEL = 2722; static final int LOG_BATTERY_STATUS = 2723; + static final int LOG_BATTERY_DISCHARGE_STATUS = 2730; static final int BATTERY_SCALE = 100; // battery capacity is a percentage + // This should probably be exposed in the API, though it's not critical + private static final int BATTERY_PLUGGED_NONE = 0; + private final Context mContext; private final IBatteryStats mBatteryStats; @@ -85,7 +90,10 @@ class BatteryService extends Binder { private int mLastBatteryTemperature; private int mPlugType; - private int mLastPlugType; + private int mLastPlugType = -1; // Extra state so we can detect first run + + private long mDischargeStartTime; + private int mDischargeStartLevel; public BatteryService(Context context) { mContext = context; @@ -145,7 +153,7 @@ class BatteryService extends Binder { } else if (mUsbOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_USB; } else { - mPlugType = 0; + mPlugType = BATTERY_PLUGGED_NONE; } if (mBatteryStatus != mLastBatteryStatus || mBatteryHealth != mLastBatteryHealth || @@ -155,6 +163,25 @@ class BatteryService extends Binder { mBatteryVoltage != mLastBatteryVoltage || mBatteryTemperature != mLastBatteryTemperature) { + if (mPlugType != mLastPlugType) { + if (mLastPlugType == BATTERY_PLUGGED_NONE) { + // discharging -> charging + + // There's no value in this data unless we've discharged at least once and the + // battery level has changed; so don't log until it does. + if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryLevel) { + long duration = SystemClock.elapsedRealtime() - mDischargeStartTime; + EventLog.writeEvent(LOG_BATTERY_DISCHARGE_STATUS, duration, + mDischargeStartLevel, mBatteryLevel); + // make sure we see a discharge event before logging again + mDischargeStartTime = 0; + } + } else if (mPlugType == BATTERY_PLUGGED_NONE) { + // charging -> discharging or we just powered up + mDischargeStartTime = SystemClock.elapsedRealtime(); + mDischargeStartLevel = mBatteryLevel; + } + } if (mBatteryStatus != mLastBatteryStatus || mBatteryHealth != mLastBatteryHealth || mBatteryPresent != mLastBatteryPresent || @@ -187,7 +214,7 @@ class BatteryService extends Binder { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); try { - mBatteryStats.setOnBattery(mPlugType == 0); + mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE); } catch (RemoteException e) { // Should never happen. } @@ -233,7 +260,7 @@ class BatteryService extends Binder { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission("android.permission.DUMP") + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump Battery service from from pid=" diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 65e36509a9c51..760988d9f382f 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -35,6 +35,7 @@ import android.os.Message; import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.Settings; +import android.provider.Sync; import android.util.EventLog; import android.util.Log; @@ -333,6 +334,32 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + /** + * @see ConnectivityManager#getBackgroundDataSetting() + */ + public boolean getBackgroundDataSetting() { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.BACKGROUND_DATA, 1) == 1; + } + + /** + * @see ConnectivityManager#setBackgroundDataSetting(boolean) + */ + public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING, + "ConnectivityService"); + + if (getBackgroundDataSetting() == allowBackgroundDataUsage) return; + + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.BACKGROUND_DATA, allowBackgroundDataUsage ? 1 : 0); + + Intent broadcast = new Intent( + ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); + mContext.sendBroadcast(broadcast); + } + private int getNumConnectedNetworks() { int numConnectedNets = 0; @@ -632,7 +659,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump ConnectivityService from from pid=" + Binder.getCallingPid() diff --git a/services/java/com/android/server/FallbackCheckinService.java b/services/java/com/android/server/FallbackCheckinService.java new file mode 100644 index 0000000000000..cf224466e7650 --- /dev/null +++ b/services/java/com/android/server/FallbackCheckinService.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.ICheckinService; +import android.os.IParentalControlCallback; +import android.util.Log; + +import java.io.IOException; + +import com.android.internal.os.RecoverySystem; +import com.google.android.net.ParentalControlState; + +/** + * @hide + */ +public final class FallbackCheckinService extends ICheckinService.Stub { + static final String TAG = "FallbackCheckinService"; + final Context mContext; + + public FallbackCheckinService(Context context) { + mContext = context; + } + + public boolean checkin() { + return false; // failure, because not implemented + } + + public void reportCrashSync(byte[] crashData) { + } + + public void reportCrashAsync(byte[] crashData) { + } + + public void masterClear() { + if (mContext.checkCallingOrSelfPermission("android.permission.MASTER_CLEAR") != + PackageManager.PERMISSION_GRANTED) { + Log.e(TAG, "Permission Denial: can't invoke masterClear from " + + "pid=" + Binder.getCallingPid() + ", " + + "uid=" + Binder.getCallingUid()); + return; + } + + // Save the android ID so the new system can get it erased. + try { + RecoverySystem.rebootAndWipe(); + } catch (IOException e) { + Log.e(TAG, "Reboot for masterClear() failed", e); + } + } + + public void getParentalControlState(IParentalControlCallback p, String requestingApp) + throws android.os.RemoteException { + ParentalControlState state = new ParentalControlState(); + state.isEnabled = false; + p.onResult(state); + } +} diff --git a/services/java/com/android/server/GadgetService.java b/services/java/com/android/server/GadgetService.java index 4be9ed557e6e3..5ef0fb9f38a91 100644 --- a/services/java/com/android/server/GadgetService.java +++ b/services/java/com/android/server/GadgetService.java @@ -16,57 +16,127 @@ package com.android.server; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.content.pm.PackageItemInfo; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.gadget.GadgetManager; import android.gadget.GadgetInfo; +import android.net.Uri; import android.os.Binder; +import android.os.RemoteException; +import android.os.SystemClock; import android.util.AttributeSet; +import android.util.Config; import android.util.Log; import android.util.Xml; +import android.widget.RemoteViews; +import java.io.IOException; +import java.io.File; import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.HashMap; +import java.util.HashSet; import com.android.internal.gadget.IGadgetService; +import com.android.internal.gadget.IGadgetHost; +import com.android.internal.util.XmlUtils; +import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; class GadgetService extends IGadgetService.Stub { private static final String TAG = "GadgetService"; + private static final boolean LOGD = Config.LOGD || false; + + private static final String SETTINGS_FILENAME = "gadgets.xml"; + private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp"; + + /* + * When identifying a Host or Provider based on the calling process, use the uid field. + * When identifying a Host or Provider based on a package manager broadcast, use the + * package given. + */ + + static class Provider { + int uid; + GadgetInfo info; + ArrayList instances = new ArrayList(); + PendingIntent broadcast; + + int tag; // for use while saving state (the index) + } + + static class Host { + int uid; + int hostId; + String packageName; + ArrayList instances = new ArrayList(); + IGadgetHost callbacks; + + int tag; // for use while saving state (the index) + } static class GadgetId { int gadgetId; - String hostPackage; - GadgetInfo info; + Provider provider; + RemoteViews views; + Host host; } Context mContext; PackageManager mPackageManager; - ArrayList mInstalledProviders; + AlarmManager mAlarmManager; + ArrayList mInstalledProviders = new ArrayList(); int mNextGadgetId = 1; ArrayList mGadgetIds = new ArrayList(); + ArrayList mHosts = new ArrayList(); GadgetService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); - mInstalledProviders = getGadgetList(); + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + + getGadgetList(); + loadStateLocked(); + + // Register for the boot completed broadcast, so we can send the + // ENABLE broacasts. If we try to send them now, they time out, + // because the system isn't ready to handle them yet. + mContext.registerReceiver(mBroadcastReceiver, + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + + // Register for broadcasts about package install, etc., so we can + // update the provider list. + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + mContext.registerReceiver(mBroadcastReceiver, filter); } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump from from pid=" + Binder.getCallingPid() @@ -78,7 +148,7 @@ class GadgetService extends IGadgetService.Stub int N = mInstalledProviders.size(); pw.println("Providers: (size=" + N + ")"); for (int i=0; i getInstalledProviders() { synchronized (mGadgetIds) { - return new ArrayList(mInstalledProviders); + final int N = mInstalledProviders.size(); + ArrayList result = new ArrayList(N); + for (int i=0; i instances = p.instances; + final int N = instances.size(); + for (int i=0; i updatedViews) { + int callingUid = enforceCallingUid(packageName); + synchronized (mGadgetIds) { + Host host = lookupOrAddHostLocked(callingUid, packageName, hostId); + host.callbacks = callbacks; + + updatedViews.clear(); + + ArrayList instances = host.instances; + int N = instances.size(); + int[] updatedIds = new int[N]; + for (int i=0; i getGadgetList() { + Host lookupHostLocked(int uid, int hostId) { + final int N = mHosts.size(); + for (int i=0; i broadcastReceivers = pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); - ArrayList result = new ArrayList(); - final int N = broadcastReceivers.size(); for (int i=0; i 0) { + // if this is the first instance, set the alarm. otherwise, + // rely on the fact that we've already set it and that + // PendingIntent.getBroadcast will update the extras. + boolean alreadyRegistered = p.broadcast != null; + int instancesSize = p.instances.size(); + Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds); + intent.setComponent(p.info.provider); + long token = Binder.clearCallingIdentity(); + try { + p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent, + PendingIntent.FLAG_UPDATE_CURRENT); + } finally { + Binder.restoreCallingIdentity(token); + } + if (!alreadyRegistered) { + mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + p.info.updatePeriodMillis, + p.info.updatePeriodMillis, p.broadcast); + } + } + } + + static int[] getGadgetIds(Provider p) { + int instancesSize = p.instances.size(); + int gadgetIds[] = new int[instancesSize]; + for (int i=0; i 0) { + out.startTag(null, "p"); + out.attribute(null, "pkg", p.info.provider.getPackageName()); + out.attribute(null, "cl", p.info.provider.getClassName()); + out.endTag(null, "h"); + p.tag = providerIndex; + providerIndex++; + } + } + + N = mHosts.size(); + for (int i=0; i loadedProviders = new HashMap(); + do { + type = parser.next(); + if (type == XmlPullParser.START_TAG) { + String tag = parser.getName(); + if ("p".equals(tag)) { + // TODO: do we need to check that this package has the same signature + // as before? + String pkg = parser.getAttributeValue(null, "pkg"); + String cl = parser.getAttributeValue(null, "cl"); + Provider p = lookupProviderLocked(new ComponentName(pkg, cl)); + // if it wasn't uninstalled or something + if (p != null) { + loadedProviders.put(providerIndex, p); + } + providerIndex++; + } + else if ("h".equals(tag)) { + Host host = new Host(); + + // TODO: do we need to check that this package has the same signature + // as before? + host.packageName = parser.getAttributeValue(null, "pkg"); + try { + host.uid = getUidForPackage(host.packageName); + host.hostId = Integer.parseInt( + parser.getAttributeValue(null, "id"), 16); + mHosts.add(host); + } catch (PackageManager.NameNotFoundException ex) { + // Just ignore drop this entry, as if it has been uninstalled. + // We need to deal with this case because of safe mode, but there's + // a bug filed about it. + } + } + else if ("g".equals(tag)) { + GadgetId id = new GadgetId(); + id.gadgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16); + if (id.gadgetId >= mNextGadgetId) { + mNextGadgetId = id.gadgetId + 1; + } + + String providerString = parser.getAttributeValue(null, "p"); + if (providerString != null) { + // there's no provider if it hasn't been bound yet. + // maybe we don't have to save this, but it brings the system + // to the state it was in. + int pIndex = Integer.parseInt(providerString, 16); + id.provider = loadedProviders.get(pIndex); + if (false) { + Log.d(TAG, "bound gadgetId=" + id.gadgetId + " to provider " + + pIndex + " which is " + id.provider); + } + if (id.provider == null) { + // This provider is gone. We just let the host figure out + // that this happened when it fails to load it. + continue; + } + } + + int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16); + id.host = mHosts.get(hIndex); + if (id.host == null) { + // This host is gone. + continue; + } + + if (id.provider != null) { + id.provider.instances.add(id); + } + id.host.instances.add(id); + mGadgetIds.add(id); + } + } + } while (type != XmlPullParser.END_DOCUMENT); + success = true; + } catch (NullPointerException e) { + Log.w(TAG, "failed parsing " + file, e); + } catch (NumberFormatException e) { + Log.w(TAG, "failed parsing " + file, e); + } catch (XmlPullParserException e) { + Log.w(TAG, "failed parsing " + file, e); + } catch (IOException e) { + Log.w(TAG, "failed parsing " + file, e); + } catch (IndexOutOfBoundsException e) { + Log.w(TAG, "failed parsing " + file, e); + } + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + } + + if (success) { + // delete any hosts that didn't manage to get connected (should happen) + // if it matters, they'll be reconnected. + final int N = mHosts.size(); + for (int i=0; i broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, + PackageManager.GET_META_DATA); + + final int N = broadcastReceivers.size(); + for (int i=0; i keep = new HashSet(); + Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + List broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, + PackageManager.GET_META_DATA); + + // add the missing ones and collect which ones to keep + int N = broadcastReceivers.size(); + for (int i=0; i 0) { + long bedtime = SystemClock.uptimeMillis(); + do { + try { + this.wait(duration); + } + catch (InterruptedException e) { + } + if (mDone) { + break; + } + duration = duration + - SystemClock.uptimeMillis() - bedtime; + } while (duration > 0); + } + } + public void run() { synchronized (this) { int index = 0; - boolean nextState = false; long[] pattern = mPattern; - if (pattern[0] == 0) { - index++; - nextState = true; - } int len = pattern.length; - long start = SystemClock.uptimeMillis(); + long duration = 0; while (!mDone) { - if (nextState) { - HardwareService.this.on(); - } else { - HardwareService.this.off(); + // add off-time duration to any accumulated on-time duration + if (index < len) { + duration += pattern[index++]; } - nextState = !nextState; - long bedtime = SystemClock.uptimeMillis(); - long duration = pattern[index]; - do { - try { - this.wait(duration); + + // sleep until it is time to start the vibrator + delay(duration); + if (mDone) { + break; + } + + if (index < len) { + // read on-time duration and start the vibrator + // duration is saved for delay() at top of loop + duration = pattern[index++]; + if (duration > 0) { + HardwareService.this.vibratorOn(duration); } - catch (InterruptedException e) { - } - if (mDone) { - break; - } - duration = duration - - SystemClock.uptimeMillis() - bedtime; - } while (duration > 0); - index++; - if (index >= len) { + } else { if (mRepeat < 0) { - HardwareService.this.off(); break; } else { index = mRepeat; + duration = 0; } } } + if (mDone) { + // make sure vibrator is off if we were cancelled. + // otherwise, it will turn off automatically + // when the last timeout expires. + HardwareService.this.vibratorOff(); + } mWakeLock.release(); } synchronized (HardwareService.this) { @@ -301,6 +321,6 @@ public class HardwareService extends IHardwareService.Stub { volatile Death mDeath; volatile IBinder mToken; - native static void on(); - native static void off(); + native static void vibratorOn(long milliseconds); + native static void vibratorOff(); } diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java index 2bea7312fc536..855734dcb80e5 100644 --- a/services/java/com/android/server/HeadsetObserver.java +++ b/services/java/com/android/server/HeadsetObserver.java @@ -19,6 +19,8 @@ package com.android.server; import android.app.ActivityManagerNative; import android.content.Context; import android.content.Intent; +import android.os.Handler; +import android.os.Message; import android.os.UEventObserver; import android.util.Log; import android.media.AudioManager; @@ -40,6 +42,8 @@ class HeadsetObserver extends UEventObserver { private int mHeadsetState; private String mHeadsetName; + private boolean mAudioRouteNeedsUpdate; + private AudioManager mAudioManager; public HeadsetObserver(Context context) { mContext = context; @@ -60,7 +64,7 @@ class HeadsetObserver extends UEventObserver { } } - private final void init() { + private synchronized final void init() { char[] buffer = new char[1024]; String newName = mHeadsetName; @@ -80,21 +84,33 @@ class HeadsetObserver extends UEventObserver { Log.e(TAG, "" , e); } + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); update(newName, newState); } private synchronized final void update(String newName, int newState) { if (newName != mHeadsetName || newState != mHeadsetState) { + boolean isUnplug = (newState == 0 && mHeadsetState == 1); mHeadsetName = newName; mHeadsetState = newState; - AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mAudioRouteNeedsUpdate = true; - audioManager.setWiredHeadsetOn(mHeadsetState == 1); - sendIntent(); + sendIntent(isUnplug); + + if (isUnplug) { + // It often takes >200ms to flush the audio pipeline after apps + // pause audio playback, but audio route changes are immediate, + // so delay the route change by 400ms. + // This could be improved once the audio sub-system provides an + // interface to clear the audio pipeline. + mHandler.sendEmptyMessageDelayed(0, 400); + } else { + updateAudioRoute(); + } } } - private synchronized final void sendIntent() { + private synchronized final void sendIntent(boolean isUnplug) { // Pack up the values and broadcast them to everyone Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -104,5 +120,25 @@ class HeadsetObserver extends UEventObserver { // TODO: Should we require a permission? ActivityManagerNative.broadcastStickyIntent(intent, null); + + if (isUnplug) { + intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + mContext.sendBroadcast(intent); + } } + + private synchronized final void updateAudioRoute() { + if (mAudioRouteNeedsUpdate) { + mAudioManager.setWiredHeadsetOn(mHeadsetState == 1); + mAudioRouteNeedsUpdate = false; + } + } + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + updateAudioRoute(); + } + }; + } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 139aaa3aaa3f0..ee49365324fad 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -30,6 +30,7 @@ import com.android.server.status.StatusBarService; import org.xmlpull.v1.XmlPullParserException; +import android.app.ActivityManagerNative; import android.app.AlertDialog; import android.content.ComponentName; import android.content.ContentResolver; @@ -61,7 +62,6 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.view.IWindowManager; import android.view.WindowManager; -import android.view.inputmethod.DefaultInputMethod; import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodInfo; @@ -188,6 +188,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ ClientState mCurClient; + /** + * The input context last provided by the current client. + */ + IInputContext mCurInputContext; + /** * The attributes last provided by the current client. */ @@ -215,6 +220,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ boolean mShowExplicitlyRequested; + /** + * Set if we were forced to be shown. + */ + boolean mShowForced; + /** * Set if we last told the input method to show itself. */ @@ -281,7 +291,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; } else { - Log.e(TAG, "Unexpected intent " + intent); + Log.w(TAG, "Unexpected intent " + intent); } // Inform the current client of the change in active status @@ -290,7 +300,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurClient.client.setActive(mScreenOn); } } catch (RemoteException e) { - Log.e(TAG, "Got RemoteException sending 'screen on/off' notification", e); + Log.w(TAG, "Got RemoteException sending 'screen on/off' notification to pid " + + mCurClient.pid + " uid " + mCurClient.uid); } } } @@ -553,12 +564,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { mCurClient.client.setActive(false); } catch (RemoteException e) { - Log.e(TAG, "Got RemoteException sending setActive(false) notification", e); + Log.w(TAG, "Got RemoteException sending setActive(false) notification to pid " + + mCurClient.pid + " uid " + mCurClient.uid); } mCurClient = null; } } + private int getShowFlags() { + int flags = 0; + if (mShowForced) { + flags |= InputMethod.SHOW_FORCED + | InputMethod.SHOW_EXPLICIT; + } else if (mShowExplicitlyRequested) { + flags |= InputMethod.SHOW_EXPLICIT; + } + return flags; + } + InputBindResult attachNewInputLocked(boolean initial, boolean needResult) { if (!mBoundToMethod) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( @@ -567,16 +590,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } final SessionState session = mCurClient.curSession; if (initial) { - executeOrSendMessage(session.method, mCaller.obtainMessageOO( - MSG_START_INPUT, session, mCurAttribute)); + executeOrSendMessage(session.method, mCaller.obtainMessageOOO( + MSG_START_INPUT, session, mCurInputContext, mCurAttribute)); } else { - executeOrSendMessage(session.method, mCaller.obtainMessageOO( - MSG_RESTART_INPUT, session, mCurAttribute)); + executeOrSendMessage(session.method, mCaller.obtainMessageOOO( + MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute)); } if (mShowRequested) { if (DEBUG) Log.v(TAG, "Attach new input asks to show input"); - showCurrentInputLocked(mShowExplicitlyRequested - ? 0 : InputMethodManager.SHOW_IMPLICIT); + showCurrentInputLocked(getShowFlags()); } return needResult ? new InputBindResult(session.session, mCurId, mCurSeq) @@ -584,7 +606,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } InputBindResult startInputLocked(IInputMethodClient client, - EditorInfo attribute, boolean initial, boolean needResult) { + IInputContext inputContext, EditorInfo attribute, + boolean initial, boolean needResult) { // If no method is currently selected, do nothing. if (mCurMethodId == null) { return mNoBinding; @@ -622,7 +645,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { cs.client.setActive(mScreenOn); } catch (RemoteException e) { - Log.e(TAG, "Got RemoteException sending setActive notification", e); + Log.w(TAG, "Got RemoteException sending setActive notification to pid " + + cs.pid + " uid " + cs.uid); } } } @@ -631,6 +655,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurSeq++; if (mCurSeq <= 0) mCurSeq = 1; mCurClient = cs; + mCurInputContext = inputContext; mCurAttribute = attribute; // Check if the input method is changing. @@ -659,6 +684,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurToken != null) { try { + if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken); mIWindowManager.removeWindowToken(mCurToken); } catch (RemoteException e) { } @@ -679,6 +705,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurId = info.getId(); mCurToken = new Binder(); try { + if (DEBUG) Log.v(TAG, "Adding window token: " + mCurToken); mIWindowManager.addWindowToken(mCurToken, WindowManager.LayoutParams.TYPE_INPUT_METHOD); } catch (RemoteException e) { @@ -686,18 +713,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return new InputBindResult(null, mCurId, mCurSeq); } else { mCurIntent = null; - Log.e(TAG, "Failure connecting to input method service: " + Log.w(TAG, "Failure connecting to input method service: " + mCurIntent); } return null; } public InputBindResult startInput(IInputMethodClient client, - EditorInfo attribute, boolean initial, boolean needResult) { + IInputContext inputContext, EditorInfo attribute, + boolean initial, boolean needResult) { synchronized (mMethodMap) { final long ident = Binder.clearCallingIdentity(); try { - return startInputLocked(client, attribute, initial, needResult); + return startInputLocked(client, inputContext, attribute, + initial, needResult); } finally { Binder.restoreCallingIdentity(ident); } @@ -712,6 +741,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurIntent != null && name.equals(mCurIntent.getComponent())) { mCurMethod = IInputMethod.Stub.asInterface(service); if (mCurClient != null) { + if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken); executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_ATTACH_TOKEN, mCurMethod, mCurToken)); if (mCurClient != null) { @@ -817,10 +847,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurMethodId = id; Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD, id); - - Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); - intent.putExtra("input_method_id", id); - mContext.sendBroadcast(intent); + + if (ActivityManagerNative.isSystemReady()) { + Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); + intent.putExtra("input_method_id", id); + mContext.sendBroadcast(intent); + } unbindCurrentInputLocked(); } finally { Binder.restoreCallingIdentity(ident); @@ -853,10 +885,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) { mShowExplicitlyRequested = true; } + if ((flags&InputMethodManager.SHOW_FORCED) != 0) { + mShowExplicitlyRequested = true; + mShowForced = true; + } if (mCurMethod != null) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO( - MSG_SHOW_SOFT_INPUT, mShowExplicitlyRequested ? 1 : 0, - mCurMethod)); + MSG_SHOW_SOFT_INPUT, getShowFlags(), mCurMethod)); mInputShown = true; } } @@ -884,11 +919,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub void hideCurrentInputLocked(int flags) { if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 - && mShowExplicitlyRequested) { + && (mShowExplicitlyRequested || mShowForced)) { if (DEBUG) Log.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); return; } + if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) { + if (DEBUG) Log.v(TAG, + "Not hiding: forced show not cancelled by not-always hide"); + return; + } if (mInputShown && mCurMethod != null) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageO( MSG_HIDE_SOFT_INPUT, mCurMethod)); @@ -896,6 +936,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mInputShown = false; mShowRequested = false; mShowExplicitlyRequested = false; + mShowForced = false; } public void windowGainedFocus(IInputMethodClient client, @@ -933,7 +974,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // be behind any soft input window, so hide the // soft input window if it is shown. if (DEBUG) Log.v(TAG, "Unspecified window will hide input"); - hideCurrentInputLocked(0); + hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS); } } else if (isTextEditor && (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) @@ -1052,7 +1093,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return true; case MSG_SHOW_SOFT_INPUT: try { - ((IInputMethod)msg.obj).showSoftInput(msg.arg1 != 0); + ((IInputMethod)msg.obj).showSoftInput(msg.arg1); } catch (RemoteException e) { } return true; @@ -1065,6 +1106,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case MSG_ATTACH_TOKEN: args = (HandlerCaller.SomeArgs)msg.obj; try { + if (DEBUG) Log.v(TAG, "Sending attach of token: " + args.arg2); ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2); } catch (RemoteException e) { } @@ -1085,7 +1127,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { SessionState session = (SessionState)args.arg1; setEnabledSessionInMainThread(session); - session.method.startInput((EditorInfo)args.arg2); + session.method.startInput((IInputContext)args.arg2, + (EditorInfo)args.arg3); } catch (RemoteException e) { } return true; @@ -1094,7 +1137,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { SessionState session = (SessionState)args.arg1; setEnabledSessionInMainThread(session); - session.method.restartInput((EditorInfo)args.arg2); + session.method.restartInput((IInputContext)args.arg2, + (EditorInfo)args.arg3); } catch (RemoteException e) { } return true; @@ -1128,10 +1172,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub PackageManager pm = mContext.getPackageManager(); - Object[][] buildin = {{ - DefaultInputMethod.class.getName(), - DefaultInputMethod.getMetaInfo()}}; - List services = pm.queryIntentServices( new Intent(InputMethod.SERVICE_INTERFACE), PackageManager.GET_META_DATA); @@ -1150,39 +1190,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Log.d(TAG, "Checking " + compName); - /* Built-in input methods are not currently supported... this will - * need to be reworked to bring them back (all input methods must - * now be published in a manifest). - */ - /* - if (compName.getPackageName().equals( - InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) { - // System build-in input methods; - String inputMethodName = null; - int kbType = 0; - String skbName = null; - - for (int j = 0; j < buildin.length; ++j) { - Object[] obj = buildin[j]; - if (compName.getClassName().equals(obj[0])) { - InputMethodMetaInfo imp = (InputMethodMetaInfo) obj[1]; - inputMethodName = imp.inputMethodName; - } - } - - InputMethodMetaInfo p = new InputMethodMetaInfo(compName, - inputMethodName, ""); - - list.add(p); - - if (DEBUG) { - Log.d(TAG, "Found a build-in input method " + p); - } - - continue; - } - */ - try { InputMethodInfo p = new InputMethodInfo(mContext, ri); list.add(p); @@ -1386,7 +1393,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission("android.permission.DUMP") + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump InputMethodManager from from pid=" @@ -1395,8 +1402,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return; } + IInputMethod method; + ClientState client; + + final Printer p = new PrintWriterPrinter(pw); + synchronized (mMethodMap) { - final Printer p = new PrintWriterPrinter(pw); p.println("Current Input Method Manager state:"); int N = mMethodList.size(); p.println(" Input Methods:"); @@ -1416,17 +1427,40 @@ public class InputMethodManagerService extends IInputMethodManager.Stub p.println(" mInputMethodIcon=" + mInputMethodIcon); p.println(" mInputMethodData=" + mInputMethodData); p.println(" mCurrentMethod=" + mCurMethodId); - p.println(" mCurSeq=" + mCurSeq + " mCurClient=" + mCurClient); + client = mCurClient; + p.println(" mCurSeq=" + mCurSeq + " mCurClient=" + client); p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection + " mBoundToMethod=" + mBoundToMethod); p.println(" mCurToken=" + mCurToken); p.println(" mCurIntent=" + mCurIntent); + method = mCurMethod; p.println(" mCurMethod=" + mCurMethod); p.println(" mEnabledSession=" + mEnabledSession); p.println(" mShowRequested=" + mShowRequested + " mShowExplicitlyRequested=" + mShowExplicitlyRequested + + " mShowForced=" + mShowForced + " mInputShown=" + mInputShown); p.println(" mScreenOn=" + mScreenOn); } + + if (client != null) { + p.println(" "); + pw.flush(); + try { + client.client.asBinder().dump(fd, args); + } catch (RemoteException e) { + p.println("Input method client dead: " + e); + } + } + + if (method != null) { + p.println(" "); + pw.flush(); + try { + method.asBinder().dump(fd, args); + } catch (RemoteException e) { + p.println("Input method service dead: " + e); + } + } } } diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java index 9874042eaeae7..63b486ce77d78 100644 --- a/services/java/com/android/server/KeyInputQueue.java +++ b/services/java/com/android/server/KeyInputQueue.java @@ -165,6 +165,7 @@ public abstract class KeyInputQueue { public static native int getScancodeState(int deviceId, int sw); public static native int getKeycodeState(int sw); public static native int getKeycodeState(int deviceId, int sw); + public static native boolean hasKeys(int[] keycodes, boolean[] keyExists); public static KeyEvent newKeyEvent(InputDevice device, long downTime, long eventTime, boolean down, int keycode, int repeatCount, diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index ed9ee7927313c..bc6fd71850d7b 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -64,12 +64,14 @@ import android.telephony.TelephonyManager; import android.util.Config; import android.util.Log; +import com.android.internal.app.IBatteryStats; import com.android.internal.location.CellState; import com.android.internal.location.GpsLocationProvider; -import com.android.internal.location.LocationCollector; -import com.android.internal.location.LocationMasfClient; -import com.android.internal.location.NetworkLocationProvider; +import com.android.internal.location.ILocationCollector; +import com.android.internal.location.INetworkLocationManager; +import com.android.internal.location.INetworkLocationProvider; import com.android.internal.location.TrackProvider; +import com.android.server.am.BatteryStatsService; /** * The service class that manages LocationProviders and issues location @@ -77,7 +79,8 @@ import com.android.internal.location.TrackProvider; * * {@hide} */ -public class LocationManagerService extends ILocationManager.Stub { +public class LocationManagerService extends ILocationManager.Stub + implements INetworkLocationManager { private static final String TAG = "LocationManagerService"; // Minimum time interval between last known location writes, in milliseconds. @@ -121,13 +124,15 @@ public class LocationManagerService extends ILocationManager.Stub { private final Context mContext; private GpsLocationProvider mGpsLocationProvider; - private NetworkLocationProvider mNetworkLocationProvider; + private LocationProviderImpl mNetworkLocationProvider; + private INetworkLocationProvider mNetworkLocationInterface; private LocationWorkerHandler mLocationHandler; // Handler messages private static final int MESSAGE_HEARTBEAT = 1; private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2; private static final int MESSAGE_RELEASE_WAKE_LOCK = 3; + private static final int MESSAGE_SET_NETWORK_LOCATION_PROVIDER = 4; // Alarm manager and wakelock variables private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT"; @@ -143,6 +148,11 @@ public class LocationManagerService extends ILocationManager.Stub { private boolean mWakeLockNetworkReceived = true; private boolean mWifiWakeLockAcquired = false; private boolean mCellWakeLockAcquired = false; + + private final IBatteryStats mBatteryStats; + + // The calling UID when we are in a clearCallingIdentity/restoreCallingIdentity block, or -1 + private int mCallingUid = -1; /** * Mapping from listener IBinder/PendingIntent to local Listener wrappers. @@ -199,10 +209,7 @@ public class LocationManagerService extends ILocationManager.Stub { private int mSignalStrength = -1; // Location collector - private LocationCollector mCollector; - - // Location MASF service - private LocationMasfClient mMasfClient; + private ILocationCollector mCollector; // Wifi Manager private WifiManager mWifiManager; @@ -417,15 +424,9 @@ public class LocationManagerService extends ILocationManager.Stub { private void _loadProvidersNoSync() { // Attempt to load "real" providers first - if (NetworkLocationProvider.isSupported()) { - // Create a network location provider - mNetworkLocationProvider = new NetworkLocationProvider(mContext, mMasfClient); - LocationProviderImpl.addProvider(mNetworkLocationProvider); - } - if (GpsLocationProvider.isSupported()) { // Create a gps location provider - mGpsLocationProvider = new GpsLocationProvider(mContext, mCollector); + mGpsLocationProvider = new GpsLocationProvider(mContext); LocationProviderImpl.addProvider(mGpsLocationProvider); } @@ -509,18 +510,15 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "Constructed LocationManager Service"); } - // Initialize the LocationMasfClient - mMasfClient = new LocationMasfClient(mContext); - - // Create location collector - mCollector = new LocationCollector(mMasfClient); - // Alarm manager, needs to be done before calling loadProviders() below mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); // Create a wake lock, needs to be done before calling loadProviders() below PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); + + // Battery statistics service to be notified when GPS turns on or off + mBatteryStats = BatteryStatsService.getService(); // Load providers loadProviders(); @@ -560,13 +558,27 @@ public class LocationManagerService extends ILocationManager.Stub { if (mWifiManager != null) { List wifiScanResults = mWifiManager.getScanResults(); if (wifiScanResults != null && wifiScanResults.size() != 0) { - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateWifiScanResults(wifiScanResults); + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.updateWifiScanResults(wifiScanResults); } } } } + public void setNetworkLocationProvider(INetworkLocationProvider provider) { + mLocationHandler.removeMessages(MESSAGE_SET_NETWORK_LOCATION_PROVIDER); + Message m = Message.obtain(mLocationHandler, + MESSAGE_SET_NETWORK_LOCATION_PROVIDER, provider); + mLocationHandler.sendMessageAtFrontOfQueue(m); + } + + public void setLocationCollector(ILocationCollector collector) { + mCollector = collector; + if (mGpsLocationProvider != null) { + mGpsLocationProvider.setLocationCollector(mCollector); + } + } + private WifiManager.WifiLock getWifiWakelock() { if (mWifiLock == null && mWifiManager != null) { mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY); @@ -690,7 +702,8 @@ public class LocationManagerService extends ILocationManager.Stub { boolean shouldBeEnabled = isAllowedBySettings(name); // Collection is only allowed when network provider is being used - if (p.getName().equals(LocationManager.NETWORK_PROVIDER)) { + if (mCollector != null && + p.getName().equals(LocationManager.NETWORK_PROVIDER)) { mCollector.updateNetworkProviderStatus(shouldBeEnabled); } @@ -847,7 +860,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void _requestLocationUpdates(String provider, + private synchronized void _requestLocationUpdates(String provider, long minTime, float minDistance, Receiver receiver) { Object key = receiver.getKey(); if (Config.LOGD) { @@ -864,6 +877,7 @@ public class LocationManagerService extends ILocationManager.Stub { String[] packages = getPackageNames(); // so wakelock calls will succeed + mCallingUid = getCallingUid(); long identity = Binder.clearCallingIdentity(); try { UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, packages); @@ -889,12 +903,16 @@ public class LocationManagerService extends ILocationManager.Stub { oldRecord.dispose(); } - if (impl instanceof NetworkLocationProvider) { - ((NetworkLocationProvider) impl).addListener(packages); - } - boolean isProviderEnabled = isAllowedBySettings(provider); if (isProviderEnabled) { + if (provider.equals(LocationManager.GPS_PROVIDER)) { + try { + mBatteryStats.noteRequestGpsOn(mCallingUid); + } catch (RemoteException e) { + Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e); + } + } + long minTimeForProvider = getMinTime(provider); impl.setMinTime(minTimeForProvider); impl.enableLocationTracking(true); @@ -904,7 +922,6 @@ public class LocationManagerService extends ILocationManager.Stub { mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider); Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider); mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000); - } else { try { // Notify the listener that updates are currently disabled @@ -919,6 +936,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } finally { Binder.restoreCallingIdentity(identity); + mCallingUid = -1; } } @@ -942,13 +960,14 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void _removeUpdates(Receiver receiver) { + private synchronized void _removeUpdates(Receiver receiver) { Object key = receiver.getKey(); if (Config.LOGD) { Log.d(TAG, "_removeUpdates: listener = " + key); } // so wakelock calls will succeed + mCallingUid = getCallingUid(); long identity = Binder.clearCallingIdentity(); try { synchronized (mLocationListeners) { @@ -964,8 +983,8 @@ public class LocationManagerService extends ILocationManager.Stub { // Call dispose() on the obsolete update records. for (UpdateRecord record : oldRecords.values()) { if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) { - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.removeListener(record.mPackages); + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.removeListener(record.mPackages); } } record.dispose(); @@ -973,6 +992,14 @@ public class LocationManagerService extends ILocationManager.Stub { // Accumulate providers providers.addAll(oldRecords.keySet()); } + + if (providers.contains("gps")) { + try { + mBatteryStats.noteRequestGpsOff(mCallingUid); + } catch (RemoteException e) { + Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e); + } + } mLocationListeners.remove(key); mLastFixBroadcast.remove(key); @@ -1010,6 +1037,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } finally { Binder.restoreCallingIdentity(identity); + mCallingUid = -1; } } @@ -1426,7 +1454,7 @@ public class LocationManagerService extends ILocationManager.Stub { } writeLastKnownLocation(provider, loc); - if (p instanceof NetworkLocationProvider) { + if (p instanceof INetworkLocationProvider) { mWakeLockNetworkReceived = true; } else if (p instanceof GpsLocationProvider) { // Gps location received signal is in NetworkStateBroadcastReceiver @@ -1570,6 +1598,17 @@ public class LocationManagerService extends ILocationManager.Stub { // Update wakelock status so the next alarm is set before releasing wakelock updateWakelockStatus(mScreenOn); releaseWakeLock(); + } else if (msg.what == MESSAGE_SET_NETWORK_LOCATION_PROVIDER) { + synchronized (LocationManagerService.class) { + Log.d(TAG, "adding network location provider"); + mNetworkLocationInterface = + (INetworkLocationProvider)msg.obj; + mNetworkLocationInterface.addListener(getPackageNames()); + mNetworkLocationProvider = + (LocationProviderImpl)mNetworkLocationInterface; + LocationProviderImpl.addProvider(mNetworkLocationProvider); + updateProviders(); + } } } catch (Exception e) { // Log, don't crash! @@ -1590,7 +1629,9 @@ public class LocationManagerService extends ILocationManager.Stub { mLastCellState = new CellState(mTelephonyManager, cellLocation, asu); // Notify collector - mCollector.updateCellState(mLastCellState); + if (mCollector != null) { + mCollector.updateCellState(mLastCellState); + } // Updates providers List providers = LocationProviderImpl.getProviders(); @@ -1648,7 +1689,9 @@ public class LocationManagerService extends ILocationManager.Stub { boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0; // Notify collector battery state - mCollector.updateBatteryState(scale, level, plugged); + if (mCollector != null) { + mCollector.updateBatteryState(scale, level, plugged); + } } } } @@ -1666,9 +1709,11 @@ public class LocationManagerService extends ILocationManager.Stub { } // Notify provider and collector of Wifi scan results - mCollector.updateWifiScanResults(wifiScanResults); - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateWifiScanResults(wifiScanResults); + if (mCollector != null) { + mCollector.updateWifiScanResults(wifiScanResults); + } + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.updateWifiScanResults(wifiScanResults); } } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { @@ -1702,8 +1747,8 @@ public class LocationManagerService extends ILocationManager.Stub { } // Notify network provider of current wifi enabled state - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateWifiEnabledState(enabled); + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.updateWifiEnabledState(enabled); } } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) { @@ -1821,8 +1866,8 @@ public class LocationManagerService extends ILocationManager.Stub { } // Notify NetworkLocationProvider - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired); + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired); } // Acquire wifi lock @@ -1845,6 +1890,14 @@ public class LocationManagerService extends ILocationManager.Stub { && mGpsLocationProvider.isLocationTracking(); if (gpsActive) { mGpsLocationProvider.startNavigating(); + long identity = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteStartGps(mCallingUid == -1 ? getCallingUid() : mCallingUid); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling noteStartGps on BatteryStatsService", e); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1853,6 +1906,14 @@ public class LocationManagerService extends ILocationManager.Stub { && mGpsLocationProvider.isLocationTracking(); if (gpsActive) { mGpsLocationProvider.stopNavigating(); + long identity = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteStopGps(mCallingUid == -1 ? getCallingUid() : mCallingUid); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling noteStopGps on BatteryStatsService", e); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1877,7 +1938,6 @@ public class LocationManagerService extends ILocationManager.Stub { } if (!mScreenOn) { - // Stop the gps stopGps(); } @@ -1889,8 +1949,8 @@ public class LocationManagerService extends ILocationManager.Stub { } // Notify NetworkLocationProvider - if (mNetworkLocationProvider != null) { - mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired); + if (mNetworkLocationInterface != null) { + mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired); } // Release wake lock @@ -1907,14 +1967,10 @@ public class LocationManagerService extends ILocationManager.Stub { public String getFromLocation(double latitude, double longitude, int maxResults, String language, String country, String variant, String appName, List

    addrs) { - try { - Locale locale = new Locale(language, country, variant); - mMasfClient.reverseGeocode(locale, appName, latitude, longitude, maxResults, addrs); - return null; - } catch(IOException e) { - return e.getMessage(); - } catch(Exception e) { - Log.e(TAG, "getFromLocation got exception:", e); + if (mNetworkLocationInterface != null) { + return mNetworkLocationInterface.getFromLocation(latitude, longitude, maxResults, + language, country, variant, appName, addrs); + } else { return null; } } @@ -1923,17 +1979,11 @@ public class LocationManagerService extends ILocationManager.Stub { double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, String language, String country, String variant, String appName, List
    addrs) { - - try { - Locale locale = new Locale(language, country, variant); - mMasfClient.forwardGeocode(locale, appName, locationName, - lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, addrs); - return null; - } catch(IOException e) { - return e.getMessage(); - } catch(Exception e) { - Log.e(TAG, "getFromLocationName got exception:", e); + if (mNetworkLocationInterface != null) { + return mNetworkLocationInterface.getFromLocationName(locationName, lowerLeftLatitude, + lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults, + language, country, variant, appName, addrs); + } else { return null; } } diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java index 40d1b724aae7b..2e430c849d973 100644 --- a/services/java/com/android/server/MountListener.java +++ b/services/java/com/android/server/MountListener.java @@ -30,7 +30,7 @@ import java.io.OutputStream; import java.net.Socket; /** - * Thread for communicating with the mount service daemon via a local socket. + * Thread for communicating with the vol service daemon via a local socket. * Events received from the daemon are passed to the MountService instance, * and the MountService instance calls MountListener to send commands to the daemon. */ @@ -38,39 +38,43 @@ final class MountListener implements Runnable { private static final String TAG = "MountListener"; - // ** THE FOLLOWING STRING CONSTANTS MUST MATCH VALUES IN system/mountd/mountd.h + // ** THE FOLLOWING STRING CONSTANTS MUST MATCH VALUES IN system/vold/ - // socket name for connecting to mountd - private static final String MOUNTD_SOCKET = "mountd"; + // socket name for connecting to vold + private static final String VOLD_SOCKET = "vold"; - // mountd commands - private static final String MOUNTD_ENABLE_UMS = "enable_ums"; - private static final String MOUNTD_DISABLE_UMS = "disable_ums"; - private static final String MOUNTD_SEND_STATUS = "send_status"; - private static final String MOUNTD_MOUNT_MEDIA = "mount_media:"; - private static final String MOUNTD_EJECT_MEDIA = "eject_media:"; + // vold commands + private static final String VOLD_CMD_ENABLE_UMS = "enable_ums"; + private static final String VOLD_CMD_DISABLE_UMS = "disable_ums"; + private static final String VOLD_CMD_SEND_UMS_STATUS = "send_ums_status"; + private static final String VOLD_CMD_MOUNT_VOLUME = "mount_volume:"; + private static final String VOLD_CMD_EJECT_MEDIA = "eject_media:"; + private static final String VOLD_CMD_FORMAT_MEDIA = "format_media:"; - // mountd events - private static final String MOUNTD_UMS_ENABLED = "ums_enabled"; - private static final String MOUNTD_UMS_DISABLED = "ums_disabled"; - private static final String MOUNTD_UMS_CONNECTED = "ums_connected"; - private static final String MOUNTD_UMS_DISCONNECTED = "ums_disconnected"; - private static final String MOUNTD_MEDIA_REMOVED = "media_removed:"; - private static final String MOUNTD_MEDIA_UNMOUNTED = "media_unmounted:"; - private static final String MOUNTD_MEDIA_MOUNTED = "media_mounted:"; - private static final String MOUNTD_MEDIA_MOUNTED_READ_ONLY = "media_mounted_ro:"; - private static final String MOUNTD_MEDIA_SHARED = "media_shared:"; - private static final String MOUNTD_MEDIA_BAD_REMOVAL = "media_bad_removal:"; - private static final String MOUNTD_MEDIA_UNMOUNTABLE = "media_unmountable:"; - private static final String MOUNTD_REQUEST_EJECT = "request_eject:"; + // vold events + private static final String VOLD_EVT_UMS_ENABLED = "ums_enabled"; + private static final String VOLD_EVT_UMS_DISABLED = "ums_disabled"; + private static final String VOLD_EVT_UMS_CONNECTED = "ums_connected"; + private static final String VOLD_EVT_UMS_DISCONNECTED = "ums_disconnected"; + + private static final String VOLD_EVT_NOMEDIA = "volume_nomedia:"; + private static final String VOLD_EVT_UNMOUNTED = "volume_unmounted:"; + private static final String VOLD_EVT_MOUNTED = "volume_mounted:"; + private static final String VOLD_EVT_MOUNTED_RO = "volume_mounted_ro:"; + private static final String VOLD_EVT_UMS = "volume_ums"; + private static final String VOLD_EVT_BAD_REMOVAL = "volume_badremoval:"; + private static final String VOLD_EVT_DAMAGED = "volume_damaged:"; + private static final String VOLD_EVT_CHECKING = "volume_checking:"; + private static final String VOLD_EVT_NOFS = "volume_nofs:"; + private static final String VOLD_EVT_EJECTING = "volume_ejecting:"; /** - * MountService that handles events received from the mount service daemon + * MountService that handles events received from the vol service daemon */ private MountService mService; /** - * Stream for sending commands to the mount service daemon. + * Stream for sending commands to the vol service daemon. */ private OutputStream mOutputStream; @@ -95,9 +99,9 @@ final class MountListener implements Runnable { } /** - * Process and dispatches events received from the mount service daemon + * Process and dispatches events received from the vol service daemon * - * @param event An event received from the mount service daemon + * @param event An event received from the vol service daemon */ private void handleEvent(String event) { if (Config.LOGD) Log.d(TAG, "handleEvent " + event); @@ -105,34 +109,38 @@ final class MountListener implements Runnable { int colonIndex = event.indexOf(':'); String path = (colonIndex > 0 ? event.substring(colonIndex + 1) : null); - if (event.equals(MOUNTD_UMS_ENABLED)) { + if (event.equals(VOLD_EVT_UMS_ENABLED)) { mUmsEnabled = true; - } else if (event.equals(MOUNTD_UMS_DISABLED)) { + } else if (event.equals(VOLD_EVT_UMS_DISABLED)) { mUmsEnabled = false; - } else if (event.equals(MOUNTD_UMS_CONNECTED)) { + } else if (event.equals(VOLD_EVT_UMS_CONNECTED)) { mUmsConnected = true; mService.notifyUmsConnected(); - } else if (event.equals(MOUNTD_UMS_DISCONNECTED)) { + } else if (event.equals(VOLD_EVT_UMS_DISCONNECTED)) { mUmsConnected = false; mService.notifyUmsDisconnected(); - } else if (event.startsWith(MOUNTD_MEDIA_REMOVED)) { + } else if (event.startsWith(VOLD_EVT_NOMEDIA)) { mService.notifyMediaRemoved(path); - } else if (event.startsWith(MOUNTD_MEDIA_UNMOUNTED)) { + } else if (event.startsWith(VOLD_EVT_UNMOUNTED)) { mService.notifyMediaUnmounted(path); - } else if (event.startsWith(MOUNTD_MEDIA_MOUNTED)) { + } else if (event.startsWith(VOLD_EVT_CHECKING)) { + mService.notifyMediaChecking(path); + } else if (event.startsWith(VOLD_EVT_NOFS)) { + mService.notifyMediaNoFs(path); + } else if (event.startsWith(VOLD_EVT_MOUNTED)) { mService.notifyMediaMounted(path, false); - } else if (event.startsWith(MOUNTD_MEDIA_MOUNTED_READ_ONLY)) { + } else if (event.startsWith(VOLD_EVT_MOUNTED_RO)) { mService.notifyMediaMounted(path, true); - } else if (event.startsWith(MOUNTD_MEDIA_SHARED)) { + } else if (event.startsWith(VOLD_EVT_UMS)) { mService.notifyMediaShared(path); - } else if (event.startsWith(MOUNTD_MEDIA_BAD_REMOVAL)) { + } else if (event.startsWith(VOLD_EVT_BAD_REMOVAL)) { mService.notifyMediaBadRemoval(path); // also send media eject intent, to notify apps to close any open // files on the media. mService.notifyMediaEject(path); - } else if (event.startsWith(MOUNTD_MEDIA_UNMOUNTABLE)) { + } else if (event.startsWith(VOLD_EVT_DAMAGED)) { mService.notifyMediaUnmountable(path); - } else if (event.startsWith(MOUNTD_REQUEST_EJECT)) { + } else if (event.startsWith(VOLD_EVT_EJECTING)) { mService.notifyMediaEject(path); } } @@ -156,7 +164,7 @@ final class MountListener implements Runnable { private void writeCommand2(String command, String argument) { synchronized (this) { if (mOutputStream == null) { - Log.e(TAG, "No connection to mountd", new IllegalStateException()); + Log.e(TAG, "No connection to vold", new IllegalStateException()); } else { StringBuilder builder = new StringBuilder(command); if (argument != null) { @@ -183,7 +191,7 @@ final class MountListener implements Runnable { try { socket = new LocalSocket(); - LocalSocketAddress address = new LocalSocketAddress(MOUNTD_SOCKET, + LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET, LocalSocketAddress.Namespace.RESERVED); socket.connect(address); @@ -193,7 +201,7 @@ final class MountListener implements Runnable { byte[] buffer = new byte[100]; - writeCommand(MOUNTD_SEND_STATUS); + writeCommand(VOLD_CMD_SEND_UMS_STATUS); while (true) { int count = inputStream.read(buffer); @@ -242,7 +250,7 @@ final class MountListener implements Runnable { * create tons of throwaway LocalSockets, making * system_server GC constantly. */ - Log.e(TAG, "Failed to connect to mountd", new IllegalStateException()); + Log.e(TAG, "Failed to connect to vold", new IllegalStateException()); SystemClock.sleep(2000); } @@ -283,7 +291,7 @@ final class MountListener implements Runnable { * @param enable true to enable USB mass storage support */ void setMassStorageEnabled(boolean enable) { - writeCommand(enable ? MOUNTD_ENABLE_UMS : MOUNTD_DISABLE_UMS); + writeCommand(enable ? VOLD_CMD_ENABLE_UMS : VOLD_CMD_DISABLE_UMS); } /** @@ -297,14 +305,20 @@ final class MountListener implements Runnable { * Mount media at given mount point. */ public void mountMedia(String mountPoint) { - writeCommand2(MOUNTD_MOUNT_MEDIA, mountPoint); + writeCommand2(VOLD_CMD_MOUNT_VOLUME, mountPoint); } /** * Unmount media at given mount point. */ public void ejectMedia(String mountPoint) { - writeCommand2(MOUNTD_EJECT_MEDIA, mountPoint); + writeCommand2(VOLD_CMD_EJECT_MEDIA, mountPoint); + } + + /** + * Format media at given mount point. + */ + public void formatMedia(String mountPoint) { + writeCommand2(VOLD_CMD_FORMAT_MEDIA, mountPoint); } } - diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 002ebeda208a4..0feb1da760841 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -19,15 +19,19 @@ package com.android.server; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; import android.os.IMountService; import android.os.Environment; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UEventObserver; +import android.text.TextUtils; import android.util.Log; import java.io.File; @@ -52,24 +56,32 @@ class MountService extends IMountService.Stub { private MountListener mListener; /** - * The notification that is shown when USB is connected. It leads the user - * to a dialog to enable mass storage mode. + * The notification that is shown when a USB mass storage host + * is connected. *

    - * This is lazily created, so use {@link #getUsbStorageNotification()}. + * This is lazily created, so use {@link #setUsbStorageNotification()}. */ private Notification mUsbStorageNotification; - private class SdDoorListener extends UEventObserver { - static final String SD_DOOR_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/sd-door"; - static final String SD_DOOR_SWITCH_NAME = "sd-door"; - public void onUEvent(UEvent event) { - if (SD_DOOR_SWITCH_NAME.equals(event.get("SWITCH_NAME"))) { - sdDoorStateChanged(event.get("SWITCH_STATE")); - } - } - }; + /** + * The notification that is shown when the following media events occur: + * - Media is being checked + * - Media is blank (or unknown filesystem) + * - Media is corrupt + * - Media is safe to unmount + * - Media is missing + *

    + * This is lazily created, so use {@link #setMediaStorageNotification()}. + */ + private Notification mMediaStorageNotification; + private boolean mShowSafeUnmountNotificationWhenUnmounted; + + private boolean mPlaySounds; + + private boolean mMounted; + /** * Constructs a new MountService instance * @@ -77,13 +89,28 @@ class MountService extends IMountService.Stub { */ public MountService(Context context) { mContext = context; + + // Register a BOOT_COMPLETED handler so that we can start + // MountListener. We defer the startup so that we don't + // start processing events before we ought-to + mContext.registerReceiver(mBroadcastReceiver, + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + mListener = new MountListener(this); - Thread thread = new Thread(mListener, MountListener.class.getName()); - thread.start(); - SdDoorListener sdDoorListener = new SdDoorListener(); - sdDoorListener.startObserving(SdDoorListener.SD_DOOR_UEVENT_MATCH); + mShowSafeUnmountNotificationWhenUnmounted = false; + + mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1"); } + BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { + Thread thread = new Thread(mListener, MountListener.class.getName()); + thread.start(); + } + } + }; + /** * @return true if USB mass storage support is enabled. */ @@ -129,19 +156,92 @@ class MountService extends IMountService.Stub { throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); } + // Set a flag so that when we get the unmounted event, we know + // to display the notification + mShowSafeUnmountNotificationWhenUnmounted = true; + // tell mountd to unmount the media mListener.ejectMedia(mountPath); } + /** + * Attempt to format external media + */ + public void formatMedia(String formatPath) throws RemoteException { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission"); + } + + mListener.formatMedia(formatPath); + } + + /** + * Returns true if we're playing media notification sounds. + */ + public boolean getPlayNotificationSounds() { + return mPlaySounds; + } + + /** + * Set whether or not we're playing media notification sounds. + */ + public void setPlayNotificationSounds(boolean enabled) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires WRITE_SETTINGS permission"); + } + mPlaySounds = enabled; + SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0")); + } + + /** + * Update the state of the USB mass storage notification + */ + void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) { + + try { + + if (getMassStorageConnected() && !suppressIfConnected) { + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + setUsbStorageNotification( + com.android.internal.R.string.usb_storage_notification_title, + com.android.internal.R.string.usb_storage_notification_message, + com.android.internal.R.drawable.stat_sys_data_usb, + sound, true, pi); + } else { + setUsbStorageNotification(0, 0, 0, false, false, null); + } + } catch (RemoteException e) { + // Nothing to do + } + } + + void handlePossibleExplicitUnmountBroadcast(String path) { + if (mMounted) { + mMounted = false; + Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, + Uri.parse("file://" + path)); + mContext.sendBroadcast(intent); + } + } + /** * Broadcasts the USB mass storage connected event to all clients. */ void notifyUmsConnected() { String storageState = Environment.getExternalStorageState(); if (!storageState.equals(Environment.MEDIA_REMOVED) && - !storageState.equals(Environment.MEDIA_BAD_REMOVAL)) { - setUsbStorageNotificationVisibility(true); + !storageState.equals(Environment.MEDIA_BAD_REMOVAL) && + !storageState.equals(Environment.MEDIA_CHECKING)) { + + updateUsbMassStorageNotification(false, true); } + Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED); mContext.sendBroadcast(intent); } @@ -150,7 +250,7 @@ class MountService extends IMountService.Stub { * Broadcasts the USB mass storage disconnected event to all clients. */ void notifyUmsDisconnected() { - setUsbStorageNotificationVisibility(false); + updateUsbMassStorageNotification(false, false); Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); mContext.sendBroadcast(intent); } @@ -159,6 +259,15 @@ class MountService extends IMountService.Stub { * Broadcasts the media removed event to all clients. */ void notifyMediaRemoved(String path) { + updateUsbMassStorageNotification(true, false); + + setMediaStorageNotification( + com.android.internal.R.string.ext_media_nomedia_notification_title, + com.android.internal.R.string.ext_media_nomedia_notification_message, + com.android.internal.R.drawable.stat_sys_no_sim, + true, false, null); + handlePossibleExplicitUnmountBroadcast(path); + Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); @@ -168,18 +277,68 @@ class MountService extends IMountService.Stub { * Broadcasts the media unmounted event to all clients. */ void notifyMediaUnmounted(String path) { + if (mShowSafeUnmountNotificationWhenUnmounted) { + setMediaStorageNotification( + com.android.internal.R.string.ext_media_safe_unmount_notification_title, + com.android.internal.R.string.ext_media_safe_unmount_notification_message, + com.android.internal.R.drawable.stat_notify_sim_toolkit, + true, true, null); + mShowSafeUnmountNotificationWhenUnmounted = false; + } else { + setMediaStorageNotification(0, 0, 0, false, false, null); + } + updateUsbMassStorageNotification(false, false); + Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); } + /** + * Broadcasts the media checking event to all clients. + */ + void notifyMediaChecking(String path) { + setMediaStorageNotification( + com.android.internal.R.string.ext_media_checking_notification_title, + com.android.internal.R.string.ext_media_checking_notification_message, + com.android.internal.R.drawable.stat_notify_sim_toolkit, + true, false, null); + + updateUsbMassStorageNotification(true, false); + Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, + Uri.parse("file://" + path)); + mContext.sendBroadcast(intent); + } + + /** + * Broadcasts the media nofs event to all clients. + */ + void notifyMediaNoFs(String path) { + + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title, + com.android.internal.R.string.ext_media_nofs_notification_message, + com.android.internal.R.drawable.stat_sys_no_sim, + true, false, pi); + updateUsbMassStorageNotification(false, false); + intent = new Intent(Intent.ACTION_MEDIA_NOFS, + Uri.parse("file://" + path)); + mContext.sendBroadcast(intent); + } + /** * Broadcasts the media mounted event to all clients. */ void notifyMediaMounted(String path, boolean readOnly) { + setMediaStorageNotification(0, 0, 0, false, false, null); + updateUsbMassStorageNotification(false, false); Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path)); intent.putExtra("read-only", readOnly); + mMounted = true; mContext.sendBroadcast(intent); } @@ -187,7 +346,15 @@ class MountService extends IMountService.Stub { * Broadcasts the media shared event to all clients. */ void notifyMediaShared(String path) { - Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED, + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title, + com.android.internal.R.string.usb_storage_stop_notification_message, + com.android.internal.R.drawable.stat_sys_warning, + false, true, pi); + handlePossibleExplicitUnmountBroadcast(path); + intent = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); } @@ -196,16 +363,39 @@ class MountService extends IMountService.Stub { * Broadcasts the media bad removal event to all clients. */ void notifyMediaBadRemoval(String path) { + updateUsbMassStorageNotification(true, false); + setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title, + com.android.internal.R.string.ext_media_badremoval_notification_message, + com.android.internal.R.drawable.stat_sys_warning, + true, true, null); + + handlePossibleExplicitUnmountBroadcast(path); Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); + + intent = new Intent(Intent.ACTION_MEDIA_REMOVED, + Uri.parse("file://" + path)); + mContext.sendBroadcast(intent); } /** * Broadcasts the media unmountable event to all clients. */ void notifyMediaUnmountable(String path) { - Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, + Intent intent = new Intent(); + intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title, + com.android.internal.R.string.ext_media_unmountable_notification_message, + com.android.internal.R.drawable.stat_sys_no_sim, + true, false, pi); + updateUsbMassStorageNotification(false, false); + + handlePossibleExplicitUnmountBroadcast(path); + + intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path)); mContext.sendBroadcast(intent); } @@ -219,90 +409,129 @@ class MountService extends IMountService.Stub { mContext.sendBroadcast(intent); } - private void sdDoorStateChanged(String doorState) { - File directory = Environment.getExternalStorageDirectory(); - String storageState = Environment.getExternalStorageState(); - - if (directory != null) { - try { - if (doorState.equals("open") && (storageState.equals(Environment.MEDIA_MOUNTED) || - storageState.equals(Environment.MEDIA_MOUNTED_READ_ONLY))) { - // request SD card unmount if SD card door is opened - unmountMedia(directory.getPath()); - } else if (doorState.equals("closed") && storageState.equals(Environment.MEDIA_UNMOUNTED)) { - // attempt to remount SD card - mountMedia(directory.getPath()); - } - } catch (RemoteException e) { - // Nothing to do. - } - } - } - /** - * Sets the visibility of the USB storage notification. This should be - * called when a USB cable is connected and also when it is disconnected. - * - * @param visible Whether to show or hide the notification. + * Sets the USB storage notification. */ - private void setUsbStorageNotificationVisibility(boolean visible) { - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager == null) { + private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible, + PendingIntent pi) { + + if (!visible && mUsbStorageNotification == null) { return; } - /* - * The convention for notification IDs is to use the icon's resource ID - * when the icon is only used by a single notification type, which is - * the case here. - */ - Notification notification = getUsbStorageNotification(); - final int notificationId = notification.icon; + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + + if (notificationManager == null) { + return; + } if (visible) { - notificationManager.notify(notificationId, notification); + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleId); + CharSequence message = r.getText(messageId); + + if (mUsbStorageNotification == null) { + mUsbStorageNotification = new Notification(); + mUsbStorageNotification.icon = icon; + mUsbStorageNotification.when = 0; + } + + if (sound && mPlaySounds) { + mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; + } else { + mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; + } + + mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; + + mUsbStorageNotification.tickerText = title; + if (pi == null) { + Intent intent = new Intent(); + pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } + + mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); + } + + final int notificationId = mUsbStorageNotification.icon; + if (visible) { + notificationManager.notify(notificationId, mUsbStorageNotification); } else { notificationManager.cancel(notificationId); } } - /** - * Gets the USB storage notification. - * - * @return A {@link Notification} that leads to the dialog to enable USB storage. - */ - private synchronized Notification getUsbStorageNotification() { - Resources r = Resources.getSystem(); - CharSequence title = - r.getText(com.android.internal.R.string.usb_storage_notification_title); - CharSequence message = - r.getText(com.android.internal.R.string.usb_storage_notification_message); + private synchronized boolean getMediaStorageNotificationDismissable() { + if ((mMediaStorageNotification != null) && + ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == + Notification.FLAG_AUTO_CANCEL)) + return true; - if (mUsbStorageNotification == null) { - mUsbStorageNotification = new Notification(); - mUsbStorageNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb; - mUsbStorageNotification.when = 0; - mUsbStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; - mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; + return false; + } + + /** + * Sets the media storage notification. + */ + private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, + boolean dismissable, PendingIntent pi) { + + if (!visible && mMediaStorageNotification == null) { + return; } - mUsbStorageNotification.tickerText = title; - mUsbStorageNotification.setLatestEventInfo(mContext, title, message, - getUsbStorageDialogIntent()); + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); - return mUsbStorageNotification; - } + if (notificationManager == null) { + return; + } + + if (mMediaStorageNotification != null && visible) { + /* + * Dismiss the previous notification - we're about to + * re-use it. + */ + final int notificationId = mMediaStorageNotification.icon; + notificationManager.cancel(notificationId); + } + + if (visible) { + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleId); + CharSequence message = r.getText(messageId); + + if (mMediaStorageNotification == null) { + mMediaStorageNotification = new Notification(); + mMediaStorageNotification.when = 0; + if (mPlaySounds) { + mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND; + } + } + + if (dismissable) { + mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; + } else { + mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; + } + + mMediaStorageNotification.tickerText = title; + if (pi == null) { + Intent intent = new Intent(); + pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } + + mMediaStorageNotification.icon = icon; + mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); + } - /** - * Creates a pending intent to start the USB storage activity. - * - * @return A {@link PendingIntent} that start the USB storage activity. - */ - private PendingIntent getUsbStorageDialogIntent() { - Intent intent = new Intent(); - intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class); - return PendingIntent.getActivity(mContext, 0, intent, 0); + final int notificationId = mMediaStorageNotification.icon; + if (visible) { + notificationManager.notify(notificationId, mMediaStorageNotification); + } else { + notificationManager.cancel(notificationId); + } } } diff --git a/services/java/com/android/server/NetStatService.java b/services/java/com/android/server/NetStatService.java index 11dbe6326351f..1ea0bac437659 100644 --- a/services/java/com/android/server/NetStatService.java +++ b/services/java/com/android/server/NetStatService.java @@ -26,20 +26,35 @@ public class NetStatService extends INetStatService.Stub { } - public int getTxPackets() { - return NetStat.netStatGetTxPkts(); + public long getMobileTxPackets() { + return NetStat.getMobileTxPkts(); } - public int getRxPackets() { - return NetStat.netStatGetRxPkts(); + public long getMobileRxPackets() { + return NetStat.getMobileRxPkts(); } - public int getTxBytes() { - return NetStat.netStatGetTxBytes(); + public long getMobileTxBytes() { + return NetStat.getMobileTxBytes(); } - public int getRxBytes() { - return NetStat.netStatGetRxBytes(); + public long getMobileRxBytes() { + return NetStat.getMobileRxBytes(); } + public long getTotalTxPackets() { + return NetStat.getTotalTxPkts(); + } + + public long getTotalRxPackets() { + return NetStat.getTotalRxPkts(); + } + + public long getTotalTxBytes() { + return NetStat.getTotalTxBytes(); + } + + public long getTotalRxBytes() { + return NetStat.getTotalRxBytes(); + } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index eb9ebe95aae19..e5de7f9fb5f61 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -865,7 +865,7 @@ class NotificationManagerService extends INotificationManager.Stub // ====================================================================== @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission("android.permission.DUMP") + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump NotificationManager from from pid=" + Binder.getCallingPid() diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 58ad42620ff0d..221ba46de6e4e 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -97,6 +97,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; @@ -983,6 +984,20 @@ class PackageManagerService extends IPackageManager.Stub { } return null; } + + public String[] getSystemSharedLibraryNames() { + Set libSet; + synchronized (mPackages) { + libSet = mSharedLibraries.keySet(); + } + int size = libSet.size(); + if (size > 0) { + String[] libs = new String[size]; + libSet.toArray(libs); + return libs; + } + return null; + } public int checkPermission(String permName, String pkgName) { synchronized (mPackages) { @@ -2678,7 +2693,7 @@ class PackageManagerService extends IPackageManager.Stub { grantPermissionsLP(pkg, false); } } - + private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) { final PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps == null) { @@ -2724,7 +2739,19 @@ class PackageManagerService extends IPackageManager.Stub { == PackageManager.SIGNATURE_MATCH); if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { - allowed = true; + // For updated system applications, the signatureOrSystem permission + // is granted only if it had been defined by the original application. + if ((pkg.applicationInfo.flags + & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + PackageSetting sysPs = mSettings.getDisabledSystemPkg(pkg.packageName); + if(sysPs.grantedPermissions.contains(perm)) { + allowed = true; + } else { + allowed = false; + } + } else { + allowed = true; + } } } } else { @@ -3157,6 +3184,7 @@ class PackageManagerService extends IPackageManager.Stub { if (removedPackage != null) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, removedUid); + extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false); sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras); } if (addedPackage != null) { @@ -3194,7 +3222,7 @@ class PackageManagerService extends IPackageManager.Stub { // There appears to be a subtle deadlock condition if the sendPackageBroadcast call appears // in the synchronized block above. if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { - res.removedInfo.sendBroadcast(); + res.removedInfo.sendBroadcast(false, true); Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, @@ -3842,7 +3870,7 @@ class PackageManagerService extends IPackageManager.Stub { } if(res && sendBroadCast) { - info.sendBroadcast(); + info.sendBroadcast(deleteCodeAndResources, false); } return res; } @@ -3852,9 +3880,13 @@ class PackageManagerService extends IPackageManager.Stub { int uid = -1; int removedUid = -1; - void sendBroadcast() { + void sendBroadcast(boolean fullRemove, boolean replacing) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid); + extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove); + if (replacing) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } if (removedPackage != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras); } @@ -4463,6 +4495,10 @@ class PackageManagerService extends IPackageManager.Stub { mSystemReady = true; } + public boolean isSafeMode() { + return mSafeMode; + } + public boolean hasSystemUidErrors() { return mHasSystemUidErrors; } @@ -4482,7 +4518,7 @@ class PackageManagerService extends IPackageManager.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump ActivityManager from from pid=" + Binder.getCallingPid() @@ -5637,23 +5673,8 @@ class PackageManagerService extends IPackageManager.Stub { } for (PackageSetting pkg : mDisabledSysPackages.values()) { - serializer.startTag(null, "updated-package"); - serializer.attribute(null, "name", pkg.name); - serializer.attribute(null, "codePath", pkg.codePathString); - serializer.attribute(null, "ts", pkg.getTimeStampStr()); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } - if (pkg.sharedUser == null) { - serializer.attribute(null, "userId", - Integer.toString(pkg.userId)); - } else { - serializer.attribute(null, "sharedUserId", - Integer.toString(pkg.userId)); - } - serializer.endTag(null, "updated-package"); + writeDisabledSysPackage(serializer, pkg); } - serializer.startTag(null, "preferred-packages"); int N = mPreferredPackages.size(); @@ -5716,6 +5737,43 @@ class PackageManagerService extends IPackageManager.Stub { //Debug.stopMethodTracing(); } + void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { + serializer.startTag(null, "updated-package"); + serializer.attribute(null, "name", pkg.name); + serializer.attribute(null, "codePath", pkg.codePathString); + serializer.attribute(null, "ts", pkg.getTimeStampStr()); + if (!pkg.resourcePathString.equals(pkg.codePathString)) { + serializer.attribute(null, "resourcePath", pkg.resourcePathString); + } + if (pkg.sharedUser == null) { + serializer.attribute(null, "userId", + Integer.toString(pkg.userId)); + } else { + serializer.attribute(null, "sharedUserId", + Integer.toString(pkg.userId)); + } + serializer.startTag(null, "perms"); + if (pkg.sharedUser == null) { + // If this is a shared user, the permissions will + // be written there. We still need to write an + // empty permissions list so permissionsFixed will + // be set. + for (final String name : pkg.grantedPermissions) { + BasePermission bp = mPermissions.get(name); + if ((bp != null) && (bp.perm != null) && (bp.perm.info != null)) { + // We only need to write signature or system permissions but this wont + // match the semantics of grantedPermissions. So write all permissions. + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + } + } + serializer.endTag(null, "perms"); + serializer.endTag(null, "updated-package"); + } + void writePackage(XmlSerializer serializer, final PackageSetting pkg) throws java.io.IOException { serializer.startTag(null, "package"); @@ -5892,33 +5950,7 @@ class PackageManagerService extends IPackageManager.Stub { } else if (tagName.equals("preferred-activities")) { readPreferredActivitiesLP(parser); } else if(tagName.equals("updated-package")) { - String name = parser.getAttributeValue(null, "name"); - String codePathStr = parser.getAttributeValue(null, "codePath"); - String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); - if(resourcePathStr == null) { - resourcePathStr = codePathStr; - } - - int pkgFlags = 0; - pkgFlags |= ApplicationInfo.FLAG_SYSTEM; - PackageSetting ps = new PackageSetting(name, - new File(codePathStr), - new File(resourcePathStr), pkgFlags); - String timeStampStr = parser.getAttributeValue(null, "ts"); - if (timeStampStr != null) { - try { - long timeStamp = Long.parseLong(timeStampStr); - ps.setTimeStamp(timeStamp, timeStampStr); - } catch (NumberFormatException e) { - } - } - String idStr = parser.getAttributeValue(null, "userId"); - ps.userId = idStr != null ? Integer.parseInt(idStr) : 0; - if(ps.userId <= 0) { - String sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); - ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; - } - mDisabledSysPackages.put(name, ps); + readDisabledSysPackageLP(parser); } else { Log.w(TAG, "Unknown element under : " + parser.getName()); @@ -6056,6 +6088,58 @@ class PackageManagerService extends IPackageManager.Stub { } } + private void readDisabledSysPackageLP(XmlPullParser parser) + throws XmlPullParserException, IOException { + String name = parser.getAttributeValue(null, "name"); + String codePathStr = parser.getAttributeValue(null, "codePath"); + String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); + if(resourcePathStr == null) { + resourcePathStr = codePathStr; + } + + int pkgFlags = 0; + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + PackageSetting ps = new PackageSetting(name, + new File(codePathStr), + new File(resourcePathStr), pkgFlags); + String timeStampStr = parser.getAttributeValue(null, "ts"); + if (timeStampStr != null) { + try { + long timeStamp = Long.parseLong(timeStampStr); + ps.setTimeStamp(timeStamp, timeStampStr); + } catch (NumberFormatException e) { + } + } + String idStr = parser.getAttributeValue(null, "userId"); + ps.userId = idStr != null ? Integer.parseInt(idStr) : 0; + if(ps.userId <= 0) { + String sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); + ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; + } + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("perms")) { + readGrantedPermissionsLP(parser, + ps.grantedPermissions); + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under : " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + mDisabledSysPackages.put(name, ps); + } + private void readPackageLP(XmlPullParser parser) throws XmlPullParserException, IOException { String name = null; diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index ca0ad1a15c82f..7c111d3d8c8ba 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -486,8 +486,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage public void acquireWakeLock(int flags, IBinder lock, String tag) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); - synchronized (mLocks) { - acquireWakeLockLocked(flags, lock, tag); + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLocks) { + acquireWakeLockLocked(flags, lock, tag); + } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -572,12 +577,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } if (acquireType >= 0) { + long origId = Binder.clearCallingIdentity(); try { - long origId = Binder.clearCallingIdentity(); mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType); - Binder.restoreCallingIdentity(origId); } catch (RemoteException e) { // Ignore + } finally { + Binder.restoreCallingIdentity(origId); } } } @@ -627,12 +633,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage releaseType = wl.monitorType; if (releaseType >= 0) { + long origId = Binder.clearCallingIdentity(); try { - long origId = Binder.clearCallingIdentity(); mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType); - Binder.restoreCallingIdentity(origId); } catch (RemoteException e) { // Ignore + } finally { + Binder.restoreCallingIdentity(origId); } } } @@ -757,7 +764,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump PowerManager from from pid=" + Binder.getCallingPid() @@ -960,8 +967,10 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage Log.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock); } if (mContext != null) { - mContext.sendOrderedBroadcast(mScreenOnIntent, null, - mScreenOnBroadcastDone, mHandler, 0, null, null); + if (ActivityManagerNative.isSystemReady()) { + mContext.sendOrderedBroadcast(mScreenOnIntent, null, + mScreenOnBroadcastDone, mHandler, 0, null, null); + } } else { synchronized (mLocks) { EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 2, @@ -1232,6 +1241,14 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } if (reallyTurnScreenOn) { err = Power.setScreenState(true); + long identity = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteScreenOn(); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e); + } finally { + Binder.restoreCallingIdentity(identity); + } } else { Power.setScreenState(false); // But continue as if we really did turn the screen on... @@ -1250,6 +1267,14 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } else { mScreenOffTime = SystemClock.elapsedRealtime(); + long identity = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteScreenOff(); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e); + } finally { + Binder.restoreCallingIdentity(identity); + } if (!mScreenBrightness.animating) { err = turnScreenOffLocked(becauseOfUser); } else { diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java index f56088c642a78..461b00600c22a 100644 --- a/services/java/com/android/server/SensorService.java +++ b/services/java/com/android/server/SensorService.java @@ -105,17 +105,17 @@ class SensorService extends ISensorService.Stub { return _sensors_control_open(); } - public boolean enableSensor(IBinder binder, int sensor, int enable) + public boolean enableSensor(IBinder binder, String name, int sensor, int enable) throws RemoteException { - if (localLOGV) Log.d(TAG, "enableSensor " + sensor + " " + enable); + if (localLOGV) Log.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable); // Inform battery statistics service of status change int uid = Binder.getCallingUid(); long identity = Binder.clearCallingIdentity(); if (enable == SENSOR_DISABLE) { - mBatteryStats.noteStopSensor(uid, sensor); + mBatteryStats.noteStopSensor(uid, name, sensor); } else { - mBatteryStats.noteStartSensor(uid, sensor); + mBatteryStats.noteStartSensor(uid, name, sensor); } Binder.restoreCallingIdentity(identity); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7f7a52e0acceb..d6245736e3c3a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -16,35 +16,36 @@ package com.android.server; +import com.android.server.am.ActivityManagerService; +import com.android.server.status.StatusBarService; + +import dalvik.system.PathClassLoader; +import dalvik.system.VMRuntime; + import android.app.ActivityManagerNative; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentService; import android.content.Context; +import android.content.Intent; import android.content.pm.IPackageManager; import android.database.ContentObserver; import android.database.Cursor; import android.media.AudioService; +import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.IBinder; -import android.provider.Settings; import android.provider.Contacts.People; -import android.server.BluetoothDeviceService; +import android.provider.Settings; import android.server.BluetoothA2dpService; -import android.server.checkin.FallbackCheckinService; +import android.server.BluetoothDeviceService; import android.server.search.SearchManagerService; import android.util.EventLog; import android.util.Log; -import dalvik.system.TouchDex; -import dalvik.system.VMRuntime; - -import com.android.server.am.ActivityManagerService; -import com.android.server.status.StatusBarService; - import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -180,7 +181,7 @@ class ServerThread extends Thread { StatusBarService statusBar = null; InputMethodManagerService imm = null; - + if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { try { Log.i(TAG, "Starting Status Bar Service."); @@ -205,7 +206,7 @@ class ServerThread extends Thread { } catch (Throwable e) { Log.e(TAG, "Failure starting Input Manager Service", e); } - + try { Log.i(TAG, "Starting Hardware Service."); ServiceManager.addService("hardware", new HardwareService(context)); @@ -272,9 +273,14 @@ class ServerThread extends Thread { } try { - Log.i(TAG, "Starting Checkin Service"); - addService(context, "checkin", "com.google.android.server.checkin.CheckinService", - FallbackCheckinService.class); + Log.i(TAG, "Starting Checkin Service."); + Intent intent = new Intent().setComponent(new ComponentName( + "com.google.android.server.checkin", + "com.google.android.server.checkin.CheckinService")); + if (context.startService(intent) == null) { + Log.w(TAG, "Using fallback Checkin Service."); + ServiceManager.addService("checkin", new FallbackCheckinService(context)); + } } catch (Throwable e) { Log.e(TAG, "Failure starting Checkin Service", e); } @@ -318,6 +324,7 @@ class ServerThread extends Thread { false, new AdbSettingsObserver()); // It is now time to start up the app processes... + boolean safeMode = wm.detectSafeMode(); if (statusBar != null) { statusBar.systemReady(); } @@ -342,51 +349,6 @@ class ServerThread extends Thread { Looper.loop(); Log.d(TAG, "System ServerThread is exiting!"); } - - private void addService(Context context, String name, String serviceClass, - Class fallback) { - - final IBinder service = findService(context, serviceClass, fallback); - if (service != null) { - ServiceManager.addService(name, service); - } else { - Log.e(TAG, "Failure starting service '" + name + "' with class " + serviceClass); - } - } - - private IBinder findService(Context context, String serviceClass, - Class fallback) { - - IBinder service = null; - try { - Class klass = Class.forName(serviceClass); - Constructor c = klass.getConstructor(Context.class); - service = (IBinder) c.newInstance(context); - } catch (ClassNotFoundException e) { - // Ignore - } catch (IllegalAccessException e) { - // Ignore - } catch (NoSuchMethodException e) { - // Ignore - } catch (InvocationTargetException e) { - // Ignore - } catch (InstantiationException e) { - // Ignore - } - - if (service == null && fallback != null) { - Log.w(TAG, "Could not find " + serviceClass + ", trying fallback"); - try { - service = fallback.newInstance(); - } catch (IllegalAccessException e) { - // Ignore - } catch (InstantiationException e) { - // Ignore - } - } - - return service; - } } class DemoThread extends Thread diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index dbaf0869547ac..b5cf1aabd702a 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -363,7 +363,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump telephony.registry from from pid=" + Binder.getCallingPid() diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 2d61b1e537640..c009fac18d699 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -22,6 +22,7 @@ import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; +import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -204,8 +205,6 @@ public class WifiService extends IWifiManager.Stub { mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK); synchronized (sDriverStopWakeLock) { if (sDriverStopWakeLock.isHeld()) { - if (DBG) Log.d(TAG, "Releasing driver-stop wakelock " + - sDriverStopWakeLock); sDriverStopWakeLock.release(); } } @@ -213,9 +212,18 @@ public class WifiService extends IWifiManager.Stub { } ); - Log.d(TAG, "WifiService starting up with Wi-Fi " + - (wifiEnabled ? "enabled" : "disabled")); - registerForBroadcasts(); + Log.i(TAG, "WifiService starting up with Wi-Fi " + + (wifiEnabled ? "enabled" : "disabled")); + + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateWifiState(); + } + }, + new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); + setWifiEnabledBlocking(wifiEnabled, false); } @@ -293,7 +301,7 @@ public class WifiService extends IWifiManager.Stub { decrementHiddentNetworkPresentCounter(); } } - mIsHiddenNetworkPresent.put(netId, new Boolean(present)); + mIsHiddenNetworkPresent.put(netId, present); } } else { Log.e(TAG, "addOrUpdateHiddenNetwork(): Invalid (negative) network id!"); @@ -422,6 +430,7 @@ public class WifiService extends IWifiManager.Stub { /** * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} + * @param enable {@code true} to enable, {@code false} to disable. * @return {@code true} if the enable/disable operation was * started or is already in the queue. */ @@ -429,10 +438,6 @@ public class WifiService extends IWifiManager.Stub { enforceChangePermission(); if (mWifiHandler == null) return false; - /* - * Remove any enable/disable Wi-Fi messages we may have in the queue - * before adding a new one - */ synchronized (mWifiHandler) { sWakeLock.acquire(); sendEnableMessage(enable, true); @@ -458,40 +463,42 @@ public class WifiService extends IWifiManager.Stub { return false; } - updateWifiState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING); + setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING); if (enable) { if (!WifiNative.loadDriver()) { Log.e(TAG, "Failed to load Wi-Fi driver."); - updateWifiState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN); return false; } if (!WifiNative.startSupplicant()) { WifiNative.unloadDriver(); Log.e(TAG, "Failed to start supplicant daemon."); - updateWifiState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN); return false; } + registerForBroadcasts(); mWifiStateTracker.startEventLoop(); } else { - // Remove notification (it will no-op if it isn't visible) + mContext.unregisterReceiver(mReceiver); + // Remove notification (it will no-op if it isn't visible) mWifiStateTracker.setNotificationVisible(false, 0, false, 0); boolean failedToStopSupplicantOrUnloadDriver = false; if (!WifiNative.stopSupplicant()) { Log.e(TAG, "Failed to stop supplicant daemon."); - updateWifiState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN); failedToStopSupplicantOrUnloadDriver = true; } - + // We must reset the interface before we unload the driver mWifiStateTracker.resetInterface(); - + if (!WifiNative.unloadDriver()) { Log.e(TAG, "Failed to unload Wi-Fi driver."); if (!failedToStopSupplicantOrUnloadDriver) { - updateWifiState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN); failedToStopSupplicantOrUnloadDriver = true; } } @@ -505,7 +512,7 @@ public class WifiService extends IWifiManager.Stub { if (persist) { persistWifiEnabled(enable); } - updateWifiState(eventualWifiState); + setWifiEnabledState(eventualWifiState); /* * Initialize the hidden networks state and the number of allowed @@ -519,7 +526,7 @@ public class WifiService extends IWifiManager.Stub { return true; } - private void updateWifiState(int wifiState) { + private void setWifiEnabledState(int wifiState) { final int previousWifiState = mWifiState; // Update state @@ -527,6 +534,7 @@ public class WifiService extends IWifiManager.Stub { // Broadcast final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState); intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState); mContext.sendStickyBroadcast(intent); @@ -551,7 +559,7 @@ public class WifiService extends IWifiManager.Stub { * {@link WifiManager#WIFI_STATE_ENABLING}, * {@link WifiManager#WIFI_STATE_UNKNOWN} */ - public int getWifiState() { + public int getWifiEnabledState() { enforceAccessPermission(); return mWifiState; } @@ -1082,9 +1090,7 @@ public class WifiService extends IWifiManager.Stub { */ removeNetworkIfHidden(netId); - synchronized (mWifiStateTracker) { - return WifiNative.removeNetworkCommand(netId); - } + return mWifiStateTracker.removeNetwork(netId); } /** @@ -1221,7 +1227,6 @@ public class WifiService extends IWifiManager.Stub { if (scanResult != null) { scanResult.level = -scanResultLevel; scanList.add(scanResult); - //if (DBG) Log.d(TAG, "ScanResult: " + scanResult); } } else if (DBG) { Log.w(TAG, @@ -1344,7 +1349,7 @@ public class WifiService extends IWifiManager.Stub { if (result && mNeedReconfig) { mNeedReconfig = false; result = WifiNative.reloadConfigCommand(); - + if (result) { Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION); mContext.sendBroadcast(intent); @@ -1446,9 +1451,7 @@ public class WifiService extends IWifiManager.Stub { int stayAwakeConditions = Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); - if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { - /* do nothing, we'll check isAirplaneModeOn later. */ - } else if (action.equals(Intent.ACTION_SCREEN_ON)) { + if (action.equals(Intent.ACTION_SCREEN_ON)) { mAlarmManager.cancel(mIdleIntent); mDeviceIdle = false; mScreenOff = false; @@ -1543,7 +1546,6 @@ public class WifiService extends IWifiManager.Stub { } private void sendStartMessage(boolean scanOnlyMode) { - if (DBG) Log.d(TAG, "sendStartMessage(" + scanOnlyMode + ")"); Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget(); } @@ -1561,6 +1563,9 @@ public class WifiService extends IWifiManager.Stub { } synchronized (mWifiHandler) { + if (mWifiState == WIFI_STATE_ENABLING && !airplaneMode) { + return; + } if (wifiShouldBeEnabled) { if (wifiShouldBeStarted) { sWakeLock.acquire(); @@ -1573,6 +1578,15 @@ public class WifiService extends IWifiManager.Stub { mContext.getContentResolver(), Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS, DEFAULT_WAKELOCK_TIMEOUT); + /* + * The following wakelock is held in order to ensure + * that the connectivity manager has time to fail over + * to the mobile data network. The connectivity manager + * releases it once mobile data connectivity has been + * established. If connectivity cannot be established, + * the wakelock is released after wakeLockTimeout + * milliseconds have elapsed. + */ sDriverStopWakeLock.acquire(); mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI); mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout); @@ -1585,18 +1599,15 @@ public class WifiService extends IWifiManager.Stub { } private void registerForBroadcasts() { + IntentFilter intentFilter = new IntentFilter(); if (isAirplaneSensitive()) { - mContext.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); + intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); } - mContext.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_SCREEN_ON)); - mContext.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_SCREEN_OFF)); - mContext.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - mContext.registerReceiver(mReceiver, - new IntentFilter(ACTION_DEVICE_IDLE)); + intentFilter.addAction(Intent.ACTION_SCREEN_ON); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); + intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); + intentFilter.addAction(ACTION_DEVICE_IDLE); + mContext.registerReceiver(mReceiver, intentFilter); } private boolean isAirplaneSensitive() { @@ -1664,7 +1675,7 @@ public class WifiService extends IWifiManager.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump WifiService from from pid=" + Binder.getCallingPid() @@ -1672,11 +1683,14 @@ public class WifiService extends IWifiManager.Stub { return; } pw.println("Wi-Fi is " + stateName(mWifiState)); - pw.println("stay-awake conditions: " + + pw.println("Stay-awake conditions: " + Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0)); pw.println(); + pw.println("Internal state:"); + pw.println(mWifiStateTracker); + pw.println(); pw.println("Latest scan results:"); List scanResults = mWifiStateTracker.getScanResultsList(); if (scanResults != null && scanResults.size() != 0) { @@ -1786,13 +1800,6 @@ public class WifiService extends IWifiManager.Stub { return -1; } - private synchronized void clear() { - if (!mList.isEmpty()) { - mList.clear(); - updateWifiState(); - } - } - private void dump(PrintWriter pw) { for (WifiLock l : mList) { pw.print(" "); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 7ed0aecdbc030..09f5d8f1ecaab 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -122,7 +122,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo static final boolean DEBUG = false; static final boolean DEBUG_FOCUS = false; static final boolean DEBUG_ANIM = false; + static final boolean DEBUG_LAYERS = false; static final boolean DEBUG_INPUT = false; + static final boolean DEBUG_INPUT_METHOD = false; static final boolean DEBUG_VISIBILITY = false; static final boolean DEBUG_ORIENTATION = false; static final boolean DEBUG_APP_TRANSITIONS = false; @@ -202,6 +204,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final boolean mHaveInputMethods; + final boolean mLimitedAlphaCompositing; + final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager(); final IActivityManager mActivityManager; @@ -306,6 +310,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final float[] mTmpFloats = new float[9]; + boolean mSafeMode; boolean mDisplayEnabled = false; boolean mSystemBooted = false; int mRotation = 0; @@ -316,7 +321,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean mLayoutNeeded = true; boolean mAnimationPending = false; - boolean mSurfacesChanged = false; boolean mDisplayFrozen = false; boolean mWindowsFreezingScreen = false; long mFreezeGcPending = 0; @@ -347,6 +351,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // This just indicates the window the input method is on top of, not // necessarily the window its input is going to. WindowState mInputMethodTarget = null; + boolean mInputMethodTargetWaitingAnim; + int mInputMethodAnimLayerAdjustment; WindowState mInputMethodWindow = null; final ArrayList mInputMethodDialogs = new ArrayList(); @@ -461,6 +467,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean haveInputMethods) { mContext = context; mHaveInputMethods = haveInputMethods; + mLimitedAlphaCompositing = context.getResources().getBoolean( + com.android.internal.R.bool.config_sf_limitedAlpha); mPowerManager = pm; mPowerManager.setPolicy(mPolicy); @@ -734,7 +742,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } - int findDesiredInputMethodWindowIndexLocked() { + int findDesiredInputMethodWindowIndexLocked(boolean willMove) { final ArrayList localmWindows = mWindows; final int N = localmWindows.size(); WindowState w = null; @@ -753,17 +761,67 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } + + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Desired input method target=" + + w + " willMove=" + willMove); + + if (willMove && w != null) { + final WindowState curTarget = mInputMethodTarget; + if (curTarget != null && curTarget.mAppToken != null) { + int curIndex = -1; + if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" + + mNextAppTransition + " curTarget animating=" + + curTarget.isAnimating() + + " layer=" + curTarget.mAnimLayer + + " new layer=" + w.mAnimLayer); + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + // If we are currently setting up for an animation, + // hold everything until we can find out what will happen. + mInputMethodTargetWaitingAnim = true; + curIndex = localmWindows.indexOf(curTarget); + } else if (curTarget.isAnimating() && + curTarget.mAnimLayer > w.mAnimLayer) { + // If the window we are currently targeting is involved + // with an animation, and it is on top of the next target + // we will be over, then hold off on moving until + // that is done. + curIndex = localmWindows.indexOf(curTarget); + } + if (curIndex >= 0) { + return curIndex + 1; + } + } + } + //Log.i(TAG, "Placing input method @" + (i+1)); if (w != null) { - mInputMethodTarget = w; + if (willMove) { + RuntimeException e = new RuntimeException(); + e.fillInStackTrace(); + if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " + + mInputMethodTarget + " to " + w, e); + mInputMethodTarget = w; + if (w.mAppToken != null) { + setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment); + } else { + setInputMethodAnimLayerAdjustment(0); + } + } return i+1; } - mInputMethodTarget = null; + if (willMove) { + RuntimeException e = new RuntimeException(); + e.fillInStackTrace(); + if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " + + mInputMethodTarget + " to null", e); + mInputMethodTarget = null; + setInputMethodAnimLayerAdjustment(0); + } return -1; } void addInputMethodWindowToListLocked(WindowState win) { - int pos = findDesiredInputMethodWindowIndexLocked(); + int pos = findDesiredInputMethodWindowIndexLocked(true); if (pos >= 0) { win.mTargetAppToken = mInputMethodTarget.mAppToken; mWindows.add(pos, win); @@ -775,6 +833,33 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo moveInputMethodDialogsLocked(pos); } + void setInputMethodAnimLayerAdjustment(int adj) { + if (DEBUG_LAYERS) Log.v(TAG, "Setting im layer adj to " + adj); + mInputMethodAnimLayerAdjustment = adj; + WindowState imw = mInputMethodWindow; + if (imw != null) { + imw.mAnimLayer = imw.mLayer + adj; + if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw + + " anim layer: " + imw.mAnimLayer); + int wi = imw.mChildWindows.size(); + while (wi > 0) { + wi--; + WindowState cw = (WindowState)imw.mChildWindows.get(wi); + cw.mAnimLayer = cw.mLayer + adj; + if (DEBUG_LAYERS) Log.v(TAG, "IM win " + cw + + " anim layer: " + cw.mAnimLayer); + } + } + int di = mInputMethodDialogs.size(); + while (di > 0) { + di --; + imw = mInputMethodDialogs.get(di); + imw.mAnimLayer = imw.mLayer + adj; + if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw + + " anim layer: " + imw.mAnimLayer); + } + } + private int tmpRemoveWindowLocked(int interestingPos, WindowState win) { int wpos = mWindows.indexOf(win); if (wpos >= 0) { @@ -814,9 +899,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } if (pos >= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; - WindowState wp = (WindowState)mWindows.get(pos); - if (wp == mInputMethodWindow) { - pos++; + if (pos < mWindows.size()) { + WindowState wp = (WindowState)mWindows.get(pos); + if (wp == mInputMethodWindow) { + pos++; + } } for (int i=0; i= 0) { // In this case, the input method windows are to be placed // immediately above the window they are targeting. @@ -915,7 +1002,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } void adjustInputMethodDialogsLocked() { - moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked()); + moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); } public int addWindow(Session session, IWindow client, @@ -975,7 +1062,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } - token = new WindowToken(attrs.token, -1); + token = new WindowToken(attrs.token, -1, false); addToken = true; } else if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { @@ -1087,11 +1174,23 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ": window=" + win); } + // sendNewConfiguration() checks caller permissions so we must call it with + // privilege. updateOrientationFromAppTokens() clears and resets the caller + // identity anyway, so it's safe to just clear & restore around this whole + // block. + final long origId = Binder.clearCallingIdentity(); if (reportNewConfig) { - final long origId = Binder.clearCallingIdentity(); sendNewConfiguration(); - Binder.restoreCallingIdentity(origId); + } else { + // Update Orientation after adding a window, only if the window needs to be + // displayed right away + if (win.isVisibleOrAdding()) { + if (updateOrientationFromAppTokens(null) != null) { + sendNewConfiguration(); + } + } } + Binder.restoreCallingIdentity(origId); return res; } @@ -1125,7 +1224,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " inPendingTransaction=" + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false) + " mDisplayFrozen=" + mDisplayFrozen); - + // Visibility of the removed window. Will be used later to update orientation later on. + boolean wasVisible = false; // First, see if we need to run an animation. If we do, we have // to hold off on removing the window until the animation is done. // If the display is frozen, just remove immediately, since the @@ -1133,7 +1233,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.mSurface != null && !mDisplayFrozen) { // If we are not currently running the exit animation, we // need to see about starting one. - if (win.isVisibleLw()) { + if (wasVisible=win.isVisibleLw()) { + int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; @@ -1161,6 +1262,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } removeWindowInnerLocked(session, win); + // Removing a visible window will effect the computed orientation + // So just update orientation if needed. + if (wasVisible) { + if (updateOrientationFromAppTokens(null) != null) { + sendNewConfiguration(); + } + } updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); Binder.restoreCallingIdentity(origId); } @@ -1191,10 +1299,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo TAG, "**** Removing window " + win + ": count=" + token.windows.size()); if (token.windows.size() == 0) { - if (atoken != token) { + if (!token.explicit) { mTokenMap.remove(token.token); mTokenList.remove(token); - } else { + } else if (atoken != null) { atoken.firstWindowDrawn = false; } } @@ -1287,7 +1395,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Surface outSurface) { boolean displayed = false; boolean inTouchMode; - + Configuration newConfig = null; long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { @@ -1435,6 +1543,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (assignLayers) { assignLayersLocked(); } + newConfig = updateOrientationFromAppTokensLocked(null); performLayoutAndPlaceSurfacesLocked(); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); @@ -1456,6 +1565,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo inTouchMode = mInTouchMode; } + if (newConfig != null) { + sendNewConfiguration(); + } + Binder.restoreCallingIdentity(origId); return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0) @@ -1708,7 +1821,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.w(TAG, "Attempted to add existing input method token: " + token); return; } - wtoken = new WindowToken(token, type); + wtoken = new WindowToken(token, type, true); mTokenMap.put(token, wtoken); mTokenList.add(wtoken); } @@ -1812,12 +1925,32 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } - public Configuration updateOrientationFromAppTokens( - IBinder freezeThisOneIfNeeded) { - boolean changed = false; - synchronized(mWindowMap) { + public int getOrientationFromWindowsLocked() { + int pos = mWindows.size() - 1; + while (pos >= 0) { + WindowState wtoken = (WindowState) mWindows.get(pos); + pos--; + if (wtoken.mAppToken != null) { + // We hit an application window. so the orientation will be determined by the + // app window. No point in continuing further. + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + if (!wtoken.isVisibleLw()) { + continue; + } + int req = wtoken.mAttrs.screenOrientation; + if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) || + (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ + continue; + } else { + return req; + } + } + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + + public int getOrientationFromAppTokensLocked() { int pos = mAppTokens.size() - 1; - int req = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; int curGroup = 0; int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean haveGroup = false; @@ -1838,7 +1971,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // the orientation behind it, then we'll stick with the // user's orientation. if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { - break; + return lastOrientation; } } int or = wtoken.requestedOrientation; @@ -1849,10 +1982,45 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo or == ActivityInfo.SCREEN_ORIENTATION_SENSOR || or == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR || or == ActivityInfo.SCREEN_ORIENTATION_USER) { - req = or; - break; + return or; } } + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + + public Configuration updateOrientationFromAppTokens( + IBinder freezeThisOneIfNeeded) { + Configuration config; + long ident = Binder.clearCallingIdentity(); + synchronized(mWindowMap) { + config = updateOrientationFromAppTokensLocked(freezeThisOneIfNeeded); + } + if (config != null) { + mLayoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + Binder.restoreCallingIdentity(ident); + return config; + } + + /* + * The orientation is computed from non-application windows first. If none of + * the non-application windows specify orientation, the orientation is computed from + * application tokens. + * @see android.view.IWindowManager#updateOrientationFromAppTokens( + * android.os.IBinder) + */ + public Configuration updateOrientationFromAppTokensLocked( + IBinder freezeThisOneIfNeeded) { + boolean changed = false; + Configuration config = null; + long ident = Binder.clearCallingIdentity(); + try { + int req = getOrientationFromWindowsLocked(); + if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + req = getOrientationFromAppTokensLocked(); + } + if (req != mForcedAppOrientation) { changed = true; mForcedAppOrientation = req; @@ -1873,14 +2041,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ActivityInfo.CONFIG_ORIENTATION); } } - Configuration config = computeNewConfigurationLocked(); - if (config != null) { - mLayoutNeeded = true; - performLayoutAndPlaceSurfacesLocked(); - } - return config; + return computeNewConfiguration(); } } + } finally { + Binder.restoreCallingIdentity(ident); } return null; @@ -2871,6 +3036,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return KeyInputQueue.getKeycodeState(devid, sw); } + public boolean hasKeys(int[] keycodes, boolean[] keyExists) { + return KeyInputQueue.hasKeys(keycodes, keyExists); + } + public void enableScreenAfterBoot() { synchronized(mWindowMap) { if (mSystemBooted) { @@ -2987,18 +3156,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { mRequestedRotation = rotation; } - if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from "+rotation); - rotation = mPolicy.rotationForOrientation(mForcedAppOrientation); - if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to "+rotation); + if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from " + rotation); + rotation = mPolicy.rotationForOrientation(mForcedAppOrientation, + mRotation, mDisplayEnabled); + if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to " + rotation); changed = mDisplayEnabled && mRotation != rotation; if (changed) { - mRotation = rotation; if (DEBUG_ORIENTATION) Log.v(TAG, "Rotation changed to " + rotation + " from " + mRotation + " (forceApp=" + mForcedAppOrientation + ", req=" + mRequestedRotation + ")"); + mRotation = rotation; mWindowsFreezingScreen = true; mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), @@ -3298,21 +3468,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return null; } + /* + * Instruct the Activity Manager to fetch the current configuration and broadcast + * that to config-changed listeners if appropriate. + */ void sendNewConfiguration() { - Configuration config; - synchronized (mWindowMap) { - config = computeNewConfigurationLocked(); - } - - if (config != null) { - try { - mActivityManager.updateConfiguration(config); - } catch (RemoteException e) { - } + try { + mActivityManager.updateConfiguration(null); + } catch (RemoteException e) { } } - Configuration computeNewConfigurationLocked() { + public Configuration computeNewConfiguration() { synchronized (mWindowMap) { if (mDisplay == null) { return null; @@ -4610,6 +4777,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } }; + public boolean detectSafeMode() { + mSafeMode = mPolicy.detectSafeMode(); + return mSafeMode; + } + public void systemReady() { mPolicy.systemReady(); } @@ -5639,6 +5811,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mAnimating = false; mAnimation = null; mAnimLayer = mLayer; + if (mIsImWindow) { + mAnimLayer += mInputMethodAnimLayerAdjustment; + } + if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this + + " anim layer: " + mAnimLayer); mHasTransformation = false; mPolicyVisibility = mPolicyVisibilityAfterAnim; mTransformation.clear(); @@ -5755,7 +5932,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // transforming since it is more important to have that // animation be smooth. mShownAlpha = mAlpha; - if (false && (!PixelFormat.formatHasAlpha(mAttrs.format) + if (!mLimitedAlphaCompositing + || (!PixelFormat.formatHasAlpha(mAttrs.format) || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) && x == frame.left && y == frame.top))) { //Log.i(TAG, "Applying alpha transform"); @@ -5944,21 +6122,23 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return mHasDrawn; } - public void showLw(boolean doAnimation) { + public boolean showLw(boolean doAnimation) { if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim) { - mSurfacesChanged = true; mPolicyVisibility = true; mPolicyVisibilityAfterAnim = true; if (doAnimation) { applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true); } requestAnimationLocked(0); + return true; } + return false; } - public void hideLw(boolean doAnimation) { - if (mPolicyVisibility || mPolicyVisibilityAfterAnim) { - mSurfacesChanged = true; + public boolean hideLw(boolean doAnimation) { + boolean current = doAnimation ? mPolicyVisibilityAfterAnim + : mPolicyVisibility; + if (current) { if (doAnimation) { applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false); if (mAnimation == null) { @@ -5968,10 +6148,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (doAnimation) { mPolicyVisibilityAfterAnim = false; } else { + mPolicyVisibilityAfterAnim = false; mPolicyVisibility = false; } requestAnimationLocked(0); + return true; } + return false; } void dump(PrintWriter pw, String prefix) { @@ -6054,6 +6237,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // The type of window this token is for, as per WindowManager.LayoutParams. final int windowType; + // Set if this token was explicitly added by a client, so should + // not be removed when all windows are removed. + final boolean explicit; + // If this is an AppWindowToken, this is non-null. AppWindowToken appWindowToken; @@ -6069,9 +6256,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Temporary for finding which tokens no longer have visible windows. boolean hasVisible; - WindowToken(IBinder _token, int type) { + WindowToken(IBinder _token, int type, boolean _explicit) { token = _token; windowType = type; + explicit = _explicit; } void dump(PrintWriter pw, String prefix) { @@ -6151,7 +6339,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean firstWindowDrawn; AppWindowToken(IApplicationToken _token) { - super(_token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION); + super(_token.asBinder(), + WindowManager.LayoutParams.TYPE_APPLICATION, true); appWindowToken = this; appToken = _token; } @@ -6198,17 +6387,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo for (int i=0; i 0) { - di --; - imw = mInputMethodDialogs.get(di); - imw.mAnimLayer = imw.mLayer + adj; - } + setInputMethodAnimLayerAdjustment(adj); } } } @@ -6295,7 +6477,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo clearAnimation(); animating = false; - + if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) { + moveInputMethodWindowsIfNeededLocked(true); + } + if (DEBUG_ANIM) Log.v( TAG, "Animation done in " + this + ": reportedVisible=" + reportedVisible); @@ -6519,6 +6704,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo synchronized(mWindowMap) { lastFocus = mLastFocus; newFocus = mCurrentFocus; + if (lastFocus == newFocus) { + // Focus is not changing, so nothing to do. + return; + } mLastFocus = newFocus; //Log.i(TAG, "Focus moving from " + lastFocus // + " to " + newFocus); @@ -6848,7 +7037,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo synchronized (mWindowMap) { // The focus for the client is the window immediately below // where we would place the input method window. - int idx = findDesiredInputMethodWindowIndexLocked(); + int idx = findDesiredInputMethodWindowIndexLocked(false); if (idx > 0) { WindowState imFocus = (WindowState)mWindows.get(idx-1); if (imFocus != null && imFocus.mSession.mClient != null && @@ -6888,13 +7077,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } private final void assignLayersLocked() { - if (mInLayout) { - if (Config.DEBUG) { - throw new RuntimeException("Recursive call!"); - } - return; - } - int N = mWindows.size(); int curBaseLayer = 0; int curLayer = 0; @@ -6916,6 +7098,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { w.mAnimLayer = w.mLayer; } + if (w.mIsImWindow) { + w.mAnimLayer += mInputMethodAnimLayerAdjustment; + } + if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": " + + w.mAnimLayer); //System.out.println( // "Assigned layer " + curLayer + " to " + w.mClient.asBinder()); } @@ -6927,6 +7114,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (Config.DEBUG) { throw new RuntimeException("Recursive call!"); } + Log.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout"); return; } @@ -6999,7 +7187,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo WindowState win = (WindowState) mWindows.get(i); boolean gone = win.mViewVisibility == View.GONE - || !win.mRelayoutCalled; + || !win.mRelayoutCalled + || win.mToken.hidden; // If this view is GONE, then skip it -- keep the current // frame, and let the caller know so they can ignore it @@ -7285,6 +7474,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // This has changed the visibility of windows, so perform // a new layout to get them all up-to-date. mLayoutNeeded = true; + moveInputMethodWindowsIfNeededLocked(true); performLayoutLockedInner(); updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES); @@ -7297,7 +7487,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final boolean someoneLosingFocus = mLosingFocus.size() != 0; - mSurfacesChanged = false; boolean obscured = false; boolean blurring = false; boolean dimming = false; @@ -7673,7 +7862,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mDimCurrentAlpha += mDimDeltaPerMs * (currentTime-mLastDimAnimTime); boolean more = true; - if (mDimDeltaPerMs > 0) { + if (mDisplayFrozen) { + // If the display is frozen, there is no reason to animate. + more = false; + } else if (mDimDeltaPerMs > 0) { if (mDimCurrentAlpha > mDimTargetAlpha) { more = false; } @@ -7811,7 +8003,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } /** - * Have the surface flinger show a surface, robustly dealing wit + * Have the surface flinger show a surface, robustly dealing with * error conditions. In particular, if there is not enough memory * to show the surface, then we will try to get rid of other surfaces * in order to succeed. @@ -7925,9 +8117,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mCurrentFocus != newFocus) { // This check makes sure that we don't already have the focus // change message pending. - if (mLastFocus == mCurrentFocus) { - mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE); - } + mH.removeMessages(H.REPORT_FOCUS_CHANGE); + mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE); if (localLOGV) Log.v( TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus); final WindowState oldFocus = mCurrentFocus; @@ -8218,13 +8409,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(" mSystemBooted=" + mSystemBooted + " mDisplayEnabled=" + mDisplayEnabled); pw.println(" mLayoutNeeded=" + mLayoutNeeded - + " mSurfacesChanged=" + mSurfacesChanged + " mBlurShown=" + mBlurShown); pw.println(" mDimShown=" + mDimShown + " current=" + mDimCurrentAlpha + " target=" + mDimTargetAlpha + " delta=" + mDimDeltaPerMs + " lastAnimTime=" + mLastDimAnimTime); + pw.println(" mInputMethodAnimLayerAdjustment=" + + mInputMethodAnimLayerAdjustment); pw.println(" mDisplayFrozen=" + mDisplayFrozen + " mWindowsFreezingScreen=" + mWindowsFreezingScreen + " mAppsFreezingScreen=" + mAppsFreezingScreen); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 361c7f7b02d3f..f5efe4b591fe8 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -48,6 +48,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; @@ -96,6 +97,7 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.PrintWriter; +import java.lang.IllegalStateException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; @@ -631,6 +633,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * any user id that can impact battery performance. */ final BatteryStatsService mBatteryStatsService; + + /** + * information about component usage + */ + final UsageStatsService mUsageStatsService; /** * Current configuration information. HistoryRecord objects are given @@ -1053,6 +1060,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen m.mLaunchingActivity.setReferenceCounted(false); m.mBatteryStatsService.publish(context); + m.mUsageStatsService.publish(context); synchronized (thr) { thr.mReady = true; @@ -1186,8 +1194,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen procs = service.mLRUProcesses; } } - pw.println("Applications Memory Usage (kB):"); - dumpApplicationMemoryUsage(fd, pw, procs, " "); + dumpApplicationMemoryUsage(fd, pw, procs, " ", args); } } @@ -1224,6 +1231,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen systemDir, "batterystats.bin").toString()); mBatteryStatsService.getActiveStatistics().readLocked(); mBatteryStatsService.getActiveStatistics().writeLocked(); + + mUsageStatsService = new UsageStatsService( new File( + systemDir, "usagestats.bin").toString()); mConfiguration.makeDefault(); mProcessStats.init(); @@ -1522,6 +1532,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.thread.scheduleLaunchActivity(new Intent(r.intent), r, r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward()); + // Update usage stats for launched activity + updateUsageStats(r, true); } catch (RemoteException e) { if (r.launchFailed) { // This is the second time we failed -- finish activity @@ -1804,6 +1816,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen prev.shortComponentName); prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving, prev.configChangeFlags); + updateUsageStats(prev, false); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. Log.w(TAG, "Exception thrown during pause", e); @@ -1848,6 +1861,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final void completePauseLocked() { HistoryRecord prev = mPausingActivity; if (DEBUG_PAUSE) Log.v(TAG, "Complete pause: " + prev); + if (prev != null) { if (prev.finishing) { if (DEBUG_PAUSE) Log.v(TAG, "Executing finish of activity: " + prev); @@ -2085,6 +2099,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ensureActivitiesVisibleLocked(r, starting, null, configChanges); } } + + private void updateUsageStats(HistoryRecord resumedComponent, boolean resumed) { + if (resumed) { + mUsageStatsService.noteResumeComponent(resumedComponent.realActivity); + } else { + mUsageStatsService.notePauseComponent(resumedComponent.realActivity); + } + } /** * Ensure that the top activity in the stack is resumed. @@ -2290,6 +2312,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen EventLog.writeEvent(LOG_AM_RESUME_ACTIVITY, System.identityHashCode(next), next.task.taskId, next.shortComponentName); + updateUsageStats(next, true); next.app.thread.scheduleResumeActivity(next, isNextTransitionForward()); @@ -2453,9 +2476,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We don't want to reuse the previous starting preview if: // (1) The current activity is in a different task. if (prev.task != r.task) prev = null; - // (2) The current activity is not the first in the task. - else if (!prev.frontOfTask) prev = null; - // (3) The current activity is already displayed. + // (2) The current activity is already displayed. else if (prev.nowVisible) prev = null; } mWindowManager.setAppStartingWindow( @@ -2536,6 +2557,40 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return null; } + /** + * Find the activity in the history stack within the given task. Returns + * the index within the history at which it's found, or < 0 if not found. + */ + private final int findActivityInHistoryLocked(HistoryRecord r, int task) { + int i = mHistory.size(); + while (i > 0) { + i--; + HistoryRecord candidate = (HistoryRecord)mHistory.get(i); + if (candidate.task.taskId != task) { + break; + } + if (candidate.realActivity.equals(r.realActivity)) { + return i; + } + } + + return -1; + } + + /** + * Reorder the history stack so that the activity at the given index is + * brought to the front. + */ + private final HistoryRecord moveActivityToFrontLocked(int where) { + HistoryRecord newTop = (HistoryRecord)mHistory.remove(where); + int top = mHistory.size(); + HistoryRecord oldTop = (HistoryRecord)mHistory.get(top-1); + mHistory.add(top, newTop); + oldTop.frontOfTask = false; + newTop.frontOfTask = true; + return newTop; + } + /** * Deliver a new Intent to an existing activity, so that its onNewIntent() * method will be called at the proper time. @@ -2969,6 +3024,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen resumeTopActivityLocked(null); return START_DELIVERED_TO_TOP; } + } else if (!addingToTask && + (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) { + // In this case, we are launching an activity in our own task + // that may already be running somewhere in the history, and + // we want to shuffle it to the front of the stack if so. + int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId); + if (where >= 0) { + HistoryRecord top = moveActivityToFrontLocked(where); + logStartActivity(LOG_AM_NEW_INTENT, r, top.task); + deliverNewIntentLocked(top, r.intent); + resumeTopActivityLocked(null); + return START_DELIVERED_TO_TOP; + } } // An existing activity is starting this new activity, so we want // to keep the new one in the same task as the one that is starting @@ -4142,8 +4210,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen long callingId = Binder.clearCallingIdentity(); try { IPackageManager pm = ActivityThread.getPackageManager(); + int pkgUid = -1; synchronized(this) { - int pkgUid = -1; try { pkgUid = pm.getPackageUid(packageName); } catch (RemoteException e) { @@ -4156,7 +4224,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen android.Manifest.permission.CLEAR_APP_USER_DATA, pid, uid, -1) == PackageManager.PERMISSION_GRANTED) { - uninstallPackageLocked(packageName, pkgUid, false); + restartPackageLocked(packageName, pkgUid); } else { throw new SecurityException(pid+" does not have permission:"+ android.Manifest.permission.CLEAR_APP_USER_DATA+" to clear data" + @@ -4167,6 +4235,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { //clear application user data pm.clearApplicationUserData(packageName, observer); + Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED, + Uri.fromParts("package", packageName, null)); + intent.putExtra(Intent.EXTRA_UID, pkgUid); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, MY_PID, Process.SYSTEM_UID); } catch (RemoteException e) { } } finally { @@ -4188,25 +4262,48 @@ public final class ActivityManagerService extends ActivityManagerNative implemen long callingId = Binder.clearCallingIdentity(); try { + IPackageManager pm = ActivityThread.getPackageManager(); + int pkgUid = -1; synchronized(this) { - uninstallPackageLocked(packageName, -1, false); - broadcastIntentLocked(null, null, - new Intent(Intent.ACTION_PACKAGE_RESTARTED, - Uri.fromParts("package", packageName, null)), - null, null, 0, null, null, null, - false, false, MY_PID, Process.SYSTEM_UID); + try { + pkgUid = pm.getPackageUid(packageName); + } catch (RemoteException e) { + } + if (pkgUid == -1) { + Log.w(TAG, "Invalid packageName:"+packageName); + return; + } + restartPackageLocked(packageName, pkgUid); } } finally { Binder.restoreCallingIdentity(callingId); } } + private void restartPackageLocked(final String packageName, int uid) { + uninstallPackageLocked(packageName, uid, false); + Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, + Uri.fromParts("package", packageName, null)); + intent.putExtra(Intent.EXTRA_UID, uid); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, MY_PID, Process.SYSTEM_UID); + } + private final void uninstallPackageLocked(String name, int uid, boolean callerWillRestart) { if (Config.LOGD) Log.d(TAG, "Uninstalling process " + name); int i, N; + final String procNamePrefix = name + ":"; + if (uid < 0) { + try { + uid = ActivityThread.getPackageManager().getPackageUid(name); + } catch (RemoteException e) { + } + } + Iterator> badApps = mProcessCrashTimes.getMap().values().iterator(); while (badApps.hasNext()) { SparseArray ba = badApps.next(); @@ -4217,14 +4314,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ArrayList procs = new ArrayList(); - final String procNamePrefix = name + ":"; - if (uid < 0) { - try { - uid = ActivityThread.getPackageManager().getPackageUid(name); - } catch (RemoteException e) { - } - } - // Remove all processes this package may have touched: all with the // same UID (except for the system or root user), and all whose name // matches the package name. @@ -7226,6 +7315,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public boolean testIsSystemReady() { + // no need to synchronize(this) just to read & return the value + return mSystemReady; + } + public void systemReady() { // In the simulator, startRunning will never have been called, which // normally sets a few crucial variables. Do it here instead. @@ -7628,6 +7722,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ActivityManager.RunningAppProcessInfo currApp = new ActivityManager.RunningAppProcessInfo(app.processName, app.pid, app.getPackageList()); + int adj = app.curAdj; + if (adj >= CONTENT_PROVIDER_ADJ) { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY; + } else if (adj >= HIDDEN_APP_MIN_ADJ) { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; + currApp.lru = adj - HIDDEN_APP_MIN_ADJ; + } else if (adj >= SECONDARY_SERVER_ADJ) { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; + } else if (adj >= VISIBLE_APP_ADJ) { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; + } else { + currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; + } + //Log.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance + // + " lru=" + currApp.lru); if (runList == null) { runList = new ArrayList(); } @@ -8176,30 +8285,55 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private static final void dumpApplicationMemoryUsage(FileDescriptor fd, - PrintWriter pw, List list, String prefix) { + PrintWriter pw, List list, String prefix, String[] args) { + final boolean isCheckinRequest = scanArgs(args, "-c"); + long uptime = SystemClock.uptimeMillis(); + long realtime = SystemClock.elapsedRealtime(); + + if (isCheckinRequest) { + // short checkin version + pw.println(uptime + "," + realtime); + pw.flush(); + } else { + pw.println("Applications Memory Usage (kB):"); + pw.println("Uptime: " + uptime + " Realtime: " + realtime); + } for (int i = list.size() - 1 ; i >= 0 ; i--) { ProcessRecord r = (ProcessRecord)list.get(i); if (r.thread != null) { - pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **"); - pw.flush(); - - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - try { - data.writeFileDescriptor(fd); - r.thread.asBinder().transact(IBinder.DUMP_TRANSACTION, data, reply, 0); - - } catch (RemoteException e) { - pw.println("Got RemoteException!"); + if (!isCheckinRequest) { + pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **"); pw.flush(); } - - data.recycle(); - reply.recycle(); + try { + r.thread.asBinder().dump(fd, args); + } catch (RemoteException e) { + if (!isCheckinRequest) { + pw.println("Got RemoteException!"); + pw.flush(); + } + } } } } + /** + * Searches array of arguments for the specified string + * @param args array of argument strings + * @param value value to search for + * @return true if the value is contained in the array + */ + private static boolean scanArgs(String[] args, String value) { + if (args != null) { + for (String arg : args) { + if (value.equals(arg)) { + return true; + } + } + } + return false; + } + private final int indexOfTokenLocked(IBinder token, boolean required) { int count = mHistory.size(); @@ -9120,6 +9254,36 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return 0; } + public IBinder peekService(Intent service, String resolvedType) { + // Refuse possible leaked file descriptors + if (service != null && service.hasFileDescriptors() == true) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + + IBinder ret = null; + + synchronized(this) { + ServiceLookupResult r = findServiceLocked(service, resolvedType); + + if (r != null) { + // r.record is null if findServiceLocked() failed the caller permission check + if (r.record == null) { + throw new SecurityException( + "Permission Denial: Accessing service " + r.record.name + + " from pid=" + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + r.permission); + } + IntentBindRecord ib = r.record.bindings.get(r.record.intent); + if (ib != null) { + ret = ib.binder; + } + } + } + + return ret; + } + public boolean stopServiceToken(ComponentName className, IBinder token, int startId) { synchronized(this) { @@ -9928,6 +10092,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { + if (!mSystemReady) { + // if the caller really truly claims to know what they're doing, go + // ahead and allow the broadcast without launching any receivers + int flags = intent.getFlags(); + if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) { + intent = new Intent(intent); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0){ + Log.e(TAG, "Attempt to launch receivers of broadcast intent " + intent + + " before boot completion"); + throw new IllegalStateException("Cannot broadcast before boot completed"); + } + } + final ProcessRecord callerApp = getRecordForAppLocked(caller); final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); @@ -10639,6 +10817,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // ========================================================= // CONFIGURATION // ========================================================= + + public ConfigurationInfo getDeviceConfigurationInfo() { + ConfigurationInfo config = new ConfigurationInfo(); + synchronized (this) { + config.reqTouchScreen = mConfiguration.touchscreen; + config.reqKeyboardType = mConfiguration.keyboard; + config.reqNavigation = mConfiguration.navigation; + if (mConfiguration.navigation != Configuration.NAVIGATION_NONAV) { + config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV; + } + if (mConfiguration.keyboard != Configuration.KEYBOARD_UNDEFINED) { + config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD; + } + } + return config; + } public Configuration getConfiguration() { Configuration ci; @@ -10653,6 +10847,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "updateConfiguration()"); synchronized(this) { + if (values == null && mWindowManager != null) { + // sentinel: fetch the current configuration from the window manager + values = mWindowManager.computeNewConfiguration(); + } + final long origId = Binder.clearCallingIdentity(); updateConfigurationLocked(values, null); Binder.restoreCallingIdentity(origId); diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index bf1bc8c6a02f5..1cd6298650329 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -65,6 +65,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } public BatteryStatsImpl getStatistics() { + mContext.enforceCallingPermission( + android.Manifest.permission.BATTERY_STATS, null); return mStats; } @@ -82,17 +84,59 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } - public void noteStartSensor(int uid, int sensor) { + public void noteStartSensor(int uid, String name, int sensor) { enforceCallingPermission(); synchronized (mStats) { - mStats.getUidStatsLocked(uid).noteStartSensor(sensor); + mStats.getUidStatsLocked(uid).noteStartSensor(name, sensor); } } - public void noteStopSensor(int uid, int sensor) { + public void noteStopSensor(int uid, String name, int sensor) { enforceCallingPermission(); synchronized (mStats) { - mStats.getUidStatsLocked(uid).noteStopSensor(sensor); + mStats.getUidStatsLocked(uid).noteStopSensor(name, sensor); + } + } + + public void noteStartGps(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteStartGps(uid); + } + } + + public void noteStopGps(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteStopGps(uid); + } + } + + public void noteRequestGpsOn(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteRequestGpsOn(uid); + } + } + + public void noteRequestGpsOff(int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteRequestGpsOff(uid); + } + } + + public void noteScreenOn() { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteScreenOn(); + } + } + + public void noteScreenOff() { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteScreenOff(); } } @@ -106,10 +150,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } public long getAwakeTimeBattery() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BATTERY_STATS, null); return mStats.getAwakeTimeBattery(); } public long getAwakeTimePlugged() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BATTERY_STATS, null); return mStats.getAwakeTimePlugged(); } @@ -117,7 +165,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub { if (Binder.getCallingPid() == Process.myPid()) { return; } - mContext.enforcePermission(android.Manifest.permission.BATTERY_STATS, + mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, Binder.getCallingPid(), Binder.getCallingUid(), null); } diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java index b370b1cdb6e8e..b4072081298bd 100644 --- a/services/java/com/android/server/am/HistoryRecord.java +++ b/services/java/com/android/server/am/HistoryRecord.java @@ -23,6 +23,7 @@ import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.os.Bundle; @@ -184,6 +185,11 @@ class HistoryRecord extends IApplicationToken.Stub { dataDir = aInfo.applicationInfo.dataDir; nonLocalizedLabel = aInfo.nonLocalizedLabel; labelRes = aInfo.labelRes; + if (nonLocalizedLabel == null && labelRes == 0) { + ApplicationInfo app = aInfo.applicationInfo; + nonLocalizedLabel = app.nonLocalizedLabel; + labelRes = app.labelRes; + } icon = aInfo.getIconResource(); theme = aInfo.getThemeResource(); if ((aInfo.flags&ActivityInfo.FLAG_MULTIPROCESS) != 0 diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java new file mode 100755 index 0000000000000..001987fd0ff34 --- /dev/null +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2006-2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import com.android.internal.app.IUsageStats; +import android.content.ComponentName; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import com.android.internal.os.PkgUsageStats; +import android.os.Process; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.util.Log; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * This service collects the statistics associated with usage + * of various components, like when a particular package is launched or + * paused and aggregates events like number of time a component is launched + * total duration of a component launch. + */ +public final class UsageStatsService extends IUsageStats.Stub { + public static final String SERVICE_NAME = "usagestats"; + private static final boolean localLOGV = false; + private static final String TAG = "UsageStats"; + static IUsageStats sService; + private Context mContext; + private String mFileName; + final private Map mStats; + private String mResumedPkg; + + private class PkgUsageStatsExtended { + int mLaunchCount; + long mUsageTime; + long mChgTime; + PkgUsageStatsExtended() { + mLaunchCount = 0; + mUsageTime = 0; + mChgTime = SystemClock.elapsedRealtime(); + } + void updateResume() { + mLaunchCount ++; + mChgTime = SystemClock.elapsedRealtime(); + } + void updatePause() { + long currTime = SystemClock.elapsedRealtime(); + mUsageTime += (currTime - mChgTime); + mChgTime = currTime; + } + } + + UsageStatsService(String filename) { + mFileName = filename; + mStats = new HashMap(); + } + + public void publish(Context context) { + mContext = context; + ServiceManager.addService(SERVICE_NAME, asBinder()); + } + + public static IUsageStats getService() { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService(SERVICE_NAME); + sService = asInterface(b); + return sService; + } + + public void noteResumeComponent(ComponentName componentName) { + enforceCallingPermission(); + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + if ((mResumedPkg != null) && (mResumedPkg.equalsIgnoreCase(pkgName))) { + // Moving across activities in same package. just return + return; + } + if (localLOGV) Log.i(TAG, "started component:"+pkgName); + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + pus = new PkgUsageStatsExtended(); + mStats.put(pkgName, pus); + } + pus.updateResume(); + mResumedPkg = pkgName; + } + + public void notePauseComponent(ComponentName componentName) { + enforceCallingPermission(); + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + if ((mResumedPkg == null) || (!pkgName.equalsIgnoreCase(mResumedPkg))) { + Log.w(TAG, "Something wrong here, Didn't expect "+pkgName+" to be paused"); + return; + } + if (localLOGV) Log.i(TAG, "paused component:"+pkgName); + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + // Weird some error here + Log.w(TAG, "No package stats for pkg:"+pkgName); + return; + } + pus.updatePause(); + } + + public void enforceCallingPermission() { + if (Binder.getCallingPid() == Process.myPid()) { + return; + } + mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, + Binder.getCallingPid(), Binder.getCallingUid(), null); + } + + public PkgUsageStats getPkgUsageStats(ComponentName componentName) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS, null); + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return null; + } + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + return null; + } + return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime); + } + + public PkgUsageStats[] getAllPkgUsageStats() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS, null); + synchronized (mStats) { + Set keys = mStats.keySet(); + int size = keys.size(); + if (size <= 0) { + return null; + } + PkgUsageStats retArr[] = new PkgUsageStats[size]; + int i = 0; + for (String key: keys) { + PkgUsageStatsExtended pus = mStats.get(key); + retArr[i] = new PkgUsageStats(key, pus.mLaunchCount, pus.mUsageTime); + i++; + } + return retArr; + } + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + StringBuilder sb = new StringBuilder(); + synchronized (mStats) { + Set keys = mStats.keySet(); + for (String key: keys) { + PkgUsageStatsExtended ps = mStats.get(key); + sb.append("pkg="); + sb.append(key); + sb.append(", launchCount="); + sb.append(ps.mLaunchCount); + sb.append(", usageTime="); + sb.append(ps.mUsageTime+" ms\n"); + } + } + pw.write(sb.toString()); + } +} diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index 00ff7bec1961d..3a5b13c6eed99 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -157,6 +157,7 @@ public class StatusBarPolicy { private IconData mBluetoothData; private int mBluetoothHeadsetState; private int mBluetoothA2dpState; + private boolean mBluetoothEnabled; // wifi private static final int[] sWifiSignalImages = new int[] { @@ -286,7 +287,16 @@ public class StatusBarPolicy { mBluetoothData = IconData.makeIcon("bluetooth", null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0); mBluetoothIcon = service.addIcon(mBluetoothData, null); - updateBluetooth(null); + BluetoothDevice bluetooth = + (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE); + if (bluetooth != null) { + mBluetoothEnabled = bluetooth.isEnabled(); + } else { + mBluetoothEnabled = false; + } + mBluetoothA2dpState = BluetoothA2dp.STATE_DISCONNECTED; + mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED; + mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled); // Gps status mGpsEnabledIconData = IconData.makeIcon("gps", @@ -767,30 +777,17 @@ public class StatusBarPolicy { } private final void updateBluetooth(Intent intent) { - boolean visible = false; - if (intent == null) { // Initialize - BluetoothDevice bluetooth = - (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE); - if (bluetooth != null) { - visible = bluetooth.isEnabled(); - } - mService.setIconVisibility(mBluetoothIcon, visible); - return; - } - int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth; - String action = intent.getAction(); + String action = intent.getAction(); if (action.equals(BluetoothIntent.DISABLED_ACTION)) { - visible = false; + mBluetoothEnabled = false; } else if (action.equals(BluetoothIntent.ENABLED_ACTION)) { - visible = true; + mBluetoothEnabled = true; } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) { - visible = true; mBluetoothHeadsetState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE, BluetoothHeadset.STATE_ERROR); } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) { - visible = true; mBluetoothA2dpState = intent.getIntExtra(BluetoothA2dp.SINK_STATE, BluetoothA2dp.STATE_DISCONNECTED); } else { @@ -805,7 +802,7 @@ public class StatusBarPolicy { mBluetoothData.iconId = iconId; mService.updateIcon(mBluetoothIcon, mBluetoothData, null); - mService.setIconVisibility(mBluetoothIcon, visible); + mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled); } private final void updateWifi(Intent intent) { diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 3b6e354b6b48a..a4844b188ea64 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -322,9 +322,11 @@ public class StatusBarService extends IStatusBar.Stub } public void systemReady() { + final StatusBarView view = mStatusBarView; WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, - 25, + view.getContext().getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height), WindowManager.LayoutParams.TYPE_STATUS_BAR, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING, @@ -333,7 +335,7 @@ public class StatusBarService extends IStatusBar.Stub lp.setTitle("StatusBar"); lp.windowAnimations = R.style.Animation_StatusBar; - WindowManagerImpl.getDefault().addView(mStatusBarView, lp); + WindowManagerImpl.getDefault().addView(view, lp); } // ================================================================================ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 374a7032cdd24..c5b1b73733594 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -252,10 +252,7 @@ public class TelephonyManager { */ public List getNeighboringCellInfo() { try { - ITelephony tel = getITelephony(); - if (tel != null) { - return tel.getNeighboringCellInfo(); - } + return getITelephony().getNeighboringCellInfo(); } catch (RemoteException ex) { } return null; @@ -683,7 +680,8 @@ public class TelephonyManager { public void listen(PhoneStateListener listener, int events) { String pkgForDebug = mContext != null ? mContext.getPackageName() : ""; try { - mRegistry.listen(pkgForDebug, listener.callback, events, true); + Boolean notifyNow = (getITelephony() != null); + mRegistry.listen(pkgForDebug, listener.callback, events, notifyNow); } catch (RemoteException ex) { // system process dead } diff --git a/telephony/java/com/android/internal/telephony/gsm/CallTracker.java b/telephony/java/com/android/internal/telephony/gsm/CallTracker.java index afd11c47094d1..2d716bb54b538 100644 --- a/telephony/java/com/android/internal/telephony/gsm/CallTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/CallTracker.java @@ -51,10 +51,7 @@ public final class CallTracker extends Handler private static final boolean REPEAT_POLLING = false; private static final boolean DBG_POLL = false; - - // Event Log Tags - private static final int EVENT_LOG_CALL_DROP = 50106; - + //***** Constants static final int POLL_DELAY_MSEC = 250; @@ -62,17 +59,17 @@ public final class CallTracker extends Handler static final int MAX_CONNECTIONS_PER_CALL = 5; // only 5 connections allowed per call //***** Instance Variables - + GSMConnection connections[] = new GSMConnection[MAX_CONNECTIONS]; RegistrantList voiceCallEndedRegistrants = new RegistrantList(); RegistrantList voiceCallStartedRegistrants = new RegistrantList(); // connections dropped durin last poll - ArrayList droppedDuringPoll - = new ArrayList(MAX_CONNECTIONS); + ArrayList droppedDuringPoll + = new ArrayList(MAX_CONNECTIONS); - GSMCall ringingCall = new GSMCall(this); + GSMCall ringingCall = new GSMCall(this); // A call that is ringing or (call) waiting GSMCall foregroundCall = new GSMCall(this); GSMCall backgroundCall = new GSMCall(this); @@ -172,13 +169,13 @@ public final class CallTracker extends Handler // for the newly dialed connection switchWaitingOrHoldingAndActive(); - // Fake local state so that + // Fake local state so that // a) foregroundCall is empty for the newly dialed connection // b) hasNonHangupStateChanged remains false in the // next poll, so that we don't clear a failed dialing call fakeHoldForegroundBeforeDial(); - } - + } + if (foregroundCall.getState() != Call.State.IDLE) { //we should have failed in !canDial() above before we get here throw new CallStateException("cannot dial in current state"); @@ -194,22 +191,22 @@ public final class CallTracker extends Handler pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER; // handlePollCalls() will notice this call not present - // and will mark it as dropped. + // and will mark it as dropped. pollCallsWhenSafe(); } else { // Always unmute when initiating a new call setMute(false); - cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); + cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); } updatePhoneState(); phone.notifyCallStateChanged(); - + return pendingMO; } - + Connection dial (String dialString) throws CallStateException { @@ -222,7 +219,7 @@ public final class CallTracker extends Handler // FIXME if SWITCH fails, should retry with ANSWER // in case the active/holding call disappeared and this // is no longer call waiting - + if (ringingCall.getState() == Call.State.INCOMING) { Log.i("phone", "acceptCall: incoming..."); // Always unmute when answering a new call @@ -270,7 +267,7 @@ public final class CallTracker extends Handler { cm.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT)); } - + void clearDisconnected() { @@ -280,7 +277,7 @@ public final class CallTracker extends Handler phone.notifyCallStateChanged(); } - boolean + boolean canConference() { return foregroundCall.getState() == Call.State.ACTIVE @@ -312,13 +309,13 @@ public final class CallTracker extends Handler } //***** Private Instance Methods - + private void internalClearDisconnected() { ringingCall.clearDisconnected(); foregroundCall.clearDisconnected(); - backgroundCall.clearDisconnected(); + backgroundCall.clearDisconnected(); } /** @@ -364,7 +361,7 @@ public final class CallTracker extends Handler /** * Obtain a complete message that indicates that this operation * does not require polling of getCurrentCalls(). However, if other - * operations that do need getCurrentCalls() are pending or are + * operations that do need getCurrentCalls() are pending or are * scheduled while this operation is pending, the invocatoin * of getCurrentCalls() will be postponed until this * operation is also complete. @@ -382,20 +379,20 @@ public final class CallTracker extends Handler operationComplete() { pendingOperations--; - + if (DBG_POLL) log("operationComplete: pendingOperations=" + pendingOperations + ", needsPoll=" + needsPoll); if (pendingOperations == 0 && needsPoll) { lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); - cm.getCurrentCalls(lastRelevantPoll); + cm.getCurrentCalls(lastRelevantPoll); } else if (pendingOperations < 0) { // this should never happen Log.e(LOG_TAG,"CallTracker.pendingOperations < 0"); pendingOperations = 0; } } - + private void pollCallsWhenSafe() { @@ -403,10 +400,10 @@ public final class CallTracker extends Handler if (checkNoOperationsPending()) { lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); - cm.getCurrentCalls(lastRelevantPoll); + cm.getCurrentCalls(lastRelevantPoll); } } - + private void pollCallsAfterDelay() { @@ -428,7 +425,7 @@ public final class CallTracker extends Handler updatePhoneState() { Phone.State oldState = state; - + if (ringingCall.isRinging()) { state = Phone.State.RINGING; } else if (pendingMO != null || @@ -436,7 +433,7 @@ public final class CallTracker extends Handler state = Phone.State.OFFHOOK; } else { state = Phone.State.IDLE; - } + } if (state == Phone.State.IDLE && oldState != state) { voiceCallEndedRegistrants.notifyRegistrants( @@ -475,7 +472,7 @@ public final class CallTracker extends Handler boolean needsPollDelay = false; boolean unknownConnectionAppeared = false; - for (int i = 0, curDC = 0, dcSize = polledCalls.size() + for (int i = 0, curDC = 0, dcSize = polledCalls.size() ; i < connections.length; i++) { GSMConnection conn = connections[i]; DriverCall dc = null; @@ -532,7 +529,7 @@ public final class CallTracker extends Handler // which is neither a ringing call or one we created. // Either we've crashed and re-attached to an existing // call, or something else (eg, SIM) initiated the call. - + Log.i(LOG_TAG,"Phantom call appeared " + dc); // If it's a connected call, set the connect time so that @@ -549,8 +546,8 @@ public final class CallTracker extends Handler hasNonHangupStateChanged = true; } else if (conn != null && dc == null) { // Connection missing in CLCC response that we were - // tracking. - droppedDuringPoll.add(conn); + // tracking. + droppedDuringPoll.add(conn); // Dropped connections are removed from the CallTracker // list but kept in the GSMCall list connections[i] = null; @@ -558,7 +555,7 @@ public final class CallTracker extends Handler // Connection in CLCC response does not match what // we were tracking. Assume dropped call and new call - droppedDuringPoll.add(conn); + droppedDuringPoll.add(conn); connections[i] = new GSMConnection (phone.getContext(), dc, this, i); if (connections[i].getCall() == ringingCall) { @@ -594,11 +591,11 @@ public final class CallTracker extends Handler // This is the first poll after an ATD. // We expect the pending call to appear in the list // If it does not, we land here - if (pendingMO != null) { - Log.d(LOG_TAG,"Pending MO dropped before poll fg state:" + if (pendingMO != null) { + Log.d(LOG_TAG,"Pending MO dropped before poll fg state:" + foregroundCall.getState()); - droppedDuringPoll.add(pendingMO); + droppedDuringPoll.add(pendingMO); pendingMO = null; hangupPendingMO = false; } @@ -619,7 +616,7 @@ public final class CallTracker extends Handler if (conn.cause == Connection.DisconnectCause.LOCAL) { cause = Connection.DisconnectCause.INCOMING_REJECTED; } else { - cause = Connection.DisconnectCause.INCOMING_MISSED; + cause = Connection.DisconnectCause.INCOMING_MISSED; } if (Phone.DEBUG_PHONE) { @@ -690,7 +687,7 @@ public final class CallTracker extends Handler dumpState() { List l; - + Log.i(LOG_TAG,"Phone State:" + state); Log.i(LOG_TAG,"Ringing call: " + ringingCall.toString()); @@ -722,7 +719,7 @@ public final class CallTracker extends Handler hangup (GSMConnection conn) throws CallStateException { if (conn.owner != this) { - throw new CallStateException ("Connection " + conn + throw new CallStateException ("Connection " + conn + "does not belong to CallTracker " + this); } @@ -731,14 +728,14 @@ public final class CallTracker extends Handler // GSM index assigned yet if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); - hangupPendingMO = true; + hangupPendingMO = true; } else { - try { + try { cm.hangupConnection (conn.getGSMIndex(), obtainCompleteMessage()); } catch (CallStateException ex) { // Ignore "connection not found" // Call may have hung up already - Log.w(LOG_TAG,"CallTracker WARN: hangup() on absent connection " + Log.w(LOG_TAG,"CallTracker WARN: hangup() on absent connection " + conn); } } @@ -750,16 +747,16 @@ public final class CallTracker extends Handler separate (GSMConnection conn) throws CallStateException { if (conn.owner != this) { - throw new CallStateException ("Connection " + conn + throw new CallStateException ("Connection " + conn + "does not belong to CallTracker " + this); } try { - cm.separateConnection (conn.getGSMIndex(), + cm.separateConnection (conn.getGSMIndex(), obtainCompleteMessage(EVENT_SEPARATE_RESULT)); } catch (CallStateException ex) { // Ignore "connection not found" // Call may have hung up already - Log.w(LOG_TAG,"CallTracker WARN: separate() on absent connection " + Log.w(LOG_TAG,"CallTracker WARN: separate() on absent connection " + conn); } } @@ -772,14 +769,14 @@ public final class CallTracker extends Handler desiredMute = mute; cm.setMute(desiredMute, null); } - + /*package*/ boolean getMute() { return desiredMute; } - + //***** Called from GSMCall /* package */ void @@ -886,11 +883,11 @@ public final class CallTracker extends Handler //****** Overridden from Handler - public void + public void handleMessage (Message msg) { AsyncResult ar; - + switch (msg.what) { case EVENT_POLL_CALLS_RESULT: ar = (AsyncResult)msg.obj; @@ -924,7 +921,7 @@ public final class CallTracker extends Handler int causeCode; ar = (AsyncResult)msg.obj; - operationComplete(); + operationComplete(); if (ar.exception != null) { // An exception occurred...just treat the disconnect @@ -936,7 +933,7 @@ public final class CallTracker extends Handler causeCode = ((int[])ar.result)[0]; } // Log the causeCode if its not normal - if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || + if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || causeCode == CallFailCause.TEMPORARY_FAILURE || causeCode == CallFailCause.SWITCHING_CONGESTION || causeCode == CallFailCause.CHANNEL_NOT_AVAIL || @@ -946,12 +943,12 @@ public final class CallTracker extends Handler int cid = -1; GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); if (loc != null) cid = loc.getCid(); - - EventLog.List val = new EventLog.List(causeCode, cid, + + EventLog.List val = new EventLog.List(causeCode, cid, TelephonyManager.getDefault().getNetworkType()); - EventLog.writeEvent(EVENT_LOG_CALL_DROP, val); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CALL_DROP, val); } - + for (int i = 0, s = droppedDuringPoll.size() ; i < s ; i++ ) { diff --git a/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java index b0b8cdca42148..b115713a9e6df 100644 --- a/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java @@ -16,11 +16,6 @@ package com.android.internal.telephony.gsm; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; -import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_EDGE; -import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_GPRS; -import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_UMTS; -import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_UNKNOWN; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -31,6 +26,8 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.ContentObserver; import android.database.Cursor; +import android.net.NetworkInfo; +import android.net.wifi.WifiManager; import android.os.AsyncResult; import android.os.Handler; import android.os.INetStatService; @@ -50,13 +47,10 @@ import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; -import android.util.EventLog.List; import com.android.internal.telephony.Phone; import com.android.internal.telephony.gsm.PdpConnection.PdpFailCause; -import android.net.wifi.WifiManager; -import android.net.NetworkInfo; - + import java.io.IOException; import java.util.ArrayList; @@ -128,7 +122,8 @@ final class DataConnectionTracker extends Handler Handler mDataConnectionTracker = null; private ContentResolver mResolver; - int txPkts, rxPkts, sentSinceLastRecv, netStatPollPeriod; + long txPkts, rxPkts, sentSinceLastRecv; + int netStatPollPeriod; private int mNoRecvPollCount = 0; private boolean mPingTestActive = false; // Count of PDP reset attempts; reset when we see incoming, @@ -161,7 +156,7 @@ final class DataConnectionTracker extends Handler /** CID of active PDP */ int cidActive; - + /** Currently requested APN type */ private String mRequestedApnType = Phone.APN_TYPE_DEFAULT; @@ -232,13 +227,6 @@ final class DataConnectionTracker extends Handler private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; - //***** Tag IDs for EventLog - private static final int EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED = 50101; - private static final int EVENT_LOG_RADIO_RESET = 50102; - private static final int EVENT_LOG_PDP_RESET = 50103; - private static final int EVENT_LOG_REREGISTER_NETWORK = 50104; - private static final int EVENT_LOG_RADIO_PDP_SETUP_FAIL = 50105; - //***** Event Codes static final int EVENT_DATA_SETUP_COMPLETE = 1; static final int EVENT_RADIO_AVAILABLE = 3; @@ -500,7 +488,7 @@ final class DataConnectionTracker extends Handler } return true; } - + private boolean isApnTypeActive(String type) { // TODO: to support simultaneous, mActiveApn can be a List instead. return mActiveApn != null && mActiveApn.canHandleType(type); @@ -537,7 +525,7 @@ final class DataConnectionTracker extends Handler Log.d(LOG_TAG, "dataEnabled[DEFAULT_APN]=" + dataEnabled[APN_DEFAULT_ID] + " dataEnabled[MMS_APN]=" + dataEnabled[APN_MMS_ID]); } - + /** * Prevent mobile data connections from being established, * or once again allow mobile data connections. If the state @@ -605,7 +593,7 @@ final class DataConnectionTracker extends Handler //Retrieve the data roaming setting from the shared preferences. public boolean getDataOnRoamingEnabled() { try { - return Settings.Secure.getInt(phone.getContext().getContentResolver(), + return Settings.Secure.getInt(phone.getContext().getContentResolver(), Settings.Secure.DATA_ROAMING) > 0; } catch (SettingNotFoundException snfe) { return false; @@ -636,7 +624,7 @@ final class DataConnectionTracker extends Handler stopNetStatPoll(); phone.notifyDataConnection(Phone.REASON_GPRS_DETACHED); } - + private void onGprsAttached() { if (state == State.CONNECTED) { startNetStatPoll(); @@ -746,7 +734,7 @@ final class DataConnectionTracker extends Handler * IDLE before the code below runs. If we didn't check * for that, future calls to trySetupData would fail, * and we would never get out of the DISCONNECTING state. - */ + */ if (!tearDown) { setState(State.IDLE); phone.notifyDataConnection(reason); @@ -932,6 +920,16 @@ final class DataConnectionTracker extends Handler Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting"); + // Add an event log when the network drops PDP + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List(cid, + TelephonyManager.getDefault().getNetworkType()); + + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); + cleanUpConnection(true, null); return; @@ -964,6 +962,16 @@ final class DataConnectionTracker extends Handler Log.i(LOG_TAG, "PDP connection has dropped (active=false case). " + " Reconnecting"); + // Log the network drop on the event log. + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List(cid, + TelephonyManager.getDefault().getNetworkType()); + + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); + cleanUpConnection(true, null); } } @@ -1040,14 +1048,14 @@ final class DataConnectionTracker extends Handler if (state == State.CONNECTED) { int maxPdpReset = Settings.Gservices.getInt(mResolver, Settings.Gservices.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, - DEFAULT_MAX_PDP_RESET_FAIL); + DEFAULT_MAX_PDP_RESET_FAIL); if (mPdpResetCount < maxPdpReset) { mPdpResetCount++; - EventLog.writeEvent(EVENT_LOG_PDP_RESET, sentSinceLastRecv); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, sentSinceLastRecv); cleanUpConnection(true, Phone.REASON_PDP_RESET); } else { mPdpResetCount = 0; - EventLog.writeEvent(EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv); phone.mSST.reRegisterNetwork(null); } // TODO: Add increasingly drastic recovery steps, eg, @@ -1096,8 +1104,8 @@ final class DataConnectionTracker extends Handler { public void run() { - int sent, received; - int preTxPkts = -1, preRxPkts = -1; + long sent, received; + long preTxPkts = -1, preRxPkts = -1; Activity newActivity; @@ -1105,8 +1113,8 @@ final class DataConnectionTracker extends Handler preRxPkts = rxPkts; try { - txPkts = netstat.getTxPackets(); - rxPkts = netstat.getRxPackets(); + txPkts = netstat.getMobileTxPackets(); + rxPkts = netstat.getMobileRxPackets(); } catch (RemoteException e) { txPkts = 0; rxPkts = 0; @@ -1152,7 +1160,7 @@ final class DataConnectionTracker extends Handler if (sentSinceLastRecv >= watchdogTrigger) { // we already have NUMBER_SENT_PACKETS sent without ack if (mNoRecvPollCount == 0) { - EventLog.writeEvent(EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED, + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED, sentSinceLastRecv); } @@ -1220,11 +1228,11 @@ final class DataConnectionTracker extends Handler } catch (Exception e) { Log.w(LOG_TAG, "exception trying to ping"); } - + if (status == 0) { // ping succeeded. False alarm. Reset netStatPoll. // ("-1" for this event indicates a false alarm) - EventLog.writeEvent(EVENT_LOG_PDP_RESET, -1); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, -1); mPdpResetCount = 0; sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL)); } else { @@ -1335,7 +1343,7 @@ final class DataConnectionTracker extends Handler case EVENT_GPRS_ATTACHED: onGprsAttached(); break; - + case EVENT_ROAMING_ON: if (getDataOnRoamingEnabled()) { trySetupData(Phone.REASON_ROAMING_ON); @@ -1410,7 +1418,7 @@ final class DataConnectionTracker extends Handler SystemProperties.set("gsm.defaultpdpcontext.active", "false"); } notifyDefaultData(reason); - + // TODO: For simultaneous PDP support, we need to build another // trigger another TRY_SETUP_DATA for the next APN type. (Note // that the existing connection may service that type, in which @@ -1421,7 +1429,7 @@ final class DataConnectionTracker extends Handler if(DBG) log("PDP setup failed " + cause); // Log this failure to the Event Logs. - if (cause == PdpConnection.PdpFailCause.BAD_APN || + if (cause == PdpConnection.PdpFailCause.BAD_APN || cause == PdpConnection.PdpFailCause.BAD_PAP_SECRET || cause == PdpConnection.PdpFailCause.BARRED || cause == PdpConnection.PdpFailCause.RADIO_ERROR_RETRY || @@ -1431,12 +1439,12 @@ final class DataConnectionTracker extends Handler int cid = -1; GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); if (loc != null) cid = loc.getCid(); - + EventLog.List val = new EventLog.List( - cause.ordinal(), cid, - TelephonyManager.getDefault().getNetworkType()); - EventLog.writeEvent(EVENT_LOG_RADIO_PDP_SETUP_FAIL, val); - } + cause.ordinal(), cid, + TelephonyManager.getDefault().getNetworkType()); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_PDP_SETUP_FAIL, val); + } // No try for permanent failure if (cause.isPermanentFail()) { notifyNoData(cause); @@ -1575,7 +1583,7 @@ final class DataConnectionTracker extends Handler if (cursor != null) { if (cursor.getCount() > 0) { allApns = createApnList(cursor); - // TODO: Figure out where this fits in. This basically just + // TODO: Figure out where this fits in. This basically just // writes the pap-secrets file. No longer tied to PdpConnection // object. Not used on current platform (no ppp). //PdpConnection pdp = pdpList.get(pdp_name); @@ -1622,7 +1630,7 @@ final class DataConnectionTracker extends Handler } /** - * Get next apn in waitingApns + * Get next apn in waitingApns * @return the first apn found in waitingApns, null if none */ private ApnSetting getNextApn() { diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java b/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java index 43930c10d5347..a50376671837e 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java @@ -77,15 +77,15 @@ public class GSMConnection extends Connection { private PowerManager.WakeLock mPartialWakeLock; //***** Event Constants - static final int EVENT_DTMF_DONE = 1; static final int EVENT_PAUSE_DONE = 2; static final int EVENT_NEXT_POST_DIAL = 3; - + static final int EVENT_WAKE_LOCK_TIMEOUT = 4; + //***** Constants - static final int PAUSE_DELAY_FIRST_MILLIS = 100; static final int PAUSE_DELAY_MILLIS = 3 * 1000; + static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000; //***** Inner Classes @@ -100,8 +100,10 @@ public class GSMConnection extends Connection { case EVENT_DTMF_DONE: case EVENT_PAUSE_DONE: processNextPostDialChar(); - break; - + break; + case EVENT_WAKE_LOCK_TIMEOUT: + releaseWakeLock(); + break; } } } @@ -279,7 +281,7 @@ public class GSMConnection extends Connection { return; } - postDialState = PostDialState.STARTED; + setPostDialState(PostDialState.STARTED); processNextPostDialChar(); } @@ -291,7 +293,7 @@ public class GSMConnection extends Connection { return; } - postDialState = PostDialState.STARTED; + setPostDialState(PostDialState.STARTED); if (false) { boolean playedTone = false; @@ -333,7 +335,7 @@ public class GSMConnection extends Connection { public void cancelPostDial() { - postDialState = PostDialState.CANCELLED; + setPostDialState(PostDialState.CANCELLED); } /** @@ -562,9 +564,9 @@ public class GSMConnection extends Connection { PAUSE_DELAY_MILLIS); } } else if (c == PhoneNumberUtils.WAIT) { - postDialState = PostDialState.WAIT; + setPostDialState(PostDialState.WAIT); } else if (c == PhoneNumberUtils.WILD) { - postDialState = PostDialState.WILD; + setPostDialState(PostDialState.WILD); } else { return false; } @@ -614,14 +616,14 @@ public class GSMConnection extends Connection { if (postDialString == null || postDialString.length() <= nextPostDialChar) { - postDialState = PostDialState.COMPLETE; + setPostDialState(PostDialState.COMPLETE); // notifyMessage.arg1 is 0 on complete c = 0; } else { boolean isValid; - postDialState = PostDialState.STARTED; + setPostDialState(PostDialState.STARTED); c = postDialString.charAt(nextPostDialChar++); @@ -699,6 +701,26 @@ public class GSMConnection extends Connection { } } + /** + * Set post dial state and acquire wake lock while switching to "started" + * state, the wake lock will be released if state switches out of "started" + * state or after WAKE_LOCK_TIMEOUT_MILLIS. + * @param s new PostDialState + */ + private void setPostDialState(PostDialState s) { + if (postDialState != PostDialState.STARTED + && s == PostDialState.STARTED) { + acquireWakeLock(); + Message msg = h.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); + h.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS); + } else if (postDialState == PostDialState.STARTED + && s != PostDialState.STARTED) { + h.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); + releaseWakeLock(); + } + postDialState = s; + } + private void createWakeLock(Context context) { PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index f314944db02e8..bd6d41e47f8c6 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -108,6 +108,8 @@ public class GSMPhone extends PhoneBase { SimPhoneBookInterfaceManager mSimPhoneBookIntManager; SimSmsInterfaceManager mSimSmsIntManager; PhoneSubInfo mSubInfo; + boolean mDnsCheckDisabled = false; + Registrant mPostDialHandler; @@ -1151,6 +1153,22 @@ public class GSMPhone extends PhoneBase { return mDataConnection.getAllPdps(); } + /** + * Disables the DNS check (i.e., allows "0.0.0.0"). + * Useful for lab testing environment. + * @param b true disables the check, false enables. + */ + public void disableDnsCheck(boolean b) { + mDnsCheckDisabled = b; + } + + /** + * Returns true if the DNS check is currently disabled. + */ + public boolean isDnsCheckDisabled() { + return mDnsCheckDisabled; + } + public void updateServiceLocation(Message response) { mSST.getLacAndCid(response); } diff --git a/telephony/java/com/android/internal/telephony/gsm/MccTable.java b/telephony/java/com/android/internal/telephony/gsm/MccTable.java index 8f6b22edc3486..57a54c4e4d6e6 100644 --- a/telephony/java/com/android/internal/telephony/gsm/MccTable.java +++ b/telephony/java/com/android/internal/telephony/gsm/MccTable.java @@ -283,8 +283,8 @@ public final class MccTable table.add(new MccEntry(436,"tj",2)); //Tajikistan (Republic of) table.add(new MccEntry(437,"kg",2)); //Kyrgyz Republic table.add(new MccEntry(438,"tm",2)); //Turkmenistan - table.add(new MccEntry(440,"jp",2)); //Japan - table.add(new MccEntry(441,"jp",2)); //Japan + table.add(new MccEntry(440,"jp",2,"Asia/Tokyo","ja")); //Japan + table.add(new MccEntry(441,"jp",2,"Asia/Tokyo","ja")); //Japan table.add(new MccEntry(450,"kr",2)); //Korea (Republic of) table.add(new MccEntry(452,"vn",2)); //Viet Nam (Socialist Republic of) table.add(new MccEntry(454,"hk",2)); //"Hong Kong, China" diff --git a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java index 6428f7047c6fa..66f8b72ae128a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java @@ -114,9 +114,6 @@ public class PdpConnection extends Handler { private static final int EVENT_DEACTIVATE_DONE = 4; private static final int EVENT_FORCE_RETRY = 5; - //***** Tag IDs for EventLog - private static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100; - //***** Instance Variables private GSMPhone phone; private String pdp_name; @@ -301,7 +298,7 @@ public class PdpConnection extends Handler { private void notifyDisconnect(Message msg) { if (DBG) log("Notify PDP disconnect"); - + if (msg != null) { AsyncResult.forMessage(msg); msg.sendToTarget(); @@ -378,7 +375,7 @@ public class PdpConnection extends Handler { if (ar.exception != null) { Log.e(LOG_TAG, "PDP Context Init failed " + ar.exception); - + if (receivedDisconnectReq) { // Don't bother reporting the error if there's already a // pending disconnect request, since DataConnectionTracker @@ -406,7 +403,7 @@ public class PdpConnection extends Handler { } else { String[] response = ((String[]) ar.result); cid = Integer.parseInt(response[0]); - + if (response.length > 2) { interfaceName = response[1]; ipAddress = response[2]; @@ -420,7 +417,8 @@ public class PdpConnection extends Handler { + " DNS2=" + dnsServers[1]); } - if (NULL_IP.equals(dnsServers[0]) && NULL_IP.equals(dnsServers[1])) { + if (NULL_IP.equals(dnsServers[0]) && NULL_IP.equals(dnsServers[1]) + && !phone.isDnsCheckDisabled()) { // Work around a race condition where QMI does not fill in DNS: // Deactivate PDP and let DataConnectionTracker retry. // Do not apply the race condition workaround for MMS APN @@ -428,20 +426,21 @@ public class PdpConnection extends Handler { // Otherwise, the default APN will not be restored anymore. if (!apn.types[0].equals(Phone.APN_TYPE_MMS) || !isIpAddress(apn.mmsProxy)) { - EventLog.writeEvent(EVENT_LOG_BAD_DNS_ADDRESS, dnsServers[0]); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_BAD_DNS_ADDRESS, + dnsServers[0]); phone.mCM.deactivateDefaultPDP(cid, obtainMessage(EVENT_FORCE_RETRY)); break; } } } - + if (dataLink != null) { dataLink.connect(); } else { onLinkStateChanged(DataLink.LinkState.LINK_UP); } - + if (DBG) log("PDP setup on cid = " + cid); } } @@ -463,7 +462,7 @@ public class PdpConnection extends Handler { } else { ar = (AsyncResult) msg.obj; PdpFailCause cause = PdpFailCause.UNKNOWN; - + if (ar.exception == null) { int rilFailCause = ((int[]) (ar.result))[0]; cause = getFailCauseFromRequest(rilFailCause); diff --git a/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java index daf4b9cac9201..0ab4a0e6b2379 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java @@ -135,7 +135,9 @@ final class SMSDispatcher extends Handler { /** Maximum number of times to retry sending a failed SMS. */ private static final int MAX_SEND_RETRIES = 3; /** Delay before next send attempt on a failed SMS, in milliseconds. */ - private static final int SEND_RETRY_DELAY = 2000; // ms + private static final int SEND_RETRY_DELAY = 2000; + /** single part SMS */ + private static final int SINGLE_PART_SMS = 1; /** * Message reference for a CONCATENATED_8_BIT_REFERENCE or @@ -169,15 +171,23 @@ final class SMSDispatcher extends Handler { mSmsStamp = new HashMap> (); } - boolean check(String appName) { + /** + * Check to see if an application allow to send new SMS messages + * + * @param appName is the application sending sms + * @param smsWaiting is the number of new sms wants to be sent + * @return true if application is allowed to send the requested number + * of new sms messages + */ + boolean check(String appName, int smsWaiting) { if (!mSmsStamp.containsKey(appName)) { mSmsStamp.put(appName, new ArrayList()); } - return isUnderLimit(mSmsStamp.get(appName)); + return isUnderLimit(mSmsStamp.get(appName), smsWaiting); } - private boolean isUnderLimit(ArrayList sent) { + private boolean isUnderLimit(ArrayList sent, int smsWaiting) { Long ct = System.currentTimeMillis(); Log.d(TAG, "SMS send size=" + sent.size() + "time=" + ct); @@ -185,9 +195,11 @@ final class SMSDispatcher extends Handler { while (sent.size() > 0 && (ct - sent.get(0)) > mCheckPeriod ) { sent.remove(0); } - - if (sent.size() < mMaxAllowed) { - sent.add(ct); + + if ( (sent.size() + smsWaiting) <= mMaxAllowed) { + for (int i = 0; i < smsWaiting; i++ ) { + sent.add(ct); + } return true; } return false; @@ -290,8 +302,11 @@ final class SMSDispatcher extends Handler { case EVENT_SEND_CONFIRMED_SMS: if (mSTracker!=null) { - Log.d(TAG, "Ready to send SMS again."); - sendSms(mSTracker); + if (isMultipartTracker(mSTracker)) { + sendMultipartSms(mSTracker); + } else { + sendSms(mSTracker); + } mSTracker = null; } break; @@ -786,6 +801,81 @@ final class SMSDispatcher extends Handler { void sendMultipartText(String destinationAddress, String scAddress, ArrayList parts, ArrayList sentIntents, ArrayList deliveryIntents) { + PendingIntent sentIntent = null; + + + int ss = mPhone.getServiceState().getState(); + + if (ss == ServiceState.STATE_IN_SERVICE) { + // Only check SMS sending limit while in service + if (sentIntents != null && sentIntents.size() > 0) { + sentIntent = sentIntents.get(0); + } + String appName = getAppNameByIntent(sentIntent); + if ( !mCounter.check(appName, parts.size())) { + HashMap map = new HashMap(); + map.put("destination", destinationAddress); + map.put("scaddress", scAddress); + map.put("parts", parts); + map.put("sentIntents", sentIntents); + map.put("deliveryIntents", deliveryIntents); + + SmsTracker multipartParameter = new SmsTracker(map, null, null); + + sendMessage(obtainMessage(EVENT_POST_ALERT, multipartParameter)); + return; + } + } + + sendMultipartTextWithPermit(destinationAddress, + scAddress, parts, sentIntents, deliveryIntents); + } + + /** + * Send a multi-part text based SMS which already passed SMS control check. + * + * It is the working function for sendMultipartText(). + * + * @param destinationAddress the address to send the message to + * @param scAddress is the service center address or null to use + * the current default SMSC + * @param parts an ArrayList of strings that, in order, + * comprise the original message + * @param sentIntents if not null, an ArrayList of + * PendingIntents (one for each message part) that is + * broadcast when the corresponding message part has been sent. + * The result code will be Activity.RESULT_OK for success, + * or one of these errors: + * RESULT_ERROR_GENERIC_FAILURE + * RESULT_ERROR_RADIO_OFF + * RESULT_ERROR_NULL_PDU. + * @param deliveryIntents if not null, an ArrayList of + * PendingIntents (one for each message part) that is + * broadcast when the corresponding message part has been delivered + * to the recipient. The raw pdu of the status report is in the + * extended data ("pdu"). + */ + private void sendMultipartTextWithPermit(String destinationAddress, + String scAddress, ArrayList parts, + ArrayList sentIntents, + ArrayList deliveryIntents) { + + PendingIntent sentIntent = null; + PendingIntent deliveryIntent = null; + + // check if in service + int ss = mPhone.getServiceState().getState(); + if (ss != ServiceState.STATE_IN_SERVICE) { + for (int i = 0, count = parts.size(); i < count; i++) { + if (sentIntents != null && sentIntents.size() > i) { + sentIntent = sentIntents.get(i); + } + SmsTracker tracker = new SmsTracker(null, sentIntent, null); + handleNotInService(ss, tracker); + } + return; + } + int ref = ++sConcatenatedRef & 0xff; for (int i = 0, count = parts.size(); i < count; i++) { @@ -796,9 +886,7 @@ final class SMSDispatcher extends Handler { data[2] = (byte) (i + 1); // 1-based sequence SmsHeader header = new SmsHeader(); header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data)); - PendingIntent sentIntent = null; - PendingIntent deliveryIntent = null; - + if (sentIntents != null && sentIntents.size() > i) { sentIntent = sentIntents.get(i); } @@ -809,10 +897,16 @@ final class SMSDispatcher extends Handler { SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, parts.get(i), deliveryIntent != null, header.toByteArray()); - sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); - } - } + HashMap map = new HashMap(); + map.put("smsc", pdus.encodedScAddress); + map.put("pdu", pdus.encodedMessage); + SmsTracker tracker = new SmsTracker(map, sentIntent, + deliveryIntent); + sendSms(tracker); + } + } + /** * Send a SMS * @@ -856,7 +950,7 @@ final class SMSDispatcher extends Handler { handleNotInService(ss, tracker); } else { String appName = getAppNameByIntent(sentIntent); - if (mCounter.check(appName)) { + if (mCounter.check(appName, SINGLE_PART_SMS)) { sendSms(tracker); } else { sendMessage(obtainMessage(EVENT_POST_ALERT, tracker)); @@ -912,6 +1006,41 @@ final class SMSDispatcher extends Handler { SimUtils.bytesToHexString(pdu), reply); } + /** + * Send the multi-part SMS based on multipart Sms tracker + * + * @param tracker holds the multipart Sms tracker ready to be sent + */ + private void sendMultipartSms (SmsTracker tracker) { + ArrayList parts; + ArrayList sentIntents; + ArrayList deliveryIntents; + + HashMap map = tracker.mData; + + String destinationAddress = (String) map.get("destination"); + String scAddress = (String) map.get("scaddress"); + + parts = (ArrayList) map.get("parts"); + sentIntents = (ArrayList) map.get("sentIntents"); + deliveryIntents = (ArrayList) map.get("deliveryIntents"); + + sendMultipartTextWithPermit(destinationAddress, + scAddress, parts, sentIntents, deliveryIntents); + + } + + /** + * Check if a SmsTracker holds multi-part Sms + * + * @param tracker a SmsTracker could hold a multi-part Sms + * @return true for tracker holds Multi-parts Sms + */ + private boolean isMultipartTracker (SmsTracker tracker) { + HashMap map = tracker.mData; + return ( map.get("parts") != null); + } + /** * Keeps track of an SMS that has been sent to the RIL, until it it has * successfully been sent, or we're done trying. @@ -939,7 +1068,7 @@ final class SMSDispatcher extends Handler { new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - if (which == DialogInterface.BUTTON1) { + if (which == DialogInterface.BUTTON_POSITIVE) { Log.d(TAG, "click YES to send out sms"); sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS)); } diff --git a/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java index d3a19c5837084..10baa1bdda54c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java @@ -122,7 +122,7 @@ final class ServiceStateTracker extends Handler private boolean mZoneDst; private long mZoneTime; private boolean mGotCountryCode = false; - + String mSavedTimeZone; long mSavedTime; long mSavedAtTime; @@ -154,7 +154,6 @@ final class ServiceStateTracker extends Handler static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; //***** Events - static final int EVENT_RADIO_STATE_CHANGED = 1; static final int EVENT_NETWORK_STATE_CHANGED = 2; static final int EVENT_GET_SIGNAL_STRENGTH = 3; @@ -175,10 +174,6 @@ final class ServiceStateTracker extends Handler static final int EVENT_RESET_PREFERRED_NETWORK_TYPE = 21; static final int EVENT_CHECK_REPORT_GPRS = 22; - // Event Log Tags - private static final int EVENT_LOG_CGREG_FAIL = 50107; - private static final int EVENT_DATA_STATE_RADIO_OFF = 50108; - //***** Time Zones private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; @@ -209,7 +204,7 @@ final class ServiceStateTracker extends Handler "tg", // Togo "uk", // U.K }; - + private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { @@ -230,7 +225,7 @@ final class ServiceStateTracker extends Handler cellLoc = new GsmCellLocation(); newCellLoc = new GsmCellLocation(); - cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); + cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null); @@ -238,16 +233,16 @@ final class ServiceStateTracker extends Handler cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); cm.registerForSIMReady(this, EVENT_SIM_READY, null); - + // system setting property AIRPLANE_MODE_ON is set in Settings. int airplaneMode = Settings.System.getInt( phone.getContext().getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0); - mDesiredPowerState = ! (airplaneMode > 0); + mDesiredPowerState = ! (airplaneMode > 0); ContentResolver cr = phone.getContext().getContentResolver(); cr.registerContentObserver( - Settings.System.getUriFor(Settings.System.AUTO_TIME), true, + Settings.System.getUriFor(Settings.System.AUTO_TIME), true, mAutoTimeObserver); setRssiDefaultValues(); mNeedToRegForSimLoaded = true; @@ -272,7 +267,7 @@ final class ServiceStateTracker extends Handler void registerForNetworkAttach(Handler h, int what, Object obj) { Registrant r = new Registrant(h, what, obj); networkAttachedRegistrants.add(r); - + if (ss.getState() == ServiceState.STATE_IN_SERVICE) { r.notifyRegistrant(); } @@ -360,7 +355,7 @@ final class ServiceStateTracker extends Handler /*package*/ void enableLocationUpdates() { cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED)); } - + /*package*/ void disableLocationUpdates() { cm.setLocationUpdates(false, null); } @@ -407,7 +402,7 @@ final class ServiceStateTracker extends Handler pollState(); break; - case EVENT_GET_SIGNAL_STRENGTH: + case EVENT_GET_SIGNAL_STRENGTH: // This callback is called when signal strength is polled // all by itself @@ -418,7 +413,7 @@ final class ServiceStateTracker extends Handler ar = (AsyncResult) msg.obj; onSignalStrengthResult(ar); queueNextSignalStrengthPoll(); - + break; case EVENT_GET_LOC_DONE: @@ -503,7 +498,7 @@ final class ServiceStateTracker extends Handler getLacAndCid(null); } break; - + case EVENT_SET_PREFERRED_NETWORK_TYPE: ar = (AsyncResult) msg.obj; // Don't care the result, only use for dereg network (COPS=2) @@ -548,7 +543,7 @@ final class ServiceStateTracker extends Handler if (loc != null) cid = loc.getCid(); EventLog.List val = new EventLog.List(ss.getOperatorNumeric(), cid); - EventLog.writeEvent(EVENT_LOG_CGREG_FAIL, val); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CGREG_FAIL, val); mReportedGprsNoReg = true; } mStartedGprsRegCheck = false; @@ -598,7 +593,7 @@ final class ServiceStateTracker extends Handler EventLog.List val = new EventLog.List( dcTracker.getStateInString(), (dcTracker.getAnyDataEnabled() ? 1 : 0) ); - EventLog.writeEvent(EVENT_DATA_STATE_RADIO_OFF, val); + EventLog.writeEvent(TelephonyEventLog.EVENT_DATA_STATE_RADIO_OFF, val); } // If it's on and available and we want it off.. cm.setRadioPower(false, null); @@ -689,7 +684,7 @@ final class ServiceStateTracker extends Handler if (states.length > 0) { try { regState = Integer.parseInt(states[0]); - + // states[3] (if present) is the current radio technology if (states.length >= 4 && states[3] != null) { type = Integer.parseInt(states[3]); @@ -1038,7 +1033,7 @@ final class ServiceStateTracker extends Handler break; } } - + return guess; } @@ -1147,7 +1142,7 @@ final class ServiceStateTracker extends Handler String simNumeric = SystemProperties.get(PROPERTY_SIM_OPERATOR_NUMERIC, ""); String operatorNumeric = s.getOperatorNumeric(); - + boolean equalsMcc = true; try { equalsMcc = simNumeric.substring(0, 3). @@ -1329,7 +1324,7 @@ final class ServiceStateTracker extends Handler } saveNitzTimeZone(zone.getID()); } - + String ignore = SystemProperties.get("gsm.ignore-nitz"); if (ignore != null && ignore.equals("yes")) { Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set"); @@ -1339,7 +1334,7 @@ final class ServiceStateTracker extends Handler if (getAutoTime()) { long millisSinceNitzReceived = SystemClock.elapsedRealtime() - nitzReceiveTime; - + if (millisSinceNitzReceived < 0) { // Sanity check: something is wrong Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " @@ -1347,7 +1342,7 @@ final class ServiceStateTracker extends Handler + nitz); return; } - + if (millisSinceNitzReceived > Integer.MAX_VALUE) { // If the time is this far off, something is wrong > 24 days! Log.i(LOG_TAG, "NITZ: not setting time, processing has taken " @@ -1355,10 +1350,10 @@ final class ServiceStateTracker extends Handler + " days"); return; } - + // Note: with range checks above, cast to int is safe c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); - + Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime() + " NITZ receive delay(ms): " + millisSinceNitzReceived + " gained(ms): " @@ -1399,11 +1394,11 @@ final class ServiceStateTracker extends Handler /** * Set the timezone and send out a sticky broadcast so the system can * determine if the timezone was set by the carrier. - * + * * @param zoneId timezone set by carrier */ private void setAndBroadcastNetworkSetTimeZone(String zoneId) { - AlarmManager alarm = + AlarmManager alarm = (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); alarm.setTimeZone(zoneId); Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); @@ -1414,9 +1409,9 @@ final class ServiceStateTracker extends Handler /** * Set the time and Send out a sticky broadcast so the system can determine * if the time was set by the carrier. - * + * * @param time time set by network - */ + */ private void setAndBroadcastNetworkSetTime(long time) { SystemClock.setCurrentTimeMillis(time); Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); diff --git a/telephony/java/com/android/internal/telephony/gsm/TelephonyEventLog.java b/telephony/java/com/android/internal/telephony/gsm/TelephonyEventLog.java new file mode 100644 index 0000000000000..1e583f0b9f9e8 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/TelephonyEventLog.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm; + +/* This class contains the details related to Telephony Event Logging */ +public final class TelephonyEventLog { + + /* Event log tags */ + public static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100; + public static final int EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED = 50101; + public static final int EVENT_LOG_RADIO_RESET = 50102; + public static final int EVENT_LOG_PDP_RESET = 50103; + public static final int EVENT_LOG_REREGISTER_NETWORK = 50104; + public static final int EVENT_LOG_RADIO_PDP_SETUP_FAIL = 50105; + public static final int EVENT_LOG_CALL_DROP = 50106; + public static final int EVENT_LOG_CGREG_FAIL = 50107; + public static final int EVENT_DATA_STATE_RADIO_OFF = 50108; + public static final int EVENT_LOG_PDP_NETWORK_DROP = 50109; +} diff --git a/test-runner/android/test/InstrumentationTestRunner.java b/test-runner/android/test/InstrumentationTestRunner.java index 8b8d47217ecb7..f038612cbd0ed 100644 --- a/test-runner/android/test/InstrumentationTestRunner.java +++ b/test-runner/android/test/InstrumentationTestRunner.java @@ -148,6 +148,8 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu public static final String ARGUMENT_TEST_SIZE_PREDICATE = "size"; /** @hide */ public static final String ARGUMENT_INCLUDE_PERF = "perf"; + /** @hide */ + public static final String ARGUMENT_DELAY_MSEC = "delay_msec"; private static final String SMALL_SUITE = "small"; private static final String MEDIUM_SUITE = "medium"; @@ -249,6 +251,7 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu private String mPackageOfTests; private boolean mCoverage; private String mCoverageFilePath; + private int mDelayMsec; @Override public void onCreate(Bundle arguments) { @@ -277,11 +280,18 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY); mCoverage = getBooleanArgument(arguments, "coverage"); mCoverageFilePath = arguments.getString("coverageFile"); + + try { + Object delay = arguments.get(ARGUMENT_DELAY_MSEC); // Accept either string or int + if (delay != null) mDelayMsec = Integer.parseInt(delay.toString()); + } catch (NumberFormatException e) { + Log.e(LOG_TAG, "Invalid delay_msec parameter", e); + } } - + TestSuiteBuilder testSuiteBuilder = new TestSuiteBuilder(getClass().getName(), getTargetContext().getClassLoader()); - + if (testSizePredicate != null) { testSuiteBuilder.addRequirements(testSizePredicate); } @@ -600,6 +610,18 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu } else { mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, ""); } + + // The delay_msec parameter is normally used to provide buffers of idle time + // for power measurement purposes. To make sure there is a delay before and after + // every test in a suite, we delay *after* every test (see endTest below) and also + // delay *before* the first test. So, delay test1 delay test2 delay. + + try { + if (mTestNum == 1) Thread.sleep(mDelayMsec); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + sendStatus(REPORT_VALUE_RESULT_START, mTestResult); mTestResultCode = 0; } @@ -636,10 +658,15 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "."); } sendStatus(mTestResultCode, mTestResult); + + try { // Sleep after every test, if specified + Thread.sleep(mDelayMsec); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } } // TODO report the end of the cycle // TODO report runtime for each test } } - diff --git a/test-runner/android/test/TouchUtils.java b/test-runner/android/test/TouchUtils.java index c4bca41bd2def..52d2ee85687b7 100644 --- a/test-runner/android/test/TouchUtils.java +++ b/test-runner/android/test/TouchUtils.java @@ -73,7 +73,7 @@ public class TouchUtils { */ @Deprecated public static void dragQuarterScreenUp(ActivityInstrumentationTestCase test) { - dragQuarterScreenDown(test, test.getActivity()); + dragQuarterScreenUp(test, test.getActivity()); } /** @@ -269,9 +269,9 @@ public class TouchUtils { inst.waitForIdleSync(); eventTime = SystemClock.uptimeMillis(); - event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, - x + (ViewConfiguration.getTouchSlop() / 2.0f), - y + (ViewConfiguration.getTouchSlop() / 2.0f), 0); + final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop(); + event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, + x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0); inst.sendPointerSync(event); inst.waitForIdleSync(); @@ -309,9 +309,9 @@ public class TouchUtils { inst.waitForIdleSync(); eventTime = SystemClock.uptimeMillis(); + final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop(); event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_CANCEL, - x + (ViewConfiguration.getTouchSlop() / 2.0f), - y + (ViewConfiguration.getTouchSlop() / 2.0f), 0); + x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0); inst.sendPointerSync(event); inst.waitForIdleSync(); @@ -345,9 +345,9 @@ public class TouchUtils { eventTime = SystemClock.uptimeMillis(); - event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, - x + (ViewConfiguration.getTouchSlop() / 2.0f), - y + (ViewConfiguration.getTouchSlop() / 2.0f), 0); + final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop(); + event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, + x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0); inst.sendPointerSync(event); inst.waitForIdleSync(); @@ -405,9 +405,9 @@ public class TouchUtils { inst.waitForIdleSync(); eventTime = SystemClock.uptimeMillis(); - event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, - x + ViewConfiguration.getTouchSlop() / 2, - y + ViewConfiguration.getTouchSlop() / 2, 0); + final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop(); + event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, + x + touchSlop / 2, y + touchSlop / 2, 0); inst.sendPointerSync(event); inst.waitForIdleSync(); diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java index f131bf26545b7..57e7e4109db5c 100644 --- a/test-runner/android/test/mock/MockPackageManager.java +++ b/test-runner/android/test/mock/MockPackageManager.java @@ -393,4 +393,14 @@ public class MockPackageManager extends PackageManager { List outActivities, String packageName) { throw new UnsupportedOperationException(); } + + @Override + public String[] getSystemSharedLibraryNames() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSafeMode() { + throw new UnsupportedOperationException(); + } } diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml index f126b8cbe60ff..230456a8dd66c 100644 --- a/tests/AndroidTests/AndroidManifest.xml +++ b/tests/AndroidTests/AndroidManifest.xml @@ -48,6 +48,7 @@ + diff --git a/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java b/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java index 6a9ac86a854a0..f9af43659cfba 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java @@ -44,7 +44,7 @@ import android.os.ServiceManager; import android.os.StatFs; public class AppCacheTest extends AndroidTestCase { - private static final boolean localLOGV = true; + private static final boolean localLOGV = false; public static final String TAG="AppCacheTest"; public final long MAX_WAIT_TIME=60*1000; public final long WAIT_TIME_INCR=10*1000; @@ -593,6 +593,20 @@ public class AppCacheTest extends AndroidTestCase { ", cache="+stats.cacheSize); } + @SmallTest + public void testGetSystemSharedLibraryNames() throws Exception { + try { + String[] sharedLibs = getPm().getSystemSharedLibraryNames(); + if (localLOGV) { + for (String str : sharedLibs) { + Log.i(TAG, str); + } + } + } catch (RemoteException e) { + fail("Failed invoking getSystemSharedLibraryNames with exception:" + e); + } + } + class FreeStorageReceiver extends BroadcastReceiver { public static final String ACTION_FREE = "com.android.unit_tests.testcallback"; private boolean doneFlag = false; diff --git a/tests/AndroidTests/src/com/android/unit_tests/BluetoothTest.java b/tests/AndroidTests/src/com/android/unit_tests/BluetoothTest.java index 21fb94b822008..0a60319761e5c 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/BluetoothTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/BluetoothTest.java @@ -336,7 +336,6 @@ public class BluetoothTest extends AndroidTestCase { filter.addAction(BluetoothIntent.ENABLED_ACTION); filter.addAction(BluetoothIntent.DISABLED_ACTION); filter.addAction(BluetoothIntent.NAME_CHANGED_ACTION); - filter.addAction(BluetoothIntent.MODE_CHANGED_ACTION); filter.addAction(BluetoothIntent.DISCOVERY_STARTED_ACTION); filter.addAction(BluetoothIntent.DISCOVERY_COMPLETED_ACTION); filter.addAction(BluetoothIntent.PAIRING_REQUEST_ACTION); @@ -349,8 +348,6 @@ public class BluetoothTest extends AndroidTestCase { filter.addAction(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION); filter.addAction(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); filter.addAction(BluetoothIntent.REMOTE_NAME_FAILED_ACTION); - filter.addAction(BluetoothIntent.REMOTE_ALIAS_CHANGED_ACTION); - filter.addAction(BluetoothIntent.REMOTE_ALIAS_CLEARED_ACTION); filter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION); filter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION); getContext().registerReceiver( @@ -386,16 +383,6 @@ public class BluetoothTest extends AndroidTestCase { msg += " name=" + name; } - String alias = intent.getStringExtra(BluetoothIntent.ALIAS); - if (alias != null) { - msg += " alias=" + alias; - } - - int mode = intent.getIntExtra(BluetoothIntent.MODE, -10); - if (mode != -10) { - msg += " mode=" + mode; - } - int state = intent.getIntExtra(BluetoothIntent.HEADSET_STATE, -10); if (state != -10) { msg += " headset state=" + state; diff --git a/tests/AndroidTests/src/com/android/unit_tests/BuildTest.java b/tests/AndroidTests/src/com/android/unit_tests/BuildTest.java index a8c51f4bef0a8..dbfd0e7ffcff1 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/BuildTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/BuildTest.java @@ -55,6 +55,7 @@ public class BuildTest extends TestCase { @SmallTest public void testBuildFields() throws Exception { assertNotEmpty("ID", Build.ID); + assertNotEmpty("DISPLAY", Build.DISPLAY); assertNotEmpty("PRODUCT", Build.PRODUCT); assertNotEmpty("DEVICE", Build.DEVICE); assertNotEmpty("BOARD", Build.BOARD); diff --git a/tests/AndroidTests/src/com/android/unit_tests/CheckinProviderTest.java b/tests/AndroidTests/src/com/android/unit_tests/CheckinProviderTest.java deleted file mode 100644 index f9a2ec0f08a6d..0000000000000 --- a/tests/AndroidTests/src/com/android/unit_tests/CheckinProviderTest.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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 com.android.unit_tests; - -import org.apache.commons.codec.binary.Base64; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.ContentUris; -import android.database.Cursor; -import android.net.Uri; -import android.provider.Checkin; -import android.server.checkin.CheckinProvider; -import android.server.data.BuildData; -import android.server.data.CrashData; -import android.server.data.ThrowableData; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; - -import java.io.DataInputStream; -import java.io.ByteArrayInputStream; - -/** Unit test for {@link CheckinProvider}. */ -public class CheckinProviderTest extends AndroidTestCase { - @MediumTest - public void testEventReport() { - long start = System.currentTimeMillis(); - ContentResolver r = getContext().getContentResolver(); - Checkin.logEvent(r, Checkin.Events.Tag.TEST, "Test Value"); - - Cursor c = r.query(Checkin.Events.CONTENT_URI, - null, - Checkin.Events.TAG + "=?", - new String[] { Checkin.Events.Tag.TEST.toString() }, - null); - - long id = -1; - while (c.moveToNext()) { - String tag = c.getString(c.getColumnIndex(Checkin.Events.TAG)); - String value = c.getString(c.getColumnIndex(Checkin.Events.VALUE)); - long date = c.getLong(c.getColumnIndex(Checkin.Events.DATE)); - assertEquals(Checkin.Events.Tag.TEST.toString(), tag); - if ("Test Value".equals(value) && date >= start) { - assertTrue(id < 0); - id = c.getInt(c.getColumnIndex(Checkin.Events._ID)); - } - } - assertTrue(id > 0); - - int rows = r.delete(ContentUris.withAppendedId(Checkin.Events.CONTENT_URI, id), null, null); - assertEquals(1, rows); - c.requery(); - while (c.moveToNext()) { - long date = c.getLong(c.getColumnIndex(Checkin.Events.DATE)); - assertTrue(date < start); // Have deleted the only newer TEST. - } - - c.close(); - } - - @MediumTest - public void testStatsUpdate() { - ContentResolver r = getContext().getContentResolver(); - - // First, delete any existing data associated with the TEST tag. - Uri uri = Checkin.updateStats(r, Checkin.Stats.Tag.TEST, 0, 0); - assertNotNull(uri); - assertEquals(1, r.delete(uri, null, null)); - assertFalse(r.query(uri, null, null, null, null).moveToNext()); - - // Now, add a known quantity to the TEST tag. - Uri u2 = Checkin.updateStats(r, Checkin.Stats.Tag.TEST, 1, 0.5); - assertFalse(uri.equals(u2)); - - Cursor c = r.query(u2, null, null, null, null); - assertTrue(c.moveToNext()); - assertEquals(1, c.getInt(c.getColumnIndex(Checkin.Stats.COUNT))); - assertEquals(0.5, c.getDouble(c.getColumnIndex(Checkin.Stats.SUM))); - assertFalse(c.moveToNext()); // Only one. - - // Add another known quantity to TEST (should sum with the first). - Uri u3 = Checkin.updateStats(r, Checkin.Stats.Tag.TEST, 2, 1.0); - assertEquals(u2, u3); - c.requery(); - assertTrue(c.moveToNext()); - assertEquals(3, c.getInt(c.getColumnIndex(Checkin.Stats.COUNT))); - assertEquals(1.5, c.getDouble(c.getColumnIndex(Checkin.Stats.SUM))); - assertFalse(c.moveToNext()); // Only one. - - // Now subtract the values; the whole row should disappear. - Uri u4 = Checkin.updateStats(r, Checkin.Stats.Tag.TEST, -3, -1.5); - assertNull(u4); - c.requery(); - assertFalse(c.moveToNext()); // Row has been deleted. - c.close(); - } - - @MediumTest - public void testCrashReport() throws Exception { - long start = System.currentTimeMillis(); - ContentResolver r = getContext().getContentResolver(); - - // Log a test (fake) crash report. - Checkin.reportCrash(r, new CrashData( - "Test", - "Test Activity", - new BuildData("Test Build", "123", start), - new ThrowableData(new RuntimeException("Test Exception")))); - - - // Crashes aren't indexed; go through them all to find the one we added. - Cursor c = r.query(Checkin.Crashes.CONTENT_URI, null, null, null, null); - - Uri uri = null; - while (c.moveToNext()) { - String coded = c.getString(c.getColumnIndex(Checkin.Crashes.DATA)); - byte[] bytes = Base64.decodeBase64(coded.getBytes()); - CrashData crash = new CrashData( - new DataInputStream(new ByteArrayInputStream(bytes))); - - // Should be exactly one recently added "Test" crash. - if (crash.getId().equals("Test") && crash.getTime() > start) { - assertEquals("Test Activity", crash.getActivity()); - assertEquals("Test Build", crash.getBuildData().getFingerprint()); - assertEquals("Test Exception", - crash.getThrowableData().getMessage()); - - assertNull(uri); - uri = ContentUris.withAppendedId(Checkin.Crashes.CONTENT_URI, c.getInt(c.getColumnIndex(Checkin.Crashes._ID))); - } - } - assertNotNull(uri); - c.close(); - - // Update the "logs" column. - ContentValues values = new ContentValues(); - values.put(Checkin.Crashes.LOGS, "Test Logs"); - assertEquals(1, r.update(uri, values, null, null)); - - c = r.query(uri, null, null, null, null); - assertTrue(c.moveToNext()); - String logs = c.getString(c.getColumnIndex(Checkin.Crashes.LOGS)); - assertEquals("Test Logs", logs); - c.deleteRow(); - c.close(); - - c.requery(); - assertFalse(c.moveToNext()); - c.close(); - } - - @MediumTest - public void testPropertiesRestricted() throws Exception { - ContentResolver r = getContext().getContentResolver(); - - // The test app doesn't have the permission to access properties, - // so any attempt to do so should fail. - try { - r.insert(Checkin.Properties.CONTENT_URI, new ContentValues()); - fail("SecurityException expected"); - } catch (SecurityException e) { - // expected - } - - try { - r.query(Checkin.Properties.CONTENT_URI, null, null, null, null); - fail("SecurityException expected"); - } catch (SecurityException e) { - // expected - } - } -} diff --git a/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java b/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java index 26a3ae0f84e46..27da4f1c72aef 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java @@ -61,6 +61,10 @@ public class HtmlTest extends TestCase { s = new SpannableString("Hello world\n\n\nor something"); assertEquals(Html.toHtml(s), "

    Hello world

    \n

    or something

    \n"); + + assertEquals("foo\nbar", Html.fromHtml("foo
    bar").toString()); + assertEquals("foo\nbar", Html.fromHtml("foo
    \nbar").toString()); + assertEquals("foo\nbar", Html.fromHtml("foo
    \n \nbar").toString()); } @SmallTest diff --git a/tests/AndroidTests/src/com/android/unit_tests/activity/ActivityManagerTest.java b/tests/AndroidTests/src/com/android/unit_tests/activity/ActivityManagerTest.java index d7d8c893c78f3..ab91761403121 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/activity/ActivityManagerTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/activity/ActivityManagerTest.java @@ -18,6 +18,8 @@ package com.android.unit_tests.activity; import android.app.ActivityManager; import android.content.Context; +import android.content.pm.ConfigurationInfo; +import android.content.res.Configuration; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.Suppress; @@ -90,6 +92,24 @@ public class ActivityManagerTest extends AndroidTestCase { // test: confirm our ANR'ing application shows up in the list } + @SmallTest + public void testGetDeviceConfigurationInfo() throws Exception { + ConfigurationInfo config = mActivityManager.getDeviceConfigurationInfo(); + assertNotNull(config); + // Validate values against configuration retrieved from resources + Configuration vconfig = mContext.getResources().getConfiguration(); + assertNotNull(vconfig); + assertEquals(config.reqKeyboardType, vconfig.keyboard); + assertEquals(config.reqTouchScreen, vconfig.touchscreen); + assertEquals(config.reqNavigation, vconfig.navigation); + if (vconfig.navigation == Configuration.NAVIGATION_NONAV) { + assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV); + } + if (vconfig.keyboard != Configuration.KEYBOARD_UNDEFINED) { + assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD); + } + } + // If any entries in appear in the list, sanity check them against all running applications private void checkErrorListSanity(List errList) { if (errList == null) return; diff --git a/tests/CoreTests/android/test/InstrumentationTestRunnerTest.java b/tests/CoreTests/android/test/InstrumentationTestRunnerTest.java index 359c902762e64..d9afd545f822b 100644 --- a/tests/CoreTests/android/test/InstrumentationTestRunnerTest.java +++ b/tests/CoreTests/android/test/InstrumentationTestRunnerTest.java @@ -89,6 +89,26 @@ public class InstrumentationTestRunnerTest extends TestCase { } + public void testDelayParameter() throws Exception { + int delayMsec = 1000; + Bundle args = new Bundle(); + args.putInt(InstrumentationTestRunner.ARGUMENT_DELAY_MSEC, delayMsec); + args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS, + PlaceHolderTest.class.getName() + "," + + PlaceHolderTest2.class.getName()); + mInstrumentationTestRunner.onCreate(args); + Thread t = new Thread() { public void run() { mInstrumentationTestRunner.onStart(); } }; + + // Should delay three times: before, between, and after the two tests. + long beforeTest = System.currentTimeMillis(); + t.start(); + t.join(); + assertTrue(System.currentTimeMillis() > beforeTest + delayMsec * 3); + assertTrue(mInstrumentationTestRunner.isStarted()); + assertTrue(mInstrumentationTestRunner.isFinished()); + assertTrue(mStubAndroidTestRunner.isRun()); + } + private void assertContentsInOrder(List actual, TestDescriptor... source) { TestDescriptor[] clonedSource = source.clone(); assertEquals("Unexpected number of items.", clonedSource.length, actual.size()); @@ -217,6 +237,7 @@ public class InstrumentationTestRunnerTest extends TestCase { } public void runTest() { + super.runTest(); mRun = true; } } diff --git a/tests/DpiTest/Android.mk b/tests/DpiTest/Android.mk new file mode 100644 index 0000000000000..3596c39a6b288 --- /dev/null +++ b/tests/DpiTest/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := DensityTest + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_PACKAGE) diff --git a/tests/DpiTest/AndroidManifest.xml b/tests/DpiTest/AndroidManifest.xml new file mode 100644 index 0000000000000..f71cff22811b0 --- /dev/null +++ b/tests/DpiTest/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + diff --git a/tests/DpiTest/res/drawable-120dpi/logo120dpi.png b/tests/DpiTest/res/drawable-120dpi/logo120dpi.png new file mode 100644 index 0000000000000..46bbd5bd3d485 Binary files /dev/null and b/tests/DpiTest/res/drawable-120dpi/logo120dpi.png differ diff --git a/tests/DpiTest/res/drawable-240dpi/logo240dpi.png b/tests/DpiTest/res/drawable-240dpi/logo240dpi.png new file mode 100644 index 0000000000000..4d717a86143dd Binary files /dev/null and b/tests/DpiTest/res/drawable-240dpi/logo240dpi.png differ diff --git a/tests/DpiTest/res/drawable/logo160dpi.png b/tests/DpiTest/res/drawable/logo160dpi.png new file mode 100644 index 0000000000000..c23b2ce83b43d Binary files /dev/null and b/tests/DpiTest/res/drawable/logo160dpi.png differ diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java new file mode 100644 index 0000000000000..3759622bbbec2 --- /dev/null +++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.test.dpi; + +import android.app.Activity; +import android.os.Bundle; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.ScrollView; +import android.view.View; +import android.content.Context; + +public class DpiTestActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout root = new LinearLayout(this); + root.setOrientation(LinearLayout.VERTICAL); + + LinearLayout layout = new LinearLayout(this); + addBitmapDrawable(layout, R.drawable.logo120dpi, true); + addBitmapDrawable(layout, R.drawable.logo160dpi, true); + addBitmapDrawable(layout, R.drawable.logo240dpi, true); + addLabelToRoot(root, "Prescaled bitmap in drawable"); + addChildToRoot(root, layout); + + layout = new LinearLayout(this); + addBitmapDrawable(layout, R.drawable.logo120dpi, false); + addBitmapDrawable(layout, R.drawable.logo160dpi, false); + addBitmapDrawable(layout, R.drawable.logo240dpi, false); + addLabelToRoot(root, "Autoscaled bitmap in drawable"); + addChildToRoot(root, layout); + + layout = new LinearLayout(this); + addResourceDrawable(layout, R.drawable.logo120dpi); + addResourceDrawable(layout, R.drawable.logo160dpi); + addResourceDrawable(layout, R.drawable.logo240dpi); + addLabelToRoot(root, "Prescaled resource drawable"); + addChildToRoot(root, layout); + + layout = new LinearLayout(this); + addCanvasBitmap(layout, R.drawable.logo120dpi, true); + addCanvasBitmap(layout, R.drawable.logo160dpi, true); + addCanvasBitmap(layout, R.drawable.logo240dpi, true); + addLabelToRoot(root, "Prescaled bitmap"); + addChildToRoot(root, layout); + + layout = new LinearLayout(this); + addCanvasBitmap(layout, R.drawable.logo120dpi, false); + addCanvasBitmap(layout, R.drawable.logo160dpi, false); + addCanvasBitmap(layout, R.drawable.logo240dpi, false); + addLabelToRoot(root, "Autoscaled bitmap"); + addChildToRoot(root, layout); + + setContentView(scrollWrap(root)); + } + + private View scrollWrap(View view) { + ScrollView scroller = new ScrollView(this); + scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.FILL_PARENT, + ScrollView.LayoutParams.FILL_PARENT)); + return scroller; + } + + private void addLabelToRoot(LinearLayout root, String text) { + TextView label = new TextView(this); + label.setText(text); + root.addView(label, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + } + + private void addChildToRoot(LinearLayout root, LinearLayout layout) { + root.addView(layout, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + } + + private void addBitmapDrawable(LinearLayout layout, int resource, boolean scale) { + Bitmap bitmap; + bitmap = loadAndPrintDpi(resource, scale); + + View view = new View(this); + + final BitmapDrawable d = new BitmapDrawable(bitmap); + if (!scale) d.setDensityScale(getResources().getDisplayMetrics()); + view.setBackgroundDrawable(d); + + view.setLayoutParams(new LinearLayout.LayoutParams(d.getIntrinsicWidth(), + d.getIntrinsicHeight())); + layout.addView(view); + } + + private void addResourceDrawable(LinearLayout layout, int resource) { + View view = new View(this); + + final Drawable d = getResources().getDrawable(resource); + view.setBackgroundDrawable(d); + + view.setLayoutParams(new LinearLayout.LayoutParams(d.getIntrinsicWidth(), + d.getIntrinsicHeight())); + layout.addView(view); + } + + private void addCanvasBitmap(LinearLayout layout, int resource, boolean scale) { + Bitmap bitmap; + bitmap = loadAndPrintDpi(resource, scale); + + ScaledBitmapView view = new ScaledBitmapView(this, bitmap); + + view.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + layout.addView(view); + } + + private Bitmap loadAndPrintDpi(int id, boolean scale) { + Bitmap bitmap; + if (scale) { + bitmap = BitmapFactory.decodeResource(getResources(), id); + } else { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inScaled = false; + bitmap = BitmapFactory.decodeResource(getResources(), id, opts); + } + return bitmap; + } + + private class ScaledBitmapView extends View { + private Bitmap mBitmap; + + public ScaledBitmapView(Context context, Bitmap bitmap) { + super(context); + mBitmap = bitmap; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension(mBitmap.getScaledWidth(), mBitmap.getScaledHeight()); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null); + } + } +} diff --git a/tests/DumpRenderTree/compare_layout_results.py b/tests/DumpRenderTree/compare_layout_results.py new file mode 100644 index 0000000000000..4383dbbd2d11a --- /dev/null +++ b/tests/DumpRenderTree/compare_layout_results.py @@ -0,0 +1,84 @@ +#!/usr/bin/python +""" +Compares results of two webkit layout test runs and writes +results to a file. +""" + +import optparse +import os + +def DiffResults(marker, new_results, old_results, diff_results, strip_reason): + """ Given two result files, generate diff and + write to diff_results file. All arguments are absolute paths + to files. + """ + old_file = open(old_results, "r") + new_file = open(new_results, "r") + diff_file = open(diff_results, "a") + + # Read lines from each file + ndict = new_file.readlines() + cdict = old_file.readlines() + + # Write marker to diff file + diff_file.writelines(marker + "\n") + diff_file.writelines("###############\n") + + # Strip reason from result lines + if strip_reason is True: + for i in range(0, len(ndict)): + ndict[i] = ndict[i].split(' ')[0] + "\n" + for i in range(0, len(cdict)): + cdict[i] = cdict[i].split(' ')[0] + "\n" + + # Find results in new_results missing in old_results + new_count=0 + for line in ndict: + if line not in cdict: + diff_file.writelines("+ " + line) + new_count += 1 + + # Find results in old_results missing in new_results + missing_count=0 + for line in cdict: + if line not in ndict: + diff_file.writelines("- " + line) + missing_count += 1 + + print marker + " >>> New = " + str(new_count) + " , Missing = " + str(missing_count) + + diff_file.writelines("\n\n") + + old_file.close() + new_file.close() + diff_file.close() + return + +def main(options, args): + results_dir = options.results_directory + ref_dir = options.ref_directory + if os.path.exists(results_dir + "/layout_tests_diff.txt"): + os.remove(results_dir + "/layout_tests_diff.txt") + + files=["passed", "nontext", "crashed"] + for f in files: + DiffResults(f, results_dir + "layout_tests_" + f + ".txt", + ref_dir + "layout_tests_" + f + ".txt", results_dir + "layout_tests_diff.txt", False) + + for f in ["failed"]: + DiffResults(f, results_dir + "layout_tests_" + f + ".txt", + ref_dir + "layout_tests_" + f + ".txt", results_dir + "layout_tests_diff.txt", True) + +if '__main__' == __name__: + option_parser = optparse.OptionParser() + option_parser.add_option("", "--ref-directory", + default="results/", + dest="ref_directory", + help="directory name under which results are stored.") + + option_parser.add_option("", "--results-directory", + default="layout-test-results/", + dest="results_directory", + help="directory name under which results are stored.") + options, args = option_parser.parse_args() + main(options, args) diff --git a/tests/DumpRenderTree/results/layout_tests_crashed.txt b/tests/DumpRenderTree/results/layout_tests_crashed.txt new file mode 100644 index 0000000000000..1859f07e37550 --- /dev/null +++ b/tests/DumpRenderTree/results/layout_tests_crashed.txt @@ -0,0 +1,2 @@ +/sdcard/android/layout_tests/fast/js/regexp-charclass-crash.html +/sdcard/android/layout_tests/fast/canvas/gradient-add-second-start-end-stop.html diff --git a/tests/DumpRenderTree/results/layout_tests_failed.txt b/tests/DumpRenderTree/results/layout_tests_failed.txt new file mode 100644 index 0000000000000..5a20b524180e9 --- /dev/null +++ b/tests/DumpRenderTree/results/layout_tests_failed.txt @@ -0,0 +1,283 @@ +/sdcard/android/layout_tests/fast/text/zero-width-characters.html : different length +/sdcard/android/layout_tests/fast/text/reset-drag-on-mouse-down.html : TIMEDOUT +/sdcard/android/layout_tests/fast/text/plain-text-line-breaks.html : different length +/sdcard/android/layout_tests/fast/replaced/table-percent-height.html : different length +/sdcard/android/layout_tests/fast/replaced/image-map.html : different length +/sdcard/android/layout_tests/fast/replaced/image-map-bug16782.html : different length +/sdcard/android/layout_tests/fast/parser/xml-declaration-missing-ending-mark.html : different length +/sdcard/android/layout_tests/fast/parser/tabindex-parsing.html : different length +/sdcard/android/layout_tests/fast/parser/script-tag-with-trailing-slash.html : different length +/sdcard/android/layout_tests/fast/parser/external-entities.xml : different length +/sdcard/android/layout_tests/fast/parser/entity-end-script-tag.html : different length +/sdcard/android/layout_tests/fast/parser/entity-comment-in-iframe.html : @offset: 0 +/sdcard/android/layout_tests/fast/parser/comment-in-iframe.html : @offset: 0 +/sdcard/android/layout_tests/fast/overflow/scroll-vertical-not-horizontal.html : different length +/sdcard/android/layout_tests/fast/loader/xmlhttprequest-missing-file-exception.html : different length +/sdcard/android/layout_tests/fast/loader/stop-provisional-loads.html : different length +/sdcard/android/layout_tests/fast/loader/plain-text-document.html : different length +/sdcard/android/layout_tests/fast/loader/opaque-base-url.html : @offset: 129 +/sdcard/android/layout_tests/fast/loader/onunload-form-submit-crash.html : different length +/sdcard/android/layout_tests/fast/loader/onunload-form-submit-crash-2.html : different length +/sdcard/android/layout_tests/fast/loader/local-JavaScript-from-local.html : different length +/sdcard/android/layout_tests/fast/loader/local-image-from-local.html : different length +/sdcard/android/layout_tests/fast/loader/local-iFrame-source-from-local.html : different length +/sdcard/android/layout_tests/fast/loader/local-CSS-from-local.html : TIMEDOUT +/sdcard/android/layout_tests/fast/loader/data-url-encoding-svg.html : different length +/sdcard/android/layout_tests/fast/loader/cancel-load-during-port-block-timer.html : TIMEDOUT +/sdcard/android/layout_tests/fast/js/var-shadows-arg-gc-crash.html : TIMEDOUT +/sdcard/android/layout_tests/fast/js/try-catch-crash.html : TIMEDOUT +/sdcard/android/layout_tests/fast/js/toString-and-valueOf-override.html : TIMEDOUT +/sdcard/android/layout_tests/fast/js/recursion-limit-equal.html : different length +/sdcard/android/layout_tests/fast/js/navigator-mimeTypes-length.html : different length +/sdcard/android/layout_tests/fast/js/math-transforms.html : TIMEDOUT +/sdcard/android/layout_tests/fast/js/global-recursion-on-full-stack.html : different length +/sdcard/android/layout_tests/fast/js/global-constructors.html : different length +/sdcard/android/layout_tests/fast/js/exceptions-thrown-in-callbacks.html : TIMEDOUT +/sdcard/android/layout_tests/fast/js/exception-sequencing.html : TIMEDOUT +/sdcard/android/layout_tests/fast/js/exception-sequencing-binops2.html : TIMEDOUT +/sdcard/android/layout_tests/fast/js/exception-sequencing-binops.html : TIMEDOUT +/sdcard/android/layout_tests/fast/js/exception-codegen-crash.html : different length +/sdcard/android/layout_tests/fast/js/duplicate-param-gc-crash.html : TIMEDOUT +/sdcard/android/layout_tests/fast/html/tab-order.html : @offset: 246 +/sdcard/android/layout_tests/fast/history/subframe-is-visited.html : different length +/sdcard/android/layout_tests/fast/history/go-back-to-changed-name.html : different length +/sdcard/android/layout_tests/fast/frames/viewsource-empty-attribute-value.html : different length +/sdcard/android/layout_tests/fast/frames/removal-before-attach-crash.html : different length +/sdcard/android/layout_tests/fast/frames/iframe-window-focus.html : different length +/sdcard/android/layout_tests/fast/frames/frameElement-widthheight.html : different length +/sdcard/android/layout_tests/fast/frames/frame-js-url-clientWidth.html : different length +/sdcard/android/layout_tests/fast/frames/frame-base-url.html : different length +/sdcard/android/layout_tests/fast/frames/empty-frame-src.html : TIMEDOUT +/sdcard/android/layout_tests/fast/forms/mailto/post-text-plain.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/post-text-plain-with-accept-charset.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items-x-www-form-urlencoded.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items-text-plain.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/post-multiple-items-multipart-form-data.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/post-append-query.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/get-overwrite-query.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii-text-plain.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii-text-plain-latin-1.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/get-non-ascii-always-utf-8.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/get-multiple-items.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/get-multiple-items-x-www-form-urlencoded.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/get-multiple-items-text-plain.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/advanced-put.html : different length +/sdcard/android/layout_tests/fast/forms/mailto/advanced-get.html : different length +/sdcard/android/layout_tests/fast/forms/textfield-to-password-on-focus.html : different length +/sdcard/android/layout_tests/fast/forms/textfield-onchange-deletion.html : different length +/sdcard/android/layout_tests/fast/forms/textfield-inside-anchor.html : different length +/sdcard/android/layout_tests/fast/forms/textarea-type-spaces.html : different length +/sdcard/android/layout_tests/fast/forms/textarea-scrolled-endline-caret.html : different length +/sdcard/android/layout_tests/fast/forms/textarea-paste-newline.html : different length +/sdcard/android/layout_tests/fast/forms/textarea-no-scroll-on-blur.html : @offset: 79 +/sdcard/android/layout_tests/fast/forms/textarea-initial-caret-position.html : different length +/sdcard/android/layout_tests/fast/forms/textarea-hard-linewrap.html : different length +/sdcard/android/layout_tests/fast/forms/textarea-default-value-leading-newline.html : TIMEDOUT +/sdcard/android/layout_tests/fast/forms/textarea-appearance-wrap.html : different length +/sdcard/android/layout_tests/fast/forms/text-field-setvalue-crash.html : different length +/sdcard/android/layout_tests/fast/forms/stuff-on-my-optgroup.html : TIMEDOUT +/sdcard/android/layout_tests/fast/forms/slider-onchange-event.html : different length +/sdcard/android/layout_tests/fast/forms/slider-mouse-events.html : different length +/sdcard/android/layout_tests/fast/forms/selection-functions.html : @offset: 306 +/sdcard/android/layout_tests/fast/forms/select-type-ahead-non-latin.html : different length +/sdcard/android/layout_tests/fast/forms/select-enter-key.html : different length +/sdcard/android/layout_tests/fast/forms/select-empty-list.html : different length +/sdcard/android/layout_tests/fast/forms/select-double-onchange.html : different length +/sdcard/android/layout_tests/fast/forms/select-accesskey.html : different length +/sdcard/android/layout_tests/fast/forms/search-hidden-cancel-button.html : different length +/sdcard/android/layout_tests/fast/forms/search-event-delay.html : different length +/sdcard/android/layout_tests/fast/forms/search-click-in-placeholder.html : @offset: 1 +/sdcard/android/layout_tests/fast/forms/search-cancel-button-mouseup.html : different length +/sdcard/android/layout_tests/fast/forms/plaintext-mode-1.html : different length +/sdcard/android/layout_tests/fast/forms/password-doubleclick-selection.html : different length +/sdcard/android/layout_tests/fast/forms/onselect-textfield.html : different length +/sdcard/android/layout_tests/fast/forms/onselect-textarea.html : different length +/sdcard/android/layout_tests/fast/forms/onselect-selectall.html : different length +/sdcard/android/layout_tests/fast/forms/onchange-enter-submit.html : different length +/sdcard/android/layout_tests/fast/forms/listbox-typeahead-scroll.html : different length +/sdcard/android/layout_tests/fast/forms/listbox-selection.html : different length +/sdcard/android/layout_tests/fast/forms/listbox-select-all.html : different length +/sdcard/android/layout_tests/fast/forms/listbox-onchange.html : different length +/sdcard/android/layout_tests/fast/forms/legend-access-key.html : different length +/sdcard/android/layout_tests/fast/forms/input-select-on-click.html : different length +/sdcard/android/layout_tests/fast/forms/input-radio-checked-tab.html : @offset: 115 +/sdcard/android/layout_tests/fast/forms/input-maxlength.html : TIMEDOUT +/sdcard/android/layout_tests/fast/forms/input-implicit-length-limit.html : different length +/sdcard/android/layout_tests/fast/forms/input-first-letter.html : TIMEDOUT +/sdcard/android/layout_tests/fast/forms/input-delete.html : different length +/sdcard/android/layout_tests/fast/forms/form-element-geometry.html : TIMEDOUT +/sdcard/android/layout_tests/fast/forms/form-collection-lookup.html : different length +/sdcard/android/layout_tests/fast/forms/form-and-frame-interaction-retains-values.html : different length +/sdcard/android/layout_tests/fast/forms/focus2.html : different length +/sdcard/android/layout_tests/fast/forms/focus.html : different length +/sdcard/android/layout_tests/fast/forms/focus-selection-textarea.html : different length +/sdcard/android/layout_tests/fast/forms/focus-selection-input.html : different length +/sdcard/android/layout_tests/fast/forms/focus-control-to-page.html : different length +/sdcard/android/layout_tests/fast/forms/enter-clicks-buttons.html : different length +/sdcard/android/layout_tests/fast/forms/drag-out-of-textarea.html : different length +/sdcard/android/layout_tests/fast/forms/drag-into-textarea.html : different length +/sdcard/android/layout_tests/fast/forms/check-box-enter-key.html : different length +/sdcard/android/layout_tests/fast/forms/button-state-restore.html : different length +/sdcard/android/layout_tests/fast/forms/button-spacebar-click.html : different length +/sdcard/android/layout_tests/fast/forms/button-enter-click.html : different length +/sdcard/android/layout_tests/fast/forms/autofocus-opera-003.html : @offset: 51 +/sdcard/android/layout_tests/fast/forms/access-key.html : different length +/sdcard/android/layout_tests/fast/events/window-events-capture.html : different length +/sdcard/android/layout_tests/fast/events/window-events-bubble2.html : different length +/sdcard/android/layout_tests/fast/events/window-events-bubble.html : different length +/sdcard/android/layout_tests/fast/events/tabindex-focus-chain.html : @offset: 0 +/sdcard/android/layout_tests/fast/events/scrollbar-double-click.html : different length +/sdcard/android/layout_tests/fast/events/scroll-to-anchor-in-overflow-hidden.html : different length +/sdcard/android/layout_tests/fast/events/scroll-event-does-not-bubble.html : different length +/sdcard/android/layout_tests/fast/events/related-target.html : different length +/sdcard/android/layout_tests/fast/events/option-tab.html : different length +/sdcard/android/layout_tests/fast/events/open-window-from-another-frame.html : different length +/sdcard/android/layout_tests/fast/events/onunload.html : different length +/sdcard/android/layout_tests/fast/events/onunload-window-property.html : different length +/sdcard/android/layout_tests/fast/events/onunload-not-on-body.html : different length +/sdcard/android/layout_tests/fast/events/onunload-clears-onbeforeunload.html : different length +/sdcard/android/layout_tests/fast/events/onsearch-enter.html : different length +/sdcard/android/layout_tests/fast/events/onload-webkit-before-webcore.html : @offset: 105 +/sdcard/android/layout_tests/fast/events/ondragenter.html : different length +/sdcard/android/layout_tests/fast/events/onclick-list-marker.html : different length +/sdcard/android/layout_tests/fast/events/onchange-textfield.html : different length +/sdcard/android/layout_tests/fast/events/onchange-select-popup.html : different length +/sdcard/android/layout_tests/fast/events/onchange-searchfield.html : different length +/sdcard/android/layout_tests/fast/events/onchange-passwordfield.html : different length +/sdcard/android/layout_tests/fast/events/onchange-click-hang.html : different length +/sdcard/android/layout_tests/fast/events/mouseup-outside-document.html : different length +/sdcard/android/layout_tests/fast/events/mouseup-from-button2.html : different length +/sdcard/android/layout_tests/fast/events/mouseover-mouseout2.html : different length +/sdcard/android/layout_tests/fast/events/mouseover-mouseout.html : different length +/sdcard/android/layout_tests/fast/events/mouseout-on-window.html : different length +/sdcard/android/layout_tests/fast/events/mouseout-dead-subframe.html : TIMEDOUT +/sdcard/android/layout_tests/fast/events/mousemove-after-drag-over-scrollbar.html : different length +/sdcard/android/layout_tests/fast/events/mouseclick-target-and-positioning.html : different length +/sdcard/android/layout_tests/fast/events/mouse-click-events.html : different length +/sdcard/android/layout_tests/fast/events/keypress-insert-tab.html : @offset: 85 +/sdcard/android/layout_tests/fast/events/keypress-focus-change.html : different length +/sdcard/android/layout_tests/fast/events/keydown-keypress-preventDefault.html : @offset: 228 +/sdcard/android/layout_tests/fast/events/keydown-keypress-focus-change.html : @offset: 172 +/sdcard/android/layout_tests/fast/events/key-events-in-input-text.html : different length +/sdcard/android/layout_tests/fast/events/key-events-in-input-button.html : different length +/sdcard/android/layout_tests/fast/events/js-keyboard-event-creation.html : different length +/sdcard/android/layout_tests/fast/events/input-image-scrolled-x-y.html : TIMEDOUT +/sdcard/android/layout_tests/fast/events/iframe-object-onload.html : different length +/sdcard/android/layout_tests/fast/events/frame-tab-focus.html : different length +/sdcard/android/layout_tests/fast/events/frame-programmatic-focus.html : different length +/sdcard/android/layout_tests/fast/events/frame-click-focus.html : different length +/sdcard/android/layout_tests/fast/events/fire-scroll-event.html : different length +/sdcard/android/layout_tests/fast/events/event-view-toString.html : TIMEDOUT +/sdcard/android/layout_tests/fast/events/drag-outside-window.html : @offset: 20 +/sdcard/android/layout_tests/fast/events/drag-in-frames.html : different length +/sdcard/android/layout_tests/fast/events/dblclick-addEventListener.html : different length +/sdcard/android/layout_tests/fast/events/contextmenu-scrolled-page-with-frame.html : different length +/sdcard/android/layout_tests/fast/events/content-changed-during-drop.html : different length +/sdcard/android/layout_tests/fast/events/click-count.html : different length +/sdcard/android/layout_tests/fast/events/capture-on-target.html : different length +/sdcard/android/layout_tests/fast/events/autoscroll-with-non-scrollable-parent.html : different length +/sdcard/android/layout_tests/fast/events/autoscroll-nonscrollable-iframe-in-scrollable-div.html : different length +/sdcard/android/layout_tests/fast/events/autoscroll-in-textfield.html : different length +/sdcard/android/layout_tests/fast/events/arrow-navigation.html : different length +/sdcard/android/layout_tests/fast/events/arrow-keys-on-body.html : different length +/sdcard/android/layout_tests/fast/events/anchor-image-scrolled-x-y.html : TIMEDOUT +/sdcard/android/layout_tests/fast/events/access-key-self-destruct.html : different length +/sdcard/android/layout_tests/fast/encoding/xml-utf-8-default.xml : different length +/sdcard/android/layout_tests/fast/encoding/url-host-name-non-ascii.html : different length +/sdcard/android/layout_tests/fast/encoding/percent-escaping.html : TIMEDOUT +/sdcard/android/layout_tests/fast/encoding/mailto-always-utf-8.html : different length +/sdcard/android/layout_tests/fast/encoding/invalid-xml.html : different length +/sdcard/android/layout_tests/fast/encoding/idn-security.html : different length +/sdcard/android/layout_tests/fast/encoding/frame-default-enc.html : @offset: 0 +/sdcard/android/layout_tests/fast/encoding/charset-koi8-u.html : @offset: 147 +/sdcard/android/layout_tests/fast/encoding/char-encoding.html : different length +/sdcard/android/layout_tests/fast/encoding/char-decoding.html : different length +/sdcard/android/layout_tests/fast/dynamic/paused-event-dispatch.html : @offset: 117 +/sdcard/android/layout_tests/fast/dom/Window/window-xy-properties.html : different length +/sdcard/android/layout_tests/fast/dom/Window/window-scroll-arguments.html : different length +/sdcard/android/layout_tests/fast/dom/Window/window-screen-properties.html : @offset: 65 +/sdcard/android/layout_tests/fast/dom/Window/window-resize.html : different length +/sdcard/android/layout_tests/fast/dom/Window/window-resize-and-move-arguments.html : different length +/sdcard/android/layout_tests/fast/dom/Window/window-property-clearing.html : different length +/sdcard/android/layout_tests/fast/dom/Window/window-properties.html : TIMEDOUT +/sdcard/android/layout_tests/fast/dom/Window/window-open-pending-url.html : different length +/sdcard/android/layout_tests/fast/dom/Window/window-onFocus.html : different length +/sdcard/android/layout_tests/fast/dom/Window/window-function-name-getter-precedence.html : different length +/sdcard/android/layout_tests/fast/dom/Window/window-early-properties.html : different length +/sdcard/android/layout_tests/fast/dom/Window/setting-properties-on-closed-window.html : TIMEDOUT +/sdcard/android/layout_tests/fast/dom/Window/redirect-with-timer.html : different length +/sdcard/android/layout_tests/fast/dom/Window/Plug-ins.html : different length +/sdcard/android/layout_tests/fast/dom/Window/orphaned-frame-access.html : different length +/sdcard/android/layout_tests/fast/dom/Window/new-window-opener.html : TIMEDOUT +/sdcard/android/layout_tests/fast/dom/Window/get-set-properties.html : @offset: 0 +/sdcard/android/layout_tests/fast/dom/Window/dom-access-from-closure-window.html : different length +/sdcard/android/layout_tests/fast/dom/Window/dom-access-from-closure-iframe.html : different length +/sdcard/android/layout_tests/fast/dom/Window/console-functions.html : different length +/sdcard/android/layout_tests/fast/dom/Window/closure-access-after-navigation-window.html : different length +/sdcard/android/layout_tests/fast/dom/Window/clear-timeout.html : different length +/sdcard/android/layout_tests/fast/dom/StyleSheet/ownerNode-lifetime-2.html : different length +/sdcard/android/layout_tests/fast/dom/HTMLSelectElement/listbox-select-reset.html : different length +/sdcard/android/layout_tests/fast/dom/HTMLObjectElement/object-as-frame.html : different length +/sdcard/android/layout_tests/fast/dom/HTMLDocument/hasFocus.html : different length +/sdcard/android/layout_tests/fast/dom/HTMLDocument/activeElement.html : different length +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/011.xml : different length +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/010.xml : different length +/sdcard/android/layout_tests/fast/dom/Element/offsetLeft-offsetTop-body-quirk.html : different length +/sdcard/android/layout_tests/fast/dom/DOMException/XPathException.html : different length +/sdcard/android/layout_tests/fast/dom/wrapper-classes.html : different length +/sdcard/android/layout_tests/fast/dom/tabindex-clamp.html : different length +/sdcard/android/layout_tests/fast/dom/simultaneouslyRegsiteredTimerFireOrder.html : different length +/sdcard/android/layout_tests/fast/dom/set-frame-src-while-running-script-in-frame.html : TIMEDOUT +/sdcard/android/layout_tests/fast/dom/select-selectedIndex.html : different length +/sdcard/android/layout_tests/fast/dom/open-and-close-by-DOM.html : different length +/sdcard/android/layout_tests/fast/dom/onerror-img.html : different length +/sdcard/android/layout_tests/fast/dom/object-plugin-hides-properties.html : TIMEDOUT +/sdcard/android/layout_tests/fast/dom/object-embed-plugin-scripting.html : different length +/sdcard/android/layout_tests/fast/dom/null-document-window-open-crash.html : different length +/sdcard/android/layout_tests/fast/dom/null-document-location-replace-crash.html : different length +/sdcard/android/layout_tests/fast/dom/null-document-location-put-crash.html : different length +/sdcard/android/layout_tests/fast/dom/null-document-location-href-put-crash.html : different length +/sdcard/android/layout_tests/fast/dom/null-document-location-assign-crash.html : different length +/sdcard/android/layout_tests/fast/dom/null-chardata-crash.html : different length +/sdcard/android/layout_tests/fast/dom/noscript-style.html : different length +/sdcard/android/layout_tests/fast/dom/noscript-canvas-in-created-html-document.html : different length +/sdcard/android/layout_tests/fast/dom/non-numeric-values-numeric-parameters.html : different length +/sdcard/android/layout_tests/fast/dom/node-item.html : different length +/sdcard/android/layout_tests/fast/dom/node-filter-gc.html : @offset: 1 +/sdcard/android/layout_tests/fast/dom/no-elements.html : different length +/sdcard/android/layout_tests/fast/dom/navigator-vendorSub.html : different length +/sdcard/android/layout_tests/fast/dom/namespaces-1.html : different length +/sdcard/android/layout_tests/fast/dom/NamedNodeMap-setNamedItem-crash.html : different length +/sdcard/android/layout_tests/fast/dom/namednodemap-namelookup.html : different length +/sdcard/android/layout_tests/fast/dom/mutation-event-remove-inserted-node.html : different length +/sdcard/android/layout_tests/fast/dom/location-hash.html : different length +/sdcard/android/layout_tests/fast/dom/length-attribute-mapping.html : TIMEDOUT +/sdcard/android/layout_tests/fast/dom/javascript-url-crash-function.html : different length +/sdcard/android/layout_tests/fast/dom/inner-text-001.html : different length +/sdcard/android/layout_tests/fast/dom/global-constructors.html : different length +/sdcard/android/layout_tests/fast/dom/gc-acid3.html : TIMEDOUT +/sdcard/android/layout_tests/fast/dom/gc-9.html : different length +/sdcard/android/layout_tests/fast/dom/gc-8.html : different length +/sdcard/android/layout_tests/fast/dom/frame-loading-via-document-write.html : TIMEDOUT +/sdcard/android/layout_tests/fast/dom/documenturi-not-affected-by-base-tag.html : different length +/sdcard/android/layout_tests/fast/dom/documenturi-can-hold-arbitrary-string.html : different length +/sdcard/android/layout_tests/fast/dom/document-width-height-force-layout.html : @offset: 142 +/sdcard/android/layout_tests/fast/dom/constructors-cached.html : different length +/sdcard/android/layout_tests/fast/dom/constructors-cached-navigate.html : different length +/sdcard/android/layout_tests/fast/dom/client-width-height.html : @offset: 119 +/sdcard/android/layout_tests/fast/dom/client-width-height-quirks.html : @offset: 115 +/sdcard/android/layout_tests/fast/dom/assign-to-window-status.html : different length +/sdcard/android/layout_tests/fast/css/variables/color-hex-test.html : different length +/sdcard/android/layout_tests/fast/css/html-attr-case-sensitivity.html : TIMEDOUT +/sdcard/android/layout_tests/fast/css/hover-affects-child.html : different length +/sdcard/android/layout_tests/fast/css/getComputedStyle-transform.html : different length +/sdcard/android/layout_tests/fast/css/dashboard-region-parser.html : different length +/sdcard/android/layout_tests/fast/css/computed-style.html : different length +/sdcard/android/layout_tests/fast/css/computed-style-without-renderer.html : different length +/sdcard/android/layout_tests/fast/canvas/toDataURL-supportedTypes.html : different length +/sdcard/android/layout_tests/fast/canvas/canvas-save-restore-with-path.html : different length +/sdcard/android/layout_tests/fast/canvas/canvas-longlived-context.html : TIMEDOUT +/sdcard/android/layout_tests/fast/canvas/canvas-getImageData.html : different length +/sdcard/android/layout_tests/fast/canvas/canvas-alphaImageData-behavior.html : different length diff --git a/tests/DumpRenderTree/results/layout_tests_nontext.txt b/tests/DumpRenderTree/results/layout_tests_nontext.txt new file mode 100644 index 0000000000000..0355cb727634a --- /dev/null +++ b/tests/DumpRenderTree/results/layout_tests_nontext.txt @@ -0,0 +1,1658 @@ +/sdcard/android/layout_tests/fast/transforms/transforms-with-opacity.html +/sdcard/android/layout_tests/fast/transforms/transform-positioned-ancestor.html +/sdcard/android/layout_tests/fast/transforms/transform-overflow.html +/sdcard/android/layout_tests/fast/transforms/skew-with-unitless-zero.html +/sdcard/android/layout_tests/fast/transforms/shadows.html +/sdcard/android/layout_tests/fast/transforms/overflow-with-transform.html +/sdcard/android/layout_tests/fast/transforms/matrix-02.html +/sdcard/android/layout_tests/fast/transforms/matrix-01.html +/sdcard/android/layout_tests/fast/transforms/identity-matrix.html +/sdcard/android/layout_tests/fast/transforms/diamond.html +/sdcard/android/layout_tests/fast/tokenizer/script_extra_close.html +/sdcard/android/layout_tests/fast/tokenizer/script-after-frameset.html +/sdcard/android/layout_tests/fast/tokenizer/missing-title-end-tag-2.html +/sdcard/android/layout_tests/fast/tokenizer/missing-title-end-tag-1.html +/sdcard/android/layout_tests/fast/tokenizer/missing-style-end-tag-2.html +/sdcard/android/layout_tests/fast/tokenizer/missing-style-end-tag-1.html +/sdcard/android/layout_tests/fast/tokenizer/external-script-document-write_2.html +/sdcard/android/layout_tests/fast/tokenizer/external-script-document-write.html +/sdcard/android/layout_tests/fast/tokenizer/003.html +/sdcard/android/layout_tests/fast/tokenizer/002.html +/sdcard/android/layout_tests/fast/tokenizer/001.html +/sdcard/android/layout_tests/fast/text/whitespace/tab-character-basics.html +/sdcard/android/layout_tests/fast/text/whitespace/span-in-word-space-causes-overflow.html +/sdcard/android/layout_tests/fast/text/whitespace/pre-wrap-spaces-after-newline.html +/sdcard/android/layout_tests/fast/text/whitespace/pre-wrap-overflow-selection.html +/sdcard/android/layout_tests/fast/text/whitespace/pre-wrap-line-test.html +/sdcard/android/layout_tests/fast/text/whitespace/pre-wrap-last-char.html +/sdcard/android/layout_tests/fast/text/whitespace/pre-newline-box-test.html +/sdcard/android/layout_tests/fast/text/whitespace/pre-break-word.html +/sdcard/android/layout_tests/fast/text/whitespace/nowrap-clear-float.html +/sdcard/android/layout_tests/fast/text/whitespace/normal-after-nowrap-breaking.html +/sdcard/android/layout_tests/fast/text/whitespace/nbsp-mode-and-linewraps.html +/sdcard/android/layout_tests/fast/text/whitespace/030.html +/sdcard/android/layout_tests/fast/text/whitespace/029.html +/sdcard/android/layout_tests/fast/text/whitespace/028.html +/sdcard/android/layout_tests/fast/text/whitespace/027.html +/sdcard/android/layout_tests/fast/text/whitespace/026.html +/sdcard/android/layout_tests/fast/text/whitespace/025.html +/sdcard/android/layout_tests/fast/text/whitespace/024.html +/sdcard/android/layout_tests/fast/text/whitespace/023.html +/sdcard/android/layout_tests/fast/text/whitespace/022.html +/sdcard/android/layout_tests/fast/text/whitespace/021.html +/sdcard/android/layout_tests/fast/text/whitespace/020.html +/sdcard/android/layout_tests/fast/text/whitespace/019.html +/sdcard/android/layout_tests/fast/text/whitespace/018.html +/sdcard/android/layout_tests/fast/text/whitespace/017.html +/sdcard/android/layout_tests/fast/text/whitespace/016.html +/sdcard/android/layout_tests/fast/text/whitespace/015.html +/sdcard/android/layout_tests/fast/text/whitespace/014.html +/sdcard/android/layout_tests/fast/text/whitespace/013.html +/sdcard/android/layout_tests/fast/text/whitespace/012.html +/sdcard/android/layout_tests/fast/text/whitespace/011.html +/sdcard/android/layout_tests/fast/text/whitespace/010.html +/sdcard/android/layout_tests/fast/text/whitespace/009.html +/sdcard/android/layout_tests/fast/text/whitespace/008.html +/sdcard/android/layout_tests/fast/text/whitespace/007.html +/sdcard/android/layout_tests/fast/text/whitespace/006.html +/sdcard/android/layout_tests/fast/text/whitespace/005.html +/sdcard/android/layout_tests/fast/text/whitespace/004.html +/sdcard/android/layout_tests/fast/text/whitespace/003.html +/sdcard/android/layout_tests/fast/text/whitespace/002.html +/sdcard/android/layout_tests/fast/text/whitespace/001.html +/sdcard/android/layout_tests/fast/text/international/wrap-CJK-001.html +/sdcard/android/layout_tests/fast/text/international/thai-line-breaks.html +/sdcard/android/layout_tests/fast/text/international/rtl-white-space-pre-wrap.html +/sdcard/android/layout_tests/fast/text/international/rtl-caret.html +/sdcard/android/layout_tests/fast/text/international/hindi-spacing.html +/sdcard/android/layout_tests/fast/text/international/complex-character-based-fallback.html +/sdcard/android/layout_tests/fast/text/international/bidi-override.html +/sdcard/android/layout_tests/fast/text/international/bidi-neutral-run.html +/sdcard/android/layout_tests/fast/text/international/bidi-neutral-directionality-paragraph-start.html +/sdcard/android/layout_tests/fast/text/international/bidi-menulist.html +/sdcard/android/layout_tests/fast/text/international/bidi-listbox.html +/sdcard/android/layout_tests/fast/text/international/bidi-listbox-atsui.html +/sdcard/android/layout_tests/fast/text/international/bidi-LDB-2-HTML.html +/sdcard/android/layout_tests/fast/text/international/bidi-LDB-2-formatting-characters.html +/sdcard/android/layout_tests/fast/text/international/bidi-LDB-2-CSS.html +/sdcard/android/layout_tests/fast/text/international/bidi-layout-across-linebreak.html +/sdcard/android/layout_tests/fast/text/international/bidi-L2-run-reordering.html +/sdcard/android/layout_tests/fast/text/international/bidi-innertext.html +/sdcard/android/layout_tests/fast/text/international/bidi-ignored-for-first-child-inline.html +/sdcard/android/layout_tests/fast/text/international/bidi-explicit-embedding.html +/sdcard/android/layout_tests/fast/text/international/bidi-european-terminators.html +/sdcard/android/layout_tests/fast/text/international/bidi-CS-after-AN.html +/sdcard/android/layout_tests/fast/text/international/bidi-control-chars-treated-as-ZWS.html +/sdcard/android/layout_tests/fast/text/international/bidi-AN-after-L.html +/sdcard/android/layout_tests/fast/text/international/bidi-AN-after-empty-run.html +/sdcard/android/layout_tests/fast/text/international/003.html +/sdcard/android/layout_tests/fast/text/international/002.html +/sdcard/android/layout_tests/fast/text/international/001.html +/sdcard/android/layout_tests/fast/text/firstline/003.html +/sdcard/android/layout_tests/fast/text/firstline/002.html +/sdcard/android/layout_tests/fast/text/firstline/001.html +/sdcard/android/layout_tests/fast/text/basic/generic-family-reset.html +/sdcard/android/layout_tests/fast/text/basic/generic-family-changes.html +/sdcard/android/layout_tests/fast/text/basic/015.html +/sdcard/android/layout_tests/fast/text/basic/014.html +/sdcard/android/layout_tests/fast/text/basic/013.html +/sdcard/android/layout_tests/fast/text/basic/012.html +/sdcard/android/layout_tests/fast/text/basic/011.html +/sdcard/android/layout_tests/fast/text/basic/009.html +/sdcard/android/layout_tests/fast/text/basic/008.html +/sdcard/android/layout_tests/fast/text/basic/007.html +/sdcard/android/layout_tests/fast/text/basic/006.html +/sdcard/android/layout_tests/fast/text/basic/005.html +/sdcard/android/layout_tests/fast/text/basic/004.html +/sdcard/android/layout_tests/fast/text/basic/003.html +/sdcard/android/layout_tests/fast/text/basic/002.html +/sdcard/android/layout_tests/fast/text/basic/001.html +/sdcard/android/layout_tests/fast/text/word-space.html +/sdcard/android/layout_tests/fast/text/word-break.html +/sdcard/android/layout_tests/fast/text/word-break-soft-hyphen.html +/sdcard/android/layout_tests/fast/text/word-break-run-rounding.html +/sdcard/android/layout_tests/fast/text/wide-zero-width-space.html +/sdcard/android/layout_tests/fast/text/wbr.html +/sdcard/android/layout_tests/fast/text/wbr-styled.html +/sdcard/android/layout_tests/fast/text/wbr-pre.html +/sdcard/android/layout_tests/fast/text/wbr-in-pre-crash.html +/sdcard/android/layout_tests/fast/text/updateNewFont.html +/sdcard/android/layout_tests/fast/text/trailing-white-space.html +/sdcard/android/layout_tests/fast/text/trailing-white-space-2.html +/sdcard/android/layout_tests/fast/text/textIteratorNilRenderer.html +/sdcard/android/layout_tests/fast/text/stroking.html +/sdcard/android/layout_tests/fast/text/stroking-decorations.html +/sdcard/android/layout_tests/fast/text/stripNullFromText.html +/sdcard/android/layout_tests/fast/text/softHyphen.html +/sdcard/android/layout_tests/fast/text/soft-hyphen-3.html +/sdcard/android/layout_tests/fast/text/soft-hyphen-2.html +/sdcard/android/layout_tests/fast/text/should-use-atsui.html +/sdcard/android/layout_tests/fast/text/shadow-no-blur.html +/sdcard/android/layout_tests/fast/text/selection-painted-separately.html +/sdcard/android/layout_tests/fast/text/selection-hard-linebreak.html +/sdcard/android/layout_tests/fast/text/reset-emptyRun.html +/sdcard/android/layout_tests/fast/text/monospace-width-cache.html +/sdcard/android/layout_tests/fast/text/midword-break-hang.html +/sdcard/android/layout_tests/fast/text/midword-break-after-breakable-char.html +/sdcard/android/layout_tests/fast/text/line-breaks.html +/sdcard/android/layout_tests/fast/text/line-breaks-after-white-space.html +/sdcard/android/layout_tests/fast/text/letter-spacing-negative-opacity.html +/sdcard/android/layout_tests/fast/text/large-text-composed-char.html +/sdcard/android/layout_tests/fast/text/justified-selection.html +/sdcard/android/layout_tests/fast/text/justified-selection-at-edge.html +/sdcard/android/layout_tests/fast/text/in-rendered-text-rtl.html +/sdcard/android/layout_tests/fast/text/font-initial.html +/sdcard/android/layout_tests/fast/text/fixed-pitch-control-characters.html +/sdcard/android/layout_tests/fast/text/embed-at-end-of-pre-wrap-line.html +/sdcard/android/layout_tests/fast/text/drawBidiText.html +/sdcard/android/layout_tests/fast/text/delete-hard-break-character.html +/sdcard/android/layout_tests/fast/text/cg-vs-atsui.html +/sdcard/android/layout_tests/fast/text/cg-fallback-bolding.html +/sdcard/android/layout_tests/fast/text/capitalize-preserve-nbsp.html +/sdcard/android/layout_tests/fast/text/capitalize-empty-generated-string.html +/sdcard/android/layout_tests/fast/text/capitalize-boundaries.html +/sdcard/android/layout_tests/fast/text/break-word.html +/sdcard/android/layout_tests/fast/text/bidi-embedding-pop-and-push-same.html +/sdcard/android/layout_tests/fast/text/atsui-spacing-features.html +/sdcard/android/layout_tests/fast/text/atsui-small-caps-punctuation-size.html +/sdcard/android/layout_tests/fast/text/atsui-rtl-override-selection.html +/sdcard/android/layout_tests/fast/text/atsui-pointtooffset-calls-cg.html +/sdcard/android/layout_tests/fast/text/atsui-partial-selection.html +/sdcard/android/layout_tests/fast/text/atsui-multiple-renderers.html +/sdcard/android/layout_tests/fast/text/atsui-kerning-and-ligatures.html +/sdcard/android/layout_tests/fast/text/apply-start-width-after-skipped-text.html +/sdcard/android/layout_tests/fast/text/align-center-rtl-spill.html +/sdcard/android/layout_tests/fast/table/border-collapsing/rtl-border-collapsing.html +/sdcard/android/layout_tests/fast/table/border-collapsing/equal-precedence-resolution.html +/sdcard/android/layout_tests/fast/table/border-collapsing/border-collapsing-head-foot.html +/sdcard/android/layout_tests/fast/table/border-collapsing/004.html +/sdcard/android/layout_tests/fast/table/border-collapsing/003.html +/sdcard/android/layout_tests/fast/table/border-collapsing/002.html +/sdcard/android/layout_tests/fast/table/border-collapsing/001.html +/sdcard/android/layout_tests/fast/table/wide-column.html +/sdcard/android/layout_tests/fast/table/wide-colspan.html +/sdcard/android/layout_tests/fast/table/vertical-align-baseline.html +/sdcard/android/layout_tests/fast/table/vertical-align-baseline-readjust.html +/sdcard/android/layout_tests/fast/table/unused-percent-heights.html +/sdcard/android/layout_tests/fast/table/unbreakable-images-quirk.html +/sdcard/android/layout_tests/fast/table/text-field-baseline.html +/sdcard/android/layout_tests/fast/table/tableInsideCaption.html +/sdcard/android/layout_tests/fast/table/table-hspace-align-center.html +/sdcard/android/layout_tests/fast/table/table-display-types.html +/sdcard/android/layout_tests/fast/table/table-display-types-strict.html +/sdcard/android/layout_tests/fast/table/stale-grid-crash.html +/sdcard/android/layout_tests/fast/table/spanOverlapRepaint.html +/sdcard/android/layout_tests/fast/table/rules-attr-dynchange2.html +/sdcard/android/layout_tests/fast/table/rules-attr-dynchange1.html +/sdcard/android/layout_tests/fast/table/rtl-cell-display-none-assert.html +/sdcard/android/layout_tests/fast/table/rowspan-paint-order.html +/sdcard/android/layout_tests/fast/table/rowindex.html +/sdcard/android/layout_tests/fast/table/row-height-recalc.html +/sdcard/android/layout_tests/fast/table/replaced-percent-height.html +/sdcard/android/layout_tests/fast/table/remove-td-display-none.html +/sdcard/android/layout_tests/fast/table/prepend-in-anonymous-table.html +/sdcard/android/layout_tests/fast/table/percent-widths-stretch.html +/sdcard/android/layout_tests/fast/table/percent-heights.html +/sdcard/android/layout_tests/fast/table/overflowHidden.html +/sdcard/android/layout_tests/fast/table/nobr.html +/sdcard/android/layout_tests/fast/table/nested-percent-height-table.html +/sdcard/android/layout_tests/fast/table/multiple-percent-height-rows.html +/sdcard/android/layout_tests/fast/table/max-width-integer-overflow.html +/sdcard/android/layout_tests/fast/table/large-width.html +/sdcard/android/layout_tests/fast/table/invisible-cell-background.html +/sdcard/android/layout_tests/fast/table/insert-row-before-form.html +/sdcard/android/layout_tests/fast/table/insert-cell-before-form.html +/sdcard/android/layout_tests/fast/table/insert-before-anonymous-ancestors.html +/sdcard/android/layout_tests/fast/table/inline-form-assert.html +/sdcard/android/layout_tests/fast/table/height-percent-test.html +/sdcard/android/layout_tests/fast/table/growCellForImageQuirk.html +/sdcard/android/layout_tests/fast/table/giantRowspan2.html +/sdcard/android/layout_tests/fast/table/giantRowspan.html +/sdcard/android/layout_tests/fast/table/giantCellspacing.html +/sdcard/android/layout_tests/fast/table/generated-caption.html +/sdcard/android/layout_tests/fast/table/frame-and-rules.html +/sdcard/android/layout_tests/fast/table/form-with-table-style.html +/sdcard/android/layout_tests/fast/table/floating-th.html +/sdcard/android/layout_tests/fast/table/fixed-with-auto-with-colspan.html +/sdcard/android/layout_tests/fast/table/fixed-table-non-cell-in-row.html +/sdcard/android/layout_tests/fast/table/fixed-nested.html +/sdcard/android/layout_tests/fast/table/empty-table-percent-height.html +/sdcard/android/layout_tests/fast/table/empty-section-crash.html +/sdcard/android/layout_tests/fast/table/empty-row-crash.html +/sdcard/android/layout_tests/fast/table/empty-cells.html +/sdcard/android/layout_tests/fast/table/edge-offsets.html +/sdcard/android/layout_tests/fast/table/dynamic-cellpadding.html +/sdcard/android/layout_tests/fast/table/div-as-col-span.html +/sdcard/android/layout_tests/fast/table/colgroup-spanning-groups-rules.html +/sdcard/android/layout_tests/fast/table/colgroup-preceded-by-caption.html +/sdcard/android/layout_tests/fast/table/click-near-anonymous-table.html +/sdcard/android/layout_tests/fast/table/cellindex.html +/sdcard/android/layout_tests/fast/table/cell-pref-width-invalidation.html +/sdcard/android/layout_tests/fast/table/cell-width-auto.html +/sdcard/android/layout_tests/fast/table/cell-absolute-child.html +/sdcard/android/layout_tests/fast/table/caption-relayout.html +/sdcard/android/layout_tests/fast/table/auto-with-percent-height.html +/sdcard/android/layout_tests/fast/table/append-cells2.html +/sdcard/android/layout_tests/fast/table/append-cells.html +/sdcard/android/layout_tests/fast/table/add-before-anonymous-child.html +/sdcard/android/layout_tests/fast/table/absolute-table-at-bottom.html +/sdcard/android/layout_tests/fast/table/100-percent-cell-width.html +/sdcard/android/layout_tests/fast/table/041.html +/sdcard/android/layout_tests/fast/table/040.html +/sdcard/android/layout_tests/fast/table/039.html +/sdcard/android/layout_tests/fast/table/038.html +/sdcard/android/layout_tests/fast/table/037.xml +/sdcard/android/layout_tests/fast/table/036.html +/sdcard/android/layout_tests/fast/table/035.html +/sdcard/android/layout_tests/fast/table/034.html +/sdcard/android/layout_tests/fast/table/033.html +/sdcard/android/layout_tests/fast/table/032.html +/sdcard/android/layout_tests/fast/table/031.html +/sdcard/android/layout_tests/fast/table/030.html +/sdcard/android/layout_tests/fast/table/029.html +/sdcard/android/layout_tests/fast/table/028.html +/sdcard/android/layout_tests/fast/table/027.html +/sdcard/android/layout_tests/fast/table/026.html +/sdcard/android/layout_tests/fast/table/025.html +/sdcard/android/layout_tests/fast/table/024.html +/sdcard/android/layout_tests/fast/table/023.html +/sdcard/android/layout_tests/fast/table/022.html +/sdcard/android/layout_tests/fast/table/021.html +/sdcard/android/layout_tests/fast/table/020.html +/sdcard/android/layout_tests/fast/table/018.html +/sdcard/android/layout_tests/fast/table/017.html +/sdcard/android/layout_tests/fast/table/016.html +/sdcard/android/layout_tests/fast/table/015.html +/sdcard/android/layout_tests/fast/table/014.html +/sdcard/android/layout_tests/fast/table/013.html +/sdcard/android/layout_tests/fast/table/012.html +/sdcard/android/layout_tests/fast/table/011.html +/sdcard/android/layout_tests/fast/table/010.html +/sdcard/android/layout_tests/fast/table/009.html +/sdcard/android/layout_tests/fast/table/008.html +/sdcard/android/layout_tests/fast/table/007.html +/sdcard/android/layout_tests/fast/table/006.html +/sdcard/android/layout_tests/fast/table/005.html +/sdcard/android/layout_tests/fast/table/004.html +/sdcard/android/layout_tests/fast/table/003.html +/sdcard/android/layout_tests/fast/table/002.html +/sdcard/android/layout_tests/fast/table/001.html +/sdcard/android/layout_tests/fast/selectors/unqualified-hover-strict.html +/sdcard/android/layout_tests/fast/selectors/unqualified-hover-quirks.html +/sdcard/android/layout_tests/fast/selectors/nondeterministic-combinators.html +/sdcard/android/layout_tests/fast/selectors/lang-vs-xml-lang.html +/sdcard/android/layout_tests/fast/selectors/lang-inheritance2.html +/sdcard/android/layout_tests/fast/selectors/lang-inheritance.html +/sdcard/android/layout_tests/fast/selectors/177b.html +/sdcard/android/layout_tests/fast/selectors/177a.html +/sdcard/android/layout_tests/fast/selectors/175c.html +/sdcard/android/layout_tests/fast/selectors/175b.html +/sdcard/android/layout_tests/fast/selectors/175a.html +/sdcard/android/layout_tests/fast/selectors/170d.html +/sdcard/android/layout_tests/fast/selectors/170c.html +/sdcard/android/layout_tests/fast/selectors/170b.html +/sdcard/android/layout_tests/fast/selectors/170a.html +/sdcard/android/layout_tests/fast/selectors/170.html +/sdcard/android/layout_tests/fast/selectors/169a.html +/sdcard/android/layout_tests/fast/selectors/169.html +/sdcard/android/layout_tests/fast/selectors/168a.html +/sdcard/android/layout_tests/fast/selectors/168.html +/sdcard/android/layout_tests/fast/selectors/167a.html +/sdcard/android/layout_tests/fast/selectors/167.html +/sdcard/android/layout_tests/fast/selectors/166a.html +/sdcard/android/layout_tests/fast/selectors/166.html +/sdcard/android/layout_tests/fast/selectors/160.html +/sdcard/android/layout_tests/fast/selectors/159.html +/sdcard/android/layout_tests/fast/selectors/158.html +/sdcard/android/layout_tests/fast/selectors/157.html +/sdcard/android/layout_tests/fast/selectors/156b.html +/sdcard/android/layout_tests/fast/selectors/155d.html +/sdcard/android/layout_tests/fast/selectors/155c.html +/sdcard/android/layout_tests/fast/selectors/155b.html +/sdcard/android/layout_tests/fast/selectors/155a.html +/sdcard/android/layout_tests/fast/selectors/155.html +/sdcard/android/layout_tests/fast/selectors/154.html +/sdcard/android/layout_tests/fast/selectors/090b.html +/sdcard/android/layout_tests/fast/selectors/089.html +/sdcard/android/layout_tests/fast/selectors/088b.html +/sdcard/android/layout_tests/fast/selectors/087b.html +/sdcard/android/layout_tests/fast/selectors/083.html +/sdcard/android/layout_tests/fast/selectors/078b.html +/sdcard/android/layout_tests/fast/selectors/077b.html +/sdcard/android/layout_tests/fast/selectors/077.html +/sdcard/android/layout_tests/fast/selectors/072b.html +/sdcard/android/layout_tests/fast/selectors/072.html +/sdcard/android/layout_tests/fast/selectors/066b.html +/sdcard/android/layout_tests/fast/selectors/066.html +/sdcard/android/layout_tests/fast/selectors/065.html +/sdcard/android/layout_tests/fast/selectors/064.html +/sdcard/android/layout_tests/fast/selectors/063.html +/sdcard/android/layout_tests/fast/selectors/062.html +/sdcard/android/layout_tests/fast/selectors/061.html +/sdcard/android/layout_tests/fast/selectors/060.html +/sdcard/android/layout_tests/fast/selectors/059.html +/sdcard/android/layout_tests/fast/selectors/058.html +/sdcard/android/layout_tests/fast/selectors/056.html +/sdcard/android/layout_tests/fast/selectors/054.html +/sdcard/android/layout_tests/fast/selectors/046.html +/sdcard/android/layout_tests/fast/selectors/045c.html +/sdcard/android/layout_tests/fast/selectors/045b.html +/sdcard/android/layout_tests/fast/selectors/045.html +/sdcard/android/layout_tests/fast/selectors/044d.html +/sdcard/android/layout_tests/fast/selectors/044c.html +/sdcard/android/layout_tests/fast/selectors/044b.html +/sdcard/android/layout_tests/fast/selectors/044.html +/sdcard/android/layout_tests/fast/selectors/043b.html +/sdcard/android/layout_tests/fast/selectors/043.html +/sdcard/android/layout_tests/fast/selectors/042.html +/sdcard/android/layout_tests/fast/selectors/041.html +/sdcard/android/layout_tests/fast/selectors/040.html +/sdcard/android/layout_tests/fast/selectors/039b.html +/sdcard/android/layout_tests/fast/selectors/039.html +/sdcard/android/layout_tests/fast/selectors/038.html +/sdcard/android/layout_tests/fast/selectors/034.html +/sdcard/android/layout_tests/fast/selectors/032.html +/sdcard/android/layout_tests/fast/selectors/027.html +/sdcard/android/layout_tests/fast/selectors/021b.html +/sdcard/android/layout_tests/fast/selectors/021.html +/sdcard/android/layout_tests/fast/selectors/020.html +/sdcard/android/layout_tests/fast/selectors/019.html +/sdcard/android/layout_tests/fast/selectors/018b.html +/sdcard/android/layout_tests/fast/selectors/018.html +/sdcard/android/layout_tests/fast/selectors/017.html +/sdcard/android/layout_tests/fast/selectors/016.html +/sdcard/android/layout_tests/fast/selectors/015.html +/sdcard/android/layout_tests/fast/selectors/014.html +/sdcard/android/layout_tests/fast/selectors/013.html +/sdcard/android/layout_tests/fast/selectors/012.html +/sdcard/android/layout_tests/fast/selectors/011.html +/sdcard/android/layout_tests/fast/selectors/010.html +/sdcard/android/layout_tests/fast/selectors/009.html +/sdcard/android/layout_tests/fast/selectors/008.html +/sdcard/android/layout_tests/fast/selectors/007b.html +/sdcard/android/layout_tests/fast/selectors/007a.html +/sdcard/android/layout_tests/fast/selectors/006.html +/sdcard/android/layout_tests/fast/selectors/005.html +/sdcard/android/layout_tests/fast/selectors/004.html +/sdcard/android/layout_tests/fast/selectors/003.html +/sdcard/android/layout_tests/fast/selectors/002.html +/sdcard/android/layout_tests/fast/selectors/001.html +/sdcard/android/layout_tests/fast/runin/001.html +/sdcard/android/layout_tests/fast/replaced/width100percent-textfield.html +/sdcard/android/layout_tests/fast/replaced/width100percent-textarea.html +/sdcard/android/layout_tests/fast/replaced/width100percent-searchfield.html +/sdcard/android/layout_tests/fast/replaced/width100percent-radio.html +/sdcard/android/layout_tests/fast/replaced/width100percent-menulist.html +/sdcard/android/layout_tests/fast/replaced/width100percent-image.html +/sdcard/android/layout_tests/fast/replaced/width100percent-checkbox.html +/sdcard/android/layout_tests/fast/replaced/width100percent-button.html +/sdcard/android/layout_tests/fast/replaced/three-selects-break.html +/sdcard/android/layout_tests/fast/replaced/selection-rect.html +/sdcard/android/layout_tests/fast/replaced/selection-rect-in-table-cell.html +/sdcard/android/layout_tests/fast/replaced/replaced-child-of-absolute-with-auto-height.html +/sdcard/android/layout_tests/fast/replaced/replaced-breaking.html +/sdcard/android/layout_tests/fast/replaced/replaced-breaking-mixture.html +/sdcard/android/layout_tests/fast/replaced/percent-height-in-anonymous-block.html +/sdcard/android/layout_tests/fast/replaced/percent-height-in-anonymous-block-widget.html +/sdcard/android/layout_tests/fast/replaced/pdf-as-image.html +/sdcard/android/layout_tests/fast/replaced/object-align-hspace-vspace.html +/sdcard/android/layout_tests/fast/replaced/minwidth-pxs.html +/sdcard/android/layout_tests/fast/replaced/minwidth-percent.html +/sdcard/android/layout_tests/fast/replaced/minheight-pxs.html +/sdcard/android/layout_tests/fast/replaced/minheight-percent.html +/sdcard/android/layout_tests/fast/replaced/maxwidth-pxs.html +/sdcard/android/layout_tests/fast/replaced/maxwidth-percent.html +/sdcard/android/layout_tests/fast/replaced/maxheight-pxs.html +/sdcard/android/layout_tests/fast/replaced/maxheight-percent.html +/sdcard/android/layout_tests/fast/replaced/max-width-percent.html +/sdcard/android/layout_tests/fast/replaced/inline-box-wrapper-handover.html +/sdcard/android/layout_tests/fast/replaced/image-tag.html +/sdcard/android/layout_tests/fast/replaced/image-solid-color-with-alpha.html +/sdcard/android/layout_tests/fast/replaced/image-sizing.html +/sdcard/android/layout_tests/fast/replaced/image-resize-width.html +/sdcard/android/layout_tests/fast/replaced/image-onload.html +/sdcard/android/layout_tests/fast/replaced/applet-rendering-java-disabled.html +/sdcard/android/layout_tests/fast/replaced/applet-disabled-positioned.html +/sdcard/android/layout_tests/fast/replaced/absolute-position-with-auto-width-and-left-and-right.html +/sdcard/android/layout_tests/fast/replaced/absolute-position-with-auto-height-and-top-and-bottom.html +/sdcard/android/layout_tests/fast/replaced/absolute-position-percentage-width.html +/sdcard/android/layout_tests/fast/replaced/absolute-position-percentage-height.html +/sdcard/android/layout_tests/fast/replaced/absolute-image-sizing.html +/sdcard/android/layout_tests/fast/replaced/008.html +/sdcard/android/layout_tests/fast/replaced/007.html +/sdcard/android/layout_tests/fast/replaced/006.html +/sdcard/android/layout_tests/fast/replaced/005.html +/sdcard/android/layout_tests/fast/replaced/004.html +/sdcard/android/layout_tests/fast/replaced/003.html +/sdcard/android/layout_tests/fast/replaced/002.html +/sdcard/android/layout_tests/fast/replaced/001.html +/sdcard/android/layout_tests/fast/repaint/transform-translate.html +/sdcard/android/layout_tests/fast/repaint/text-shadow.html +/sdcard/android/layout_tests/fast/repaint/text-shadow-horizontal.html +/sdcard/android/layout_tests/fast/repaint/text-selection-rect-in-overflow.html +/sdcard/android/layout_tests/fast/repaint/text-selection-rect-in-overflow-2.html +/sdcard/android/layout_tests/fast/repaint/table-two-pass-layout-overpaint.html +/sdcard/android/layout_tests/fast/repaint/table-section-repaint.html +/sdcard/android/layout_tests/fast/repaint/table-section-overflow.html +/sdcard/android/layout_tests/fast/repaint/table-row.html +/sdcard/android/layout_tests/fast/repaint/table-outer-border.html +/sdcard/android/layout_tests/fast/repaint/table-extra-bottom-grow.html +/sdcard/android/layout_tests/fast/repaint/table-collapsed-border.html +/sdcard/android/layout_tests/fast/repaint/table-col-background.html +/sdcard/android/layout_tests/fast/repaint/table-cell-vertical-overflow.html +/sdcard/android/layout_tests/fast/repaint/table-cell-move.html +/sdcard/android/layout_tests/fast/repaint/table-cell-collapsed-border.html +/sdcard/android/layout_tests/fast/repaint/subtree-root-skipped.html +/sdcard/android/layout_tests/fast/repaint/subtree-root-clip.html +/sdcard/android/layout_tests/fast/repaint/subtree-root-clip-3.html +/sdcard/android/layout_tests/fast/repaint/subtree-root-clip-2.html +/sdcard/android/layout_tests/fast/repaint/static-to-positioned.html +/sdcard/android/layout_tests/fast/repaint/shadow-multiple-vertical.html +/sdcard/android/layout_tests/fast/repaint/shadow-multiple-strict-vertical.html +/sdcard/android/layout_tests/fast/repaint/shadow-multiple-strict-horizontal.html +/sdcard/android/layout_tests/fast/repaint/shadow-multiple-horizontal.html +/sdcard/android/layout_tests/fast/repaint/selection-gap-overflow-scroll.html +/sdcard/android/layout_tests/fast/repaint/selection-after-remove.html +/sdcard/android/layout_tests/fast/repaint/selection-after-delete.html +/sdcard/android/layout_tests/fast/repaint/repaint-resized-overflow.html +/sdcard/android/layout_tests/fast/repaint/renderer-destruction-by-invalidateSelection-crash.html +/sdcard/android/layout_tests/fast/repaint/overflow-scroll-delete.html +/sdcard/android/layout_tests/fast/repaint/overflow-outline-repaint.html +/sdcard/android/layout_tests/fast/repaint/overflow-into-content.html +/sdcard/android/layout_tests/fast/repaint/overflow-delete-line.html +/sdcard/android/layout_tests/fast/repaint/overflow-clip-subtree-layout.html +/sdcard/android/layout_tests/fast/repaint/outline-shrinking.html +/sdcard/android/layout_tests/fast/repaint/outline-repaint-glitch.html +/sdcard/android/layout_tests/fast/repaint/outline-inset.html +/sdcard/android/layout_tests/fast/repaint/make-children-non-inline.html +/sdcard/android/layout_tests/fast/repaint/list-marker.html +/sdcard/android/layout_tests/fast/repaint/list-marker-2.html +/sdcard/android/layout_tests/fast/repaint/lines-with-layout-delta.html +/sdcard/android/layout_tests/fast/repaint/line-overflow.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-9.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-8.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-7.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-6.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-5.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-4.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-3.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-2.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-10.html +/sdcard/android/layout_tests/fast/repaint/line-flow-with-floats-1.html +/sdcard/android/layout_tests/fast/repaint/layout-state-relative.html +/sdcard/android/layout_tests/fast/repaint/layout-state-only-positioned.html +/sdcard/android/layout_tests/fast/repaint/layer-visibility.html +/sdcard/android/layout_tests/fast/repaint/layer-outline.html +/sdcard/android/layout_tests/fast/repaint/layer-outline-horizontal.html +/sdcard/android/layout_tests/fast/repaint/layer-hide-when-needs-layout.html +/sdcard/android/layout_tests/fast/repaint/layer-full-repaint.html +/sdcard/android/layout_tests/fast/repaint/layer-child-outline.html +/sdcard/android/layout_tests/fast/repaint/invisible-objects.html +/sdcard/android/layout_tests/fast/repaint/intermediate-layout-position.html +/sdcard/android/layout_tests/fast/repaint/intermediate-layout-position-clip.html +/sdcard/android/layout_tests/fast/repaint/inline-outline-repaint.html +/sdcard/android/layout_tests/fast/repaint/inline-block-overflow.html +/sdcard/android/layout_tests/fast/repaint/focus-ring.html +/sdcard/android/layout_tests/fast/repaint/focus-layers.html +/sdcard/android/layout_tests/fast/repaint/float-overflow.html +/sdcard/android/layout_tests/fast/repaint/float-overflow-right.html +/sdcard/android/layout_tests/fast/repaint/float-move-during-layout.html +/sdcard/android/layout_tests/fast/repaint/flexible-box-overflow.html +/sdcard/android/layout_tests/fast/repaint/flexible-box-overflow-horizontal.html +/sdcard/android/layout_tests/fast/repaint/fixed.html +/sdcard/android/layout_tests/fast/repaint/erase-overflow.html +/sdcard/android/layout_tests/fast/repaint/delete-into-nested-block.html +/sdcard/android/layout_tests/fast/repaint/control-clip.html +/sdcard/android/layout_tests/fast/repaint/continuation-after-outline.html +/sdcard/android/layout_tests/fast/repaint/content-into-overflow.html +/sdcard/android/layout_tests/fast/repaint/containing-block-position-change.html +/sdcard/android/layout_tests/fast/repaint/clipped-relative.html +/sdcard/android/layout_tests/fast/repaint/clip-with-layout-delta.html +/sdcard/android/layout_tests/fast/repaint/caret-outside-block.html +/sdcard/android/layout_tests/fast/repaint/button-spurious-layout-hint.html +/sdcard/android/layout_tests/fast/repaint/bugzilla-7235.html +/sdcard/android/layout_tests/fast/repaint/bugzilla-6473.html +/sdcard/android/layout_tests/fast/repaint/bugzilla-6388.html +/sdcard/android/layout_tests/fast/repaint/bugzilla-6278.html +/sdcard/android/layout_tests/fast/repaint/bugzilla-5699.html +/sdcard/android/layout_tests/fast/repaint/bugzilla-3509.html +/sdcard/android/layout_tests/fast/repaint/box-shadow-v.html +/sdcard/android/layout_tests/fast/repaint/box-shadow-h.html +/sdcard/android/layout_tests/fast/repaint/box-shadow-dynamic.html +/sdcard/android/layout_tests/fast/repaint/border-repaint-glitch.html +/sdcard/android/layout_tests/fast/repaint/border-radius-repaint.html +/sdcard/android/layout_tests/fast/repaint/border-fit-lines.html +/sdcard/android/layout_tests/fast/repaint/body-background-image.html +/sdcard/android/layout_tests/fast/repaint/backgroundSizeRepaint.html +/sdcard/android/layout_tests/fast/repaint/4776765.html +/sdcard/android/layout_tests/fast/repaint/4774354.html +/sdcard/android/layout_tests/fast/reflections/table-cell.html +/sdcard/android/layout_tests/fast/reflections/reflection-nesting.html +/sdcard/android/layout_tests/fast/reflections/reflection-direction.html +/sdcard/android/layout_tests/fast/reflections/inline-crash.html +/sdcard/android/layout_tests/fast/parser/title-error-test.html +/sdcard/android/layout_tests/fast/parser/style-script-head-test.html +/sdcard/android/layout_tests/fast/parser/parseCommentsInTitles.html +/sdcard/android/layout_tests/fast/parser/open-comment-in-textarea.html +/sdcard/android/layout_tests/fast/parser/open-comment-in-style.html +/sdcard/android/layout_tests/fast/parser/fonts.html +/sdcard/android/layout_tests/fast/parser/external-entities-in-xslt.xml +/sdcard/android/layout_tests/fast/parser/entity-comment-in-textarea.html +/sdcard/android/layout_tests/fast/parser/entity-comment-in-style.html +/sdcard/android/layout_tests/fast/parser/document-write-option.html +/sdcard/android/layout_tests/fast/parser/comments.html +/sdcard/android/layout_tests/fast/parser/comment-in-style.html +/sdcard/android/layout_tests/fast/parser/comment-in-script.html +/sdcard/android/layout_tests/fast/parser/broken-comments-vs-parsing-mode.html +/sdcard/android/layout_tests/fast/parser/bad-xml-slash.html +/sdcard/android/layout_tests/fast/parser/001.html +/sdcard/android/layout_tests/fast/overflow/unreachable-overflow-rtl-bug.html +/sdcard/android/layout_tests/fast/overflow/unreachable-content-test.html +/sdcard/android/layout_tests/fast/overflow/table-overflow-float.html +/sdcard/android/layout_tests/fast/overflow/scrollRevealButton.html +/sdcard/android/layout_tests/fast/overflow/scrollbar-position-update.html +/sdcard/android/layout_tests/fast/overflow/scroll-nested-positioned-layer-in-overflow.html +/sdcard/android/layout_tests/fast/overflow/position-relative.html +/sdcard/android/layout_tests/fast/overflow/overflow_hidden.html +/sdcard/android/layout_tests/fast/overflow/overflow-x-y.html +/sdcard/android/layout_tests/fast/overflow/overflow-text-hit-testing.html +/sdcard/android/layout_tests/fast/overflow/overflow-rtl.html +/sdcard/android/layout_tests/fast/overflow/overflow-rtl-inline-scrollbar.html +/sdcard/android/layout_tests/fast/overflow/overflow-focus-ring.html +/sdcard/android/layout_tests/fast/overflow/overflow-auto-table.html +/sdcard/android/layout_tests/fast/overflow/overflow-auto-position-absolute.html +/sdcard/android/layout_tests/fast/overflow/infiniteRecursionGuard.html +/sdcard/android/layout_tests/fast/overflow/infiniteRecursion.html +/sdcard/android/layout_tests/fast/overflow/image-selection-highlight.html +/sdcard/android/layout_tests/fast/overflow/hit-test-overflow-controls.html +/sdcard/android/layout_tests/fast/overflow/hidden-scrollbar-resize.html +/sdcard/android/layout_tests/fast/overflow/float-in-relpositioned.html +/sdcard/android/layout_tests/fast/overflow/dynamic-hidden.html +/sdcard/android/layout_tests/fast/overflow/clip-rects-fixed-ancestor.html +/sdcard/android/layout_tests/fast/overflow/childFocusRingClip.html +/sdcard/android/layout_tests/fast/overflow/008.html +/sdcard/android/layout_tests/fast/overflow/007.html +/sdcard/android/layout_tests/fast/overflow/006.html +/sdcard/android/layout_tests/fast/overflow/005.html +/sdcard/android/layout_tests/fast/overflow/004.html +/sdcard/android/layout_tests/fast/overflow/003.xml +/sdcard/android/layout_tests/fast/overflow/002.html +/sdcard/android/layout_tests/fast/overflow/001.html +/sdcard/android/layout_tests/fast/multicol/zeroColumnCount.html +/sdcard/android/layout_tests/fast/multicol/negativeColumnWidth.html +/sdcard/android/layout_tests/fast/multicol/float-multicol.html +/sdcard/android/layout_tests/fast/multicol/float-avoidance.html +/sdcard/android/layout_tests/fast/multicol/columns-shorthand-parsing.html +/sdcard/android/layout_tests/fast/multicol/column-rules.html +/sdcard/android/layout_tests/fast/media/viewport-media-query.html +/sdcard/android/layout_tests/fast/media/mq-width-absolute-04.html +/sdcard/android/layout_tests/fast/media/mq-width-absolute-03.html +/sdcard/android/layout_tests/fast/media/mq-width-absolute-02.html +/sdcard/android/layout_tests/fast/media/mq-width-absolute-01.html +/sdcard/android/layout_tests/fast/media/mq-valueless.html +/sdcard/android/layout_tests/fast/media/mq-simple-query-05.html +/sdcard/android/layout_tests/fast/media/mq-simple-query-04.html +/sdcard/android/layout_tests/fast/media/mq-simple-query-03.html +/sdcard/android/layout_tests/fast/media/mq-simple-query-02.html +/sdcard/android/layout_tests/fast/media/mq-simple-query-01.html +/sdcard/android/layout_tests/fast/media/mq-simple-neg-query-05.html +/sdcard/android/layout_tests/fast/media/mq-simple-neg-query-04.html +/sdcard/android/layout_tests/fast/media/mq-simple-neg-query-03.html +/sdcard/android/layout_tests/fast/media/mq-simple-neg-query-02.html +/sdcard/android/layout_tests/fast/media/mq-simple-neg-query-01.html +/sdcard/android/layout_tests/fast/media/mq-relative-constraints-09.html +/sdcard/android/layout_tests/fast/media/mq-relative-constraints-08.html +/sdcard/android/layout_tests/fast/media/mq-relative-constraints-07.html +/sdcard/android/layout_tests/fast/media/mq-relative-constraints-06.html +/sdcard/android/layout_tests/fast/media/mq-relative-constraints-05.html +/sdcard/android/layout_tests/fast/media/mq-relative-constraints-04.html +/sdcard/android/layout_tests/fast/media/mq-relative-constraints-03.html +/sdcard/android/layout_tests/fast/media/mq-relative-constraints-02.html +/sdcard/android/layout_tests/fast/media/mq-pixel-ratio.html +/sdcard/android/layout_tests/fast/media/mq-min-pixel-ratio.html +/sdcard/android/layout_tests/fast/media/mq-min-constraint.html +/sdcard/android/layout_tests/fast/media/mq-max-pixel-ratio.html +/sdcard/android/layout_tests/fast/media/mq-js-stylesheet-media-04.html +/sdcard/android/layout_tests/fast/media/mq-js-stylesheet-media-03.html +/sdcard/android/layout_tests/fast/media/mq-js-stylesheet-media-02.html +/sdcard/android/layout_tests/fast/media/mq-js-stylesheet-media-01.html +/sdcard/android/layout_tests/fast/media/mq-js-media-forward-syntax.html +/sdcard/android/layout_tests/fast/media/mq-js-media-except-03.html +/sdcard/android/layout_tests/fast/media/mq-js-media-except-02.html +/sdcard/android/layout_tests/fast/media/mq-js-media-except-01.html +/sdcard/android/layout_tests/fast/media/mq-invalid-syntax-05.html +/sdcard/android/layout_tests/fast/media/mq-invalid-syntax-04.html +/sdcard/android/layout_tests/fast/media/mq-invalid-syntax-03.html +/sdcard/android/layout_tests/fast/media/mq-invalid-syntax-02.html +/sdcard/android/layout_tests/fast/media/mq-invalid-syntax-01.html +/sdcard/android/layout_tests/fast/media/mq-invalid-media-feature-04.html +/sdcard/android/layout_tests/fast/media/mq-invalid-media-feature-03.html +/sdcard/android/layout_tests/fast/media/mq-invalid-media-feature-02.html +/sdcard/android/layout_tests/fast/media/mq-invalid-media-feature-01.html +/sdcard/android/layout_tests/fast/media/mq-grid-02.html +/sdcard/android/layout_tests/fast/media/mq-grid-01.html +/sdcard/android/layout_tests/fast/media/mq-compound-query-05.html +/sdcard/android/layout_tests/fast/media/mq-compound-query-04.html +/sdcard/android/layout_tests/fast/media/mq-compound-query-03.html +/sdcard/android/layout_tests/fast/media/mq-compound-query-02.html +/sdcard/android/layout_tests/fast/media/mq-compound-query-01.html +/sdcard/android/layout_tests/fast/media/monochrome.html +/sdcard/android/layout_tests/fast/media/media-type-syntax-02.html +/sdcard/android/layout_tests/fast/media/media-type-syntax-01.html +/sdcard/android/layout_tests/fast/media/media-descriptor-syntax-06.html +/sdcard/android/layout_tests/fast/media/media-descriptor-syntax-05.html +/sdcard/android/layout_tests/fast/media/media-descriptor-syntax-04.html +/sdcard/android/layout_tests/fast/media/media-descriptor-syntax-03.html +/sdcard/android/layout_tests/fast/media/media-descriptor-syntax-02.html +/sdcard/android/layout_tests/fast/media/media-descriptor-syntax-01.html +/sdcard/android/layout_tests/fast/media/implicit-media-all.html +/sdcard/android/layout_tests/fast/loader/text-document-wrapping.html +/sdcard/android/layout_tests/fast/loader/start-load-in-unload.html +/sdcard/android/layout_tests/fast/lists/w3-list-styles.html +/sdcard/android/layout_tests/fast/lists/scrolled-marker-paint.html +/sdcard/android/layout_tests/fast/lists/ordered-list-with-no-ol-tag.html +/sdcard/android/layout_tests/fast/lists/olstart.html +/sdcard/android/layout_tests/fast/lists/ol-start-dynamic.html +/sdcard/android/layout_tests/fast/lists/ol-display-types.html +/sdcard/android/layout_tests/fast/lists/numeric-markers-outside-list.html +/sdcard/android/layout_tests/fast/lists/markers-in-selection.html +/sdcard/android/layout_tests/fast/lists/marker-image-error.html +/sdcard/android/layout_tests/fast/lists/marker-before-empty-inline.html +/sdcard/android/layout_tests/fast/lists/list-style-type-dynamic-change.html +/sdcard/android/layout_tests/fast/lists/list-style-none-crash.html +/sdcard/android/layout_tests/fast/lists/list-item-line-height.html +/sdcard/android/layout_tests/fast/lists/li-values.html +/sdcard/android/layout_tests/fast/lists/li-style-alpha-huge-value-crash.html +/sdcard/android/layout_tests/fast/lists/li-br.html +/sdcard/android/layout_tests/fast/lists/item-not-in-list-line-wrapping.html +/sdcard/android/layout_tests/fast/lists/inlineBoxWrapperNullCheck.html +/sdcard/android/layout_tests/fast/lists/dynamic-marker-crash.html +/sdcard/android/layout_tests/fast/lists/drag-into-marker.html +/sdcard/android/layout_tests/fast/lists/decimal-leading-zero.html +/sdcard/android/layout_tests/fast/lists/big-list-marker.html +/sdcard/android/layout_tests/fast/lists/alpha-list-wrap.html +/sdcard/android/layout_tests/fast/lists/009.html +/sdcard/android/layout_tests/fast/lists/008.html +/sdcard/android/layout_tests/fast/lists/007.html +/sdcard/android/layout_tests/fast/lists/006.html +/sdcard/android/layout_tests/fast/lists/005.html +/sdcard/android/layout_tests/fast/lists/004.html +/sdcard/android/layout_tests/fast/lists/003.html +/sdcard/android/layout_tests/fast/lists/002.html +/sdcard/android/layout_tests/fast/lists/001.html +/sdcard/android/layout_tests/fast/layers/zindex-ridonkulous.html +/sdcard/android/layout_tests/fast/layers/zindex-inherit.html +/sdcard/android/layout_tests/fast/layers/scroll-rect-to-visible.html +/sdcard/android/layout_tests/fast/layers/remove-layer-with-nested-stacking.html +/sdcard/android/layout_tests/fast/layers/positioned-inside-root-with-margins.html +/sdcard/android/layout_tests/fast/layers/overflow-scroll-auto-switch.html +/sdcard/android/layout_tests/fast/layers/opacity-stacking.html +/sdcard/android/layout_tests/fast/layers/opacity-outline.html +/sdcard/android/layout_tests/fast/layers/layer-visibility.html +/sdcard/android/layout_tests/fast/layers/layer-visibility-sublayer.html +/sdcard/android/layout_tests/fast/layers/layer-content-visibility-change.html +/sdcard/android/layout_tests/fast/layers/add-layer-with-nested-stacking.html +/sdcard/android/layout_tests/fast/js/missing-style-end-tag-js.html +/sdcard/android/layout_tests/fast/invalid/td-inside-object.html +/sdcard/android/layout_tests/fast/invalid/table-residual-style-crash.html +/sdcard/android/layout_tests/fast/invalid/table-inside-stray-table-content.html +/sdcard/android/layout_tests/fast/invalid/residual-style.html +/sdcard/android/layout_tests/fast/invalid/nestedh3s.html +/sdcard/android/layout_tests/fast/invalid/missing-font-end-tag.html +/sdcard/android/layout_tests/fast/invalid/missing-dt-end-tag.html +/sdcard/android/layout_tests/fast/invalid/missing-dl-end-tag.html +/sdcard/android/layout_tests/fast/invalid/missing-address-end-tag.html +/sdcard/android/layout_tests/fast/invalid/junk-data.xml +/sdcard/android/layout_tests/fast/invalid/021.html +/sdcard/android/layout_tests/fast/invalid/020.xml +/sdcard/android/layout_tests/fast/invalid/019.html +/sdcard/android/layout_tests/fast/invalid/018.html +/sdcard/android/layout_tests/fast/invalid/017.html +/sdcard/android/layout_tests/fast/invalid/016.html +/sdcard/android/layout_tests/fast/invalid/015.html +/sdcard/android/layout_tests/fast/invalid/014.html +/sdcard/android/layout_tests/fast/invalid/013.html +/sdcard/android/layout_tests/fast/invalid/012.html +/sdcard/android/layout_tests/fast/invalid/011.html +/sdcard/android/layout_tests/fast/invalid/010.html +/sdcard/android/layout_tests/fast/invalid/009.html +/sdcard/android/layout_tests/fast/invalid/008.html +/sdcard/android/layout_tests/fast/invalid/007.html +/sdcard/android/layout_tests/fast/invalid/006.html +/sdcard/android/layout_tests/fast/invalid/005.html +/sdcard/android/layout_tests/fast/invalid/004.html +/sdcard/android/layout_tests/fast/invalid/003.html +/sdcard/android/layout_tests/fast/invalid/002.html +/sdcard/android/layout_tests/fast/invalid/001.html +/sdcard/android/layout_tests/fast/inspector/style.html +/sdcard/android/layout_tests/fast/inspector/matchedrules.html +/sdcard/android/layout_tests/fast/innerHTML/006.html +/sdcard/android/layout_tests/fast/innerHTML/003.html +/sdcard/android/layout_tests/fast/innerHTML/002.html +/sdcard/android/layout_tests/fast/innerHTML/001.html +/sdcard/android/layout_tests/fast/inline-block/tricky-baseline.html +/sdcard/android/layout_tests/fast/inline-block/overflow-clip.html +/sdcard/android/layout_tests/fast/inline-block/inline-block-vertical-align.html +/sdcard/android/layout_tests/fast/inline-block/contenteditable-baseline.html +/sdcard/android/layout_tests/fast/inline-block/14498-positionForCoordinates.html +/sdcard/android/layout_tests/fast/inline-block/006.html +/sdcard/android/layout_tests/fast/inline-block/005.html +/sdcard/android/layout_tests/fast/inline-block/004.html +/sdcard/android/layout_tests/fast/inline-block/003.html +/sdcard/android/layout_tests/fast/inline-block/002.html +/sdcard/android/layout_tests/fast/inline-block/001.html +/sdcard/android/layout_tests/fast/inline/styledEmptyInlinesWithBRs.html +/sdcard/android/layout_tests/fast/inline/positionedLifetime.html +/sdcard/android/layout_tests/fast/inline/percentage-margins.html +/sdcard/android/layout_tests/fast/inline/outline-continuations.html +/sdcard/android/layout_tests/fast/inline/inline-text-quirk-bpm.html +/sdcard/android/layout_tests/fast/inline/inline-padding-disables-text-quirk.html +/sdcard/android/layout_tests/fast/inline/inline-borders-with-bidi-override.html +/sdcard/android/layout_tests/fast/inline/emptyInlinesWithinLists.html +/sdcard/android/layout_tests/fast/inline/drawStyledEmptyInlinesWithWS.html +/sdcard/android/layout_tests/fast/inline/drawStyledEmptyInlines.html +/sdcard/android/layout_tests/fast/inline/dirtyLinesForInline.html +/sdcard/android/layout_tests/fast/inline/continuation-outlines.html +/sdcard/android/layout_tests/fast/inline/continuation-outlines-with-layers.html +/sdcard/android/layout_tests/fast/inline/002.html +/sdcard/android/layout_tests/fast/inline/001.html +/sdcard/android/layout_tests/fast/images/svg-as-tiled-background.html +/sdcard/android/layout_tests/fast/images/svg-as-relative-image.html +/sdcard/android/layout_tests/fast/images/svg-as-image.html +/sdcard/android/layout_tests/fast/images/svg-as-background.html +/sdcard/android/layout_tests/fast/images/pdf-as-tiled-background.html +/sdcard/android/layout_tests/fast/images/pdf-as-image.html +/sdcard/android/layout_tests/fast/images/pdf-as-image-landscape.html +/sdcard/android/layout_tests/fast/images/pdf-as-background.html +/sdcard/android/layout_tests/fast/images/object-image.html +/sdcard/android/layout_tests/fast/images/image-map-anchor-children.html +/sdcard/android/layout_tests/fast/images/image-in-map.html +/sdcard/android/layout_tests/fast/images/favicon-as-image.html +/sdcard/android/layout_tests/fast/images/embed-image.html +/sdcard/android/layout_tests/fast/images/animated-gif-with-offsets.html +/sdcard/android/layout_tests/fast/html/marquee-scroll.html +/sdcard/android/layout_tests/fast/html/listing.html +/sdcard/android/layout_tests/fast/html/link-rel-stylesheet.html +/sdcard/android/layout_tests/fast/html/keygen.html +/sdcard/android/layout_tests/fast/history/history_reload.html +/sdcard/android/layout_tests/fast/history/clicked-link-is-visited.html +/sdcard/android/layout_tests/fast/frames/viewsource-attribute.html +/sdcard/android/layout_tests/fast/frames/valid.html +/sdcard/android/layout_tests/fast/frames/onlyCommentInIFrame.html +/sdcard/android/layout_tests/fast/frames/no-frame-borders.html +/sdcard/android/layout_tests/fast/frames/invalid.html +/sdcard/android/layout_tests/fast/frames/inline-object-inside-frameset.html +/sdcard/android/layout_tests/fast/frames/iframe-with-frameborder.html +/sdcard/android/layout_tests/fast/frames/iframe-text-contents.html +/sdcard/android/layout_tests/fast/frames/iframe-scrolling-attribute.html +/sdcard/android/layout_tests/fast/frames/frameset-style-recalc.html +/sdcard/android/layout_tests/fast/frames/frameElement-iframe.html +/sdcard/android/layout_tests/fast/frames/frameElement-frame.html +/sdcard/android/layout_tests/fast/frames/frame-src-attribute.html +/sdcard/android/layout_tests/fast/frames/frame-set-whitespace-attributes.html +/sdcard/android/layout_tests/fast/frames/frame-scrolling-attribute.html +/sdcard/android/layout_tests/fast/frames/frame-navigation.html +/sdcard/android/layout_tests/fast/frames/frame-length-fractional.html +/sdcard/android/layout_tests/fast/frames/frame-element-name.html +/sdcard/android/layout_tests/fast/frames/empty-cols-attribute.html +/sdcard/android/layout_tests/fast/frames/contentWindow_iFrame.html +/sdcard/android/layout_tests/fast/frames/contentWindow_Frame.html +/sdcard/android/layout_tests/fast/frames/calculate-round.html +/sdcard/android/layout_tests/fast/frames/calculate-relative.html +/sdcard/android/layout_tests/fast/frames/calculate-percentage.html +/sdcard/android/layout_tests/fast/frames/calculate-order.html +/sdcard/android/layout_tests/fast/frames/calculate-fixed.html +/sdcard/android/layout_tests/fast/frames/002.html +/sdcard/android/layout_tests/fast/frames/001.html +/sdcard/android/layout_tests/fast/forms/thumbslider-no-parent-slider.html +/sdcard/android/layout_tests/fast/forms/thumbslider-crash.html +/sdcard/android/layout_tests/fast/forms/textfield-outline.html +/sdcard/android/layout_tests/fast/forms/textfield-drag-into-disabled.html +/sdcard/android/layout_tests/fast/forms/textAreaLineHeight.html +/sdcard/android/layout_tests/fast/forms/textarea-width.html +/sdcard/android/layout_tests/fast/forms/textarea-setinnerhtml.html +/sdcard/android/layout_tests/fast/forms/textarea-scrolled-type.html +/sdcard/android/layout_tests/fast/forms/textarea-rows-cols.html +/sdcard/android/layout_tests/fast/forms/slider-thumb-shared-style.html +/sdcard/android/layout_tests/fast/forms/slider-padding.html +/sdcard/android/layout_tests/fast/forms/select-writing-direction-natural.html +/sdcard/android/layout_tests/fast/forms/select-size.html +/sdcard/android/layout_tests/fast/forms/select-selected.html +/sdcard/android/layout_tests/fast/forms/select-list-box-with-height.html +/sdcard/android/layout_tests/fast/forms/select-initial-position.html +/sdcard/android/layout_tests/fast/forms/select-display-none-style-resolve.html +/sdcard/android/layout_tests/fast/forms/select-disabled-appearance.html +/sdcard/android/layout_tests/fast/forms/select-change-popup-to-listbox.html +/sdcard/android/layout_tests/fast/forms/select-change-listbox-to-popup.html +/sdcard/android/layout_tests/fast/forms/select-change-listbox-size.html +/sdcard/android/layout_tests/fast/forms/select-block-background.html +/sdcard/android/layout_tests/fast/forms/select-baseline.html +/sdcard/android/layout_tests/fast/forms/select-align.html +/sdcard/android/layout_tests/fast/forms/search-placeholder-value-changed.html +/sdcard/android/layout_tests/fast/forms/search-display-none-cancel-button.html +/sdcard/android/layout_tests/fast/forms/radio_checked_dynamic.html +/sdcard/android/layout_tests/fast/forms/radio_checked.html +/sdcard/android/layout_tests/fast/forms/radio-nested-labels.html +/sdcard/android/layout_tests/fast/forms/radio-attr-order.html +/sdcard/android/layout_tests/fast/forms/preserveFormDuringResidualStyle.html +/sdcard/android/layout_tests/fast/forms/plaintext-mode-2.html +/sdcard/android/layout_tests/fast/forms/placeholder-set-attribute.html +/sdcard/android/layout_tests/fast/forms/placeholder-pseudo-style.html +/sdcard/android/layout_tests/fast/forms/password-placeholder.html +/sdcard/android/layout_tests/fast/forms/password-placeholder-text-security.html +/sdcard/android/layout_tests/fast/forms/option-text-clip.html +/sdcard/android/layout_tests/fast/forms/option-strip-whitespace.html +/sdcard/android/layout_tests/fast/forms/option-script.html +/sdcard/android/layout_tests/fast/forms/option-index.html +/sdcard/android/layout_tests/fast/forms/negativeLineHeight.html +/sdcard/android/layout_tests/fast/forms/minWidthPercent.html +/sdcard/android/layout_tests/fast/forms/menulist-width-change.html +/sdcard/android/layout_tests/fast/forms/menulist-separator-painting.html +/sdcard/android/layout_tests/fast/forms/menulist-restrict-line-height.html +/sdcard/android/layout_tests/fast/forms/menulist-option-wrap.html +/sdcard/android/layout_tests/fast/forms/menulist-no-overflow.html +/sdcard/android/layout_tests/fast/forms/menulist-narrow-width.html +/sdcard/android/layout_tests/fast/forms/menulist-deselect-update.html +/sdcard/android/layout_tests/fast/forms/menulist-clip.html +/sdcard/android/layout_tests/fast/forms/listbox-width-change.html +/sdcard/android/layout_tests/fast/forms/listbox-selection-2.html +/sdcard/android/layout_tests/fast/forms/listbox-scrollbar-incremental-load.html +/sdcard/android/layout_tests/fast/forms/listbox-deselect-scroll.html +/sdcard/android/layout_tests/fast/forms/listbox-clip.html +/sdcard/android/layout_tests/fast/forms/input-width.html +/sdcard/android/layout_tests/fast/forms/input-value.html +/sdcard/android/layout_tests/fast/forms/input-type-text-min-width.html +/sdcard/android/layout_tests/fast/forms/input-type-change2.html +/sdcard/android/layout_tests/fast/forms/input-type-change.html +/sdcard/android/layout_tests/fast/forms/input-text-word-wrap.html +/sdcard/android/layout_tests/fast/forms/input-text-self-emptying-click.html +/sdcard/android/layout_tests/fast/forms/input-text-scroll-left-on-blur.html +/sdcard/android/layout_tests/fast/forms/input-text-paste-maxlength.html +/sdcard/android/layout_tests/fast/forms/input-text-option-delete.html +/sdcard/android/layout_tests/fast/forms/input-text-maxlength.html +/sdcard/android/layout_tests/fast/forms/input-text-drag-down.html +/sdcard/android/layout_tests/fast/forms/input-text-double-click.html +/sdcard/android/layout_tests/fast/forms/input-text-click-outside.html +/sdcard/android/layout_tests/fast/forms/input-text-click-inside.html +/sdcard/android/layout_tests/fast/forms/input-table.html +/sdcard/android/layout_tests/fast/forms/input-spaces.html +/sdcard/android/layout_tests/fast/forms/input-readonly-empty.html +/sdcard/android/layout_tests/fast/forms/input-readonly-dimmed.html +/sdcard/android/layout_tests/fast/forms/input-readonly-autoscroll.html +/sdcard/android/layout_tests/fast/forms/input-paste-undo.html +/sdcard/android/layout_tests/fast/forms/input-no-renderer.html +/sdcard/android/layout_tests/fast/forms/input-field-text-truncated.html +/sdcard/android/layout_tests/fast/forms/input-double-click-selection-gap-bug.html +/sdcard/android/layout_tests/fast/forms/input-disabled-color.html +/sdcard/android/layout_tests/fast/forms/input-baseline.html +/sdcard/android/layout_tests/fast/forms/input-appearance-width.html +/sdcard/android/layout_tests/fast/forms/input-appearance-visibility.html +/sdcard/android/layout_tests/fast/forms/input-appearance-selection.html +/sdcard/android/layout_tests/fast/forms/input-appearance-readonly.html +/sdcard/android/layout_tests/fast/forms/input-appearance-preventDefault.html +/sdcard/android/layout_tests/fast/forms/input-appearance-height.html +/sdcard/android/layout_tests/fast/forms/input-appearance-focus.html +/sdcard/android/layout_tests/fast/forms/input-appearance-default-bkcolor.html +/sdcard/android/layout_tests/fast/forms/input-appearance-bkcolor.html +/sdcard/android/layout_tests/fast/forms/input-align.html +/sdcard/android/layout_tests/fast/forms/input-align-image.html +/sdcard/android/layout_tests/fast/forms/indeterminate.html +/sdcard/android/layout_tests/fast/forms/image-border.html +/sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label07.html +/sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label06.html +/sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label05.html +/sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label04.html +/sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label03.html +/sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label02.html +/sdcard/android/layout_tests/fast/forms/HTMLOptionElement_label01.html +/sdcard/android/layout_tests/fast/forms/hidden-listbox.html +/sdcard/android/layout_tests/fast/forms/hidden-input-file.html +/sdcard/android/layout_tests/fast/forms/formmove3.html +/sdcard/android/layout_tests/fast/forms/formmove2.html +/sdcard/android/layout_tests/fast/forms/formmove.html +/sdcard/android/layout_tests/fast/forms/form-hides-table.html +/sdcard/android/layout_tests/fast/forms/float-before-fieldset.html +/sdcard/android/layout_tests/fast/forms/file-input-disabled.html +/sdcard/android/layout_tests/fast/forms/file-input-direction.html +/sdcard/android/layout_tests/fast/forms/fieldset-with-float.html +/sdcard/android/layout_tests/fast/forms/fieldset-align.html +/sdcard/android/layout_tests/fast/forms/dragging-to-file-input.html +/sdcard/android/layout_tests/fast/forms/dragging-to-disabled-file-input.html +/sdcard/android/layout_tests/fast/forms/disabled-select-change-index.html +/sdcard/android/layout_tests/fast/forms/control-restrict-line-height.html +/sdcard/android/layout_tests/fast/forms/control-clip.html +/sdcard/android/layout_tests/fast/forms/control-clip-overflow.html +/sdcard/android/layout_tests/fast/forms/checkbox-radio-onchange.html +/sdcard/android/layout_tests/fast/forms/button-white-space.html +/sdcard/android/layout_tests/fast/forms/button-text-transform.html +/sdcard/android/layout_tests/fast/forms/button-table-styles.html +/sdcard/android/layout_tests/fast/forms/button-submit.html +/sdcard/android/layout_tests/fast/forms/button-sizes.html +/sdcard/android/layout_tests/fast/forms/button-positioned.html +/sdcard/android/layout_tests/fast/forms/button-inner-block-reuse.html +/sdcard/android/layout_tests/fast/forms/button-generated-content.html +/sdcard/android/layout_tests/fast/forms/button-default-title.html +/sdcard/android/layout_tests/fast/forms/button-cannot-be-nested.html +/sdcard/android/layout_tests/fast/forms/button-align.html +/sdcard/android/layout_tests/fast/forms/box-shadow-override.html +/sdcard/android/layout_tests/fast/forms/blankbuttons.html +/sdcard/android/layout_tests/fast/forms/006.html +/sdcard/android/layout_tests/fast/forms/005.html +/sdcard/android/layout_tests/fast/forms/004.html +/sdcard/android/layout_tests/fast/forms/003.html +/sdcard/android/layout_tests/fast/forms/002.html +/sdcard/android/layout_tests/fast/forms/001.html +/sdcard/android/layout_tests/fast/flexbox/flex-hang.html +/sdcard/android/layout_tests/fast/flexbox/026.html +/sdcard/android/layout_tests/fast/flexbox/025.html +/sdcard/android/layout_tests/fast/flexbox/024.html +/sdcard/android/layout_tests/fast/flexbox/023.html +/sdcard/android/layout_tests/fast/flexbox/022.html +/sdcard/android/layout_tests/fast/flexbox/021.html +/sdcard/android/layout_tests/fast/flexbox/020.html +/sdcard/android/layout_tests/fast/flexbox/019.html +/sdcard/android/layout_tests/fast/flexbox/018.html +/sdcard/android/layout_tests/fast/flexbox/017.html +/sdcard/android/layout_tests/fast/flexbox/016.html +/sdcard/android/layout_tests/fast/flexbox/015.html +/sdcard/android/layout_tests/fast/flexbox/014.html +/sdcard/android/layout_tests/fast/flexbox/013.html +/sdcard/android/layout_tests/fast/flexbox/012.html +/sdcard/android/layout_tests/fast/flexbox/011.html +/sdcard/android/layout_tests/fast/flexbox/010.html +/sdcard/android/layout_tests/fast/flexbox/009.html +/sdcard/android/layout_tests/fast/flexbox/008.html +/sdcard/android/layout_tests/fast/flexbox/007.html +/sdcard/android/layout_tests/fast/flexbox/006.html +/sdcard/android/layout_tests/fast/flexbox/005.html +/sdcard/android/layout_tests/fast/flexbox/004.html +/sdcard/android/layout_tests/fast/flexbox/003.html +/sdcard/android/layout_tests/fast/flexbox/002.html +/sdcard/android/layout_tests/fast/flexbox/001.html +/sdcard/android/layout_tests/fast/events/updateLayoutForHitTest.html +/sdcard/android/layout_tests/fast/events/standalone-image-drag-to-editable.html +/sdcard/android/layout_tests/fast/events/reveal-link-when-focused.html +/sdcard/android/layout_tests/fast/events/onloadFrameCrash.html +/sdcard/android/layout_tests/fast/events/onload-re-entry.html +/sdcard/android/layout_tests/fast/events/mouseout-dead-node.html +/sdcard/android/layout_tests/fast/events/label-focus.html +/sdcard/android/layout_tests/fast/events/keydown-1.html +/sdcard/android/layout_tests/fast/events/focusingUnloadedFrame.html +/sdcard/android/layout_tests/fast/events/event-sender-mouse-moved.html +/sdcard/android/layout_tests/fast/events/event-listener-on-link.html +/sdcard/android/layout_tests/fast/events/context-onmousedown-event.html +/sdcard/android/layout_tests/fast/events/autoscroll.html +/sdcard/android/layout_tests/fast/events/attempt-scroll-with-no-scrollbars.html +/sdcard/android/layout_tests/fast/events/5056619.html +/sdcard/android/layout_tests/fast/encoding/xmacroman-encoding-test.html +/sdcard/android/layout_tests/fast/encoding/utf-16-no-bom.xml +/sdcard/android/layout_tests/fast/encoding/utf-16-little-endian.html +/sdcard/android/layout_tests/fast/encoding/utf-16-big-endian.html +/sdcard/android/layout_tests/fast/encoding/invalid-UTF-8.html +/sdcard/android/layout_tests/fast/encoding/denormalised-voiced-japanese-chars.html +/sdcard/android/layout_tests/fast/dynamic/view-overflow.html +/sdcard/android/layout_tests/fast/dynamic/subtree-table-cell-height.html +/sdcard/android/layout_tests/fast/dynamic/subtree-parent-static-y.html +/sdcard/android/layout_tests/fast/dynamic/subtree-no-common-root-static-y.html +/sdcard/android/layout_tests/fast/dynamic/subtree-boundary-percent-height.html +/sdcard/android/layout_tests/fast/dynamic/staticY.html +/sdcard/android/layout_tests/fast/dynamic/staticY-marking-parents-regression.html +/sdcard/android/layout_tests/fast/dynamic/selection-highlight-adjust.html +/sdcard/android/layout_tests/fast/dynamic/positioned-movement-with-positioned-children.html +/sdcard/android/layout_tests/fast/dynamic/outerHTML-img.html +/sdcard/android/layout_tests/fast/dynamic/outerHTML-doc.html +/sdcard/android/layout_tests/fast/dynamic/noninlinebadness.html +/sdcard/android/layout_tests/fast/dynamic/move-node-with-selection.html +/sdcard/android/layout_tests/fast/dynamic/link-href-change.html +/sdcard/android/layout_tests/fast/dynamic/layer-hit-test-crash.html +/sdcard/android/layout_tests/fast/dynamic/insert-before-table-part-in-continuation.html +/sdcard/android/layout_tests/fast/dynamic/genContentDestroyChildren.html +/sdcard/android/layout_tests/fast/dynamic/floating-to-positioned.html +/sdcard/android/layout_tests/fast/dynamic/floating-to-positioned-2.html +/sdcard/android/layout_tests/fast/dynamic/float-withdrawal.html +/sdcard/android/layout_tests/fast/dynamic/float-withdrawal-2.html +/sdcard/android/layout_tests/fast/dynamic/float-no-longer-overhanging.html +/sdcard/android/layout_tests/fast/dynamic/float-in-trailing-whitespace-after-last-line-break.html +/sdcard/android/layout_tests/fast/dynamic/flash-replacement-test.html +/sdcard/android/layout_tests/fast/dynamic/create-renderer-for-whitespace-only-text.html +/sdcard/android/layout_tests/fast/dynamic/containing-block-change.html +/sdcard/android/layout_tests/fast/dynamic/anonymous-block-orphaned-lines.html +/sdcard/android/layout_tests/fast/dynamic/anonymous-block-layer-lost.html +/sdcard/android/layout_tests/fast/dynamic/015.html +/sdcard/android/layout_tests/fast/dynamic/014.html +/sdcard/android/layout_tests/fast/dynamic/013.html +/sdcard/android/layout_tests/fast/dynamic/012.html +/sdcard/android/layout_tests/fast/dynamic/011.html +/sdcard/android/layout_tests/fast/dynamic/010.html +/sdcard/android/layout_tests/fast/dynamic/009.html +/sdcard/android/layout_tests/fast/dynamic/008.html +/sdcard/android/layout_tests/fast/dynamic/007.html +/sdcard/android/layout_tests/fast/dynamic/006.html +/sdcard/android/layout_tests/fast/dynamic/005.html +/sdcard/android/layout_tests/fast/dynamic/004.html +/sdcard/android/layout_tests/fast/dynamic/002.html +/sdcard/android/layout_tests/fast/dynamic/001.html +/sdcard/android/layout_tests/fast/dom/Window/open-existing-pop-up-blocking.html +/sdcard/android/layout_tests/fast/dom/Window/btoa-pnglet.html +/sdcard/android/layout_tests/fast/dom/Range/surroundContents-1.html +/sdcard/android/layout_tests/fast/dom/Range/create-contextual-fragment.html +/sdcard/android/layout_tests/fast/dom/HTMLTextAreaElement/reset-textarea.html +/sdcard/android/layout_tests/fast/dom/HTMLTableElement/createCaption.html +/sdcard/android/layout_tests/fast/dom/HTMLTableElement/colSpan.html +/sdcard/android/layout_tests/fast/dom/HTMLStyleElement/insert-parser-generated.html +/sdcard/android/layout_tests/fast/dom/HTMLObjectElement/vspace-hspace-as-number.html +/sdcard/android/layout_tests/fast/dom/HTMLHeadElement/textInHead5.html +/sdcard/android/layout_tests/fast/dom/HTMLHeadElement/textInHead4.html +/sdcard/android/layout_tests/fast/dom/HTMLHeadElement/textInHead3.html +/sdcard/android/layout_tests/fast/dom/HTMLHeadElement/textInHead2.html +/sdcard/android/layout_tests/fast/dom/HTMLHeadElement/textInHead1.html +/sdcard/android/layout_tests/fast/dom/HTMLHeadElement/head-link-style-href-check.html +/sdcard/android/layout_tests/fast/dom/HTMLElement/bdo.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/frameless-location-bugzilla10837.html +/sdcard/android/layout_tests/fast/dom/Element/null-offset-parent.html +/sdcard/android/layout_tests/fast/dom/Document/early-document-access.html +/sdcard/android/layout_tests/fast/dom/row-inner-text.html +/sdcard/android/layout_tests/fast/dom/replaceChild.html +/sdcard/android/layout_tests/fast/dom/outerText.html +/sdcard/android/layout_tests/fast/dom/isindex-002.html +/sdcard/android/layout_tests/fast/dom/isindex-001.html +/sdcard/android/layout_tests/fast/dom/focus-contenteditable.html +/sdcard/android/layout_tests/fast/dom/dom-parse-serialize-display.html +/sdcard/android/layout_tests/fast/dom/delete-contents.html +/sdcard/android/layout_tests/fast/dom/css-rule-functions.html +/sdcard/android/layout_tests/fast/dom/css-mediarule-insertRule-update.html +/sdcard/android/layout_tests/fast/dom/css-mediarule-deleteRule-update.html +/sdcard/android/layout_tests/fast/dom/css-inline-style-important.html +/sdcard/android/layout_tests/fast/dom/css-cached-import-rule.html +/sdcard/android/layout_tests/fast/dom/clone-node-dynamic-style.html +/sdcard/android/layout_tests/fast/dom/clone-contents-0-end-offset.html +/sdcard/android/layout_tests/fast/dom/clientWidthAfterDocumentIsRemoved.html +/sdcard/android/layout_tests/fast/dom/children-nodes.html +/sdcard/android/layout_tests/fast/dom/blur-contenteditable.html +/sdcard/android/layout_tests/fast/dom/attr_dead_doc.html +/sdcard/android/layout_tests/fast/doctypes/004.html +/sdcard/android/layout_tests/fast/doctypes/003.html +/sdcard/android/layout_tests/fast/doctypes/002.html +/sdcard/android/layout_tests/fast/doctypes/001.html +/sdcard/android/layout_tests/fast/css-generated-content/wbr-with-before-content.html +/sdcard/android/layout_tests/fast/css-generated-content/visibleContentHiddenParent.html +/sdcard/android/layout_tests/fast/css-generated-content/table-with-before.html +/sdcard/android/layout_tests/fast/css-generated-content/table-row-with-before.html +/sdcard/android/layout_tests/fast/css-generated-content/table-row-group-with-before.html +/sdcard/android/layout_tests/fast/css-generated-content/table-row-group-to-inline.html +/sdcard/android/layout_tests/fast/css-generated-content/table-cell-before-content.html +/sdcard/android/layout_tests/fast/css-generated-content/spellingToolTip-assert.html +/sdcard/android/layout_tests/fast/css-generated-content/positioned-background-hit-test-crash.html +/sdcard/android/layout_tests/fast/css-generated-content/no-openclose-quote.html +/sdcard/android/layout_tests/fast/css-generated-content/inline-display-types.html +/sdcard/android/layout_tests/fast/css-generated-content/hover-style-change.html +/sdcard/android/layout_tests/fast/css-generated-content/before-with-first-letter.html +/sdcard/android/layout_tests/fast/css-generated-content/after-order.html +/sdcard/android/layout_tests/fast/css-generated-content/absolute-position-inside-inline.html +/sdcard/android/layout_tests/fast/css-generated-content/016.html +/sdcard/android/layout_tests/fast/css-generated-content/015.html +/sdcard/android/layout_tests/fast/css-generated-content/014.html +/sdcard/android/layout_tests/fast/css-generated-content/013.html +/sdcard/android/layout_tests/fast/css-generated-content/012.html +/sdcard/android/layout_tests/fast/css-generated-content/011.html +/sdcard/android/layout_tests/fast/css-generated-content/010.html +/sdcard/android/layout_tests/fast/css-generated-content/009.html +/sdcard/android/layout_tests/fast/css-generated-content/008.html +/sdcard/android/layout_tests/fast/css-generated-content/007.html +/sdcard/android/layout_tests/fast/css-generated-content/005.html +/sdcard/android/layout_tests/fast/css-generated-content/004.html +/sdcard/android/layout_tests/fast/css-generated-content/003.html +/sdcard/android/layout_tests/fast/css-generated-content/002.html +/sdcard/android/layout_tests/fast/css-generated-content/001.html +/sdcard/android/layout_tests/fast/css/variables/variable-iteration-test.html +/sdcard/android/layout_tests/fast/css/variables/shorthand-test.html +/sdcard/android/layout_tests/fast/css/variables/set-variable-test.html +/sdcard/android/layout_tests/fast/css/variables/remove-variable-test.html +/sdcard/android/layout_tests/fast/css/variables/print-test.html +/sdcard/android/layout_tests/fast/css/variables/override-test.html +/sdcard/android/layout_tests/fast/css/variables/multiple-term-test.html +/sdcard/android/layout_tests/fast/css/variables/multiple-blocks-test.html +/sdcard/android/layout_tests/fast/css/variables/misplaced-variables-test.html +/sdcard/android/layout_tests/fast/css/variables/misplaced-import-test.html +/sdcard/android/layout_tests/fast/css/variables/margin-test.html +/sdcard/android/layout_tests/fast/css/variables/invalid-variable-test.html +/sdcard/android/layout_tests/fast/css/variables/inline-style-test.html +/sdcard/android/layout_tests/fast/css/variables/import-test.html +/sdcard/android/layout_tests/fast/css/variables/image-test.html +/sdcard/android/layout_tests/fast/css/variables/font-test.html +/sdcard/android/layout_tests/fast/css/variables/declaration-block-test.html +/sdcard/android/layout_tests/fast/css/variables/colors-test.html +/sdcard/android/layout_tests/fast/css/variables/block-cycle-test.html +/sdcard/android/layout_tests/fast/css/namespaces/007.xml +/sdcard/android/layout_tests/fast/css/namespaces/006.xml +/sdcard/android/layout_tests/fast/css/namespaces/005.xml +/sdcard/android/layout_tests/fast/css/namespaces/004.xml +/sdcard/android/layout_tests/fast/css/namespaces/003.xml +/sdcard/android/layout_tests/fast/css/namespaces/002.xml +/sdcard/android/layout_tests/fast/css/namespaces/001.xml +/sdcard/android/layout_tests/fast/css/counters/invalidate-cached-counter-node.html +/sdcard/android/layout_tests/fast/css/counters/counter-text-transform.html +/sdcard/android/layout_tests/fast/css/counters/counter-text-security.html +/sdcard/android/layout_tests/fast/css/zoom-property-parsing.html +/sdcard/android/layout_tests/fast/css/zoom-font-size.html +/sdcard/android/layout_tests/fast/css/ZeroOpacityLayers2.html +/sdcard/android/layout_tests/fast/css/ZeroOpacityLayers.html +/sdcard/android/layout_tests/fast/css/xml-stylesheet-pi-not-in-prolog.xml +/sdcard/android/layout_tests/fast/css/word-space-extra.html +/sdcard/android/layout_tests/fast/css/visibility-hit-test.html +/sdcard/android/layout_tests/fast/css/vertical-align-lengths.html +/sdcard/android/layout_tests/fast/css/value-list-out-of-bounds-crash.html +/sdcard/android/layout_tests/fast/css/universal-hover-quirk.html +/sdcard/android/layout_tests/fast/css/transition-color-unspecified.html +/sdcard/android/layout_tests/fast/css/transform-default-parameter.html +/sdcard/android/layout_tests/fast/css/textCapitalizeEdgeCases.html +/sdcard/android/layout_tests/fast/css/text-security.html +/sdcard/android/layout_tests/fast/css/text-align.html +/sdcard/android/layout_tests/fast/css/target-fragment-match.html +/sdcard/android/layout_tests/fast/css/table-text-align-strict.html +/sdcard/android/layout_tests/fast/css/table-text-align-quirk.html +/sdcard/android/layout_tests/fast/css/style-parsed-outside-head.html +/sdcard/android/layout_tests/fast/css/style-outside-head.html +/sdcard/android/layout_tests/fast/css/simple-selector-chain-parsing.html +/sdcard/android/layout_tests/fast/css/shadow-multiple.html +/sdcard/android/layout_tests/fast/css/selector-set-attribute.html +/sdcard/android/layout_tests/fast/css/rtl-ordering.html +/sdcard/android/layout_tests/fast/css/rgb-float.html +/sdcard/android/layout_tests/fast/css/resize-corner-tracking.html +/sdcard/android/layout_tests/fast/css/quirk-orphaned-units.html +/sdcard/android/layout_tests/fast/css/position-negative-top-margin.html +/sdcard/android/layout_tests/fast/css/percentage-non-integer.html +/sdcard/android/layout_tests/fast/css/pendingStylesheetFontSize.html +/sdcard/android/layout_tests/fast/css/outline-auto-location.html +/sdcard/android/layout_tests/fast/css/outline-auto-empty-rects.html +/sdcard/android/layout_tests/fast/css/only-of-type-pseudo-class.html +/sdcard/android/layout_tests/fast/css/only-child-pseudo-class.html +/sdcard/android/layout_tests/fast/css/negative-nth-child.html +/sdcard/android/layout_tests/fast/css/max-height-none.html +/sdcard/android/layout_tests/fast/css/MarqueeLayoutTest.html +/sdcard/android/layout_tests/fast/css/marginComputedStyle.html +/sdcard/android/layout_tests/fast/css/margin-top-bottom-dynamic.html +/sdcard/android/layout_tests/fast/css/margin-bottom-form-element-strict.html +/sdcard/android/layout_tests/fast/css/margin-bottom-form-element-quirk.html +/sdcard/android/layout_tests/fast/css/live-cssrules.html +/sdcard/android/layout_tests/fast/css/list-outline.html +/sdcard/android/layout_tests/fast/css/link-outside-head.html +/sdcard/android/layout_tests/fast/css/line-height.html +/sdcard/android/layout_tests/fast/css/line-height-overflow.html +/sdcard/android/layout_tests/fast/css/line-height-negative.html +/sdcard/android/layout_tests/fast/css/line-height-font-order.html +/sdcard/android/layout_tests/fast/css/layerZOrderCrash.html +/sdcard/android/layout_tests/fast/css/last-of-type-pseudo-class.html +/sdcard/android/layout_tests/fast/css/last-child-style-sharing.html +/sdcard/android/layout_tests/fast/css/last-child-pseudo-class.html +/sdcard/android/layout_tests/fast/css/invalidation-errors.html +/sdcard/android/layout_tests/fast/css/invalidation-errors-3.html +/sdcard/android/layout_tests/fast/css/invalidation-errors-2.html +/sdcard/android/layout_tests/fast/css/invalid-pseudo-classes.html +/sdcard/android/layout_tests/fast/css/invalid-percentage-property.html +/sdcard/android/layout_tests/fast/css/inline-properties-important.html +/sdcard/android/layout_tests/fast/css/import_with_baseurl.html +/sdcard/android/layout_tests/fast/css/import-rule-regression-11590.html +/sdcard/android/layout_tests/fast/css/imageTileOpacity.html +/sdcard/android/layout_tests/fast/css/ignore-text-zoom.html +/sdcard/android/layout_tests/fast/css/hsla-color.html +/sdcard/android/layout_tests/fast/css/hsl-color.html +/sdcard/android/layout_tests/fast/css/hover-subselector.html +/sdcard/android/layout_tests/fast/css/font_property_normal.html +/sdcard/android/layout_tests/fast/css/font-weight-1.html +/sdcard/android/layout_tests/fast/css/font-size-negative.html +/sdcard/android/layout_tests/fast/css/font-shorthand-weight-only.html +/sdcard/android/layout_tests/fast/css/font-face-unicode-range.html +/sdcard/android/layout_tests/fast/css/font-face-remote.html +/sdcard/android/layout_tests/fast/css/font-face-multiple-remote-sources.html +/sdcard/android/layout_tests/fast/css/font-face-multiple-faces.html +/sdcard/android/layout_tests/fast/css/font-face-descriptor-multiple-values.html +/sdcard/android/layout_tests/fast/css/font-face-in-media-rule.html +/sdcard/android/layout_tests/fast/css/font-face-default-font.html +/sdcard/android/layout_tests/fast/css/first-of-type-pseudo-class.html +/sdcard/android/layout_tests/fast/css/first-letter-visibility.html +/sdcard/android/layout_tests/fast/css/first-letter-skip-out-of-flow.html +/sdcard/android/layout_tests/fast/css/first-letter-recalculation.html +/sdcard/android/layout_tests/fast/css/first-letter-hover.html +/sdcard/android/layout_tests/fast/css/first-letter-float.html +/sdcard/android/layout_tests/fast/css/first-letter-float-after-float.html +/sdcard/android/layout_tests/fast/css/first-letter-detach.html +/sdcard/android/layout_tests/fast/css/first-letter-capitalized.html +/sdcard/android/layout_tests/fast/css/first-child-pseudo-class.html +/sdcard/android/layout_tests/fast/css/find-next-layer.html +/sdcard/android/layout_tests/fast/css/fieldset-display-row.html +/sdcard/android/layout_tests/fast/css/ex-after-font-variant.html +/sdcard/android/layout_tests/fast/css/error-in-last-decl.html +/sdcard/android/layout_tests/fast/css/empty-pseudo-class.html +/sdcard/android/layout_tests/fast/css/empty-generated-content.html +/sdcard/android/layout_tests/fast/css/empty-body-test.html +/sdcard/android/layout_tests/fast/css/dynamic-sibling-selector.html +/sdcard/android/layout_tests/fast/css/disabled-author-styles.html +/sdcard/android/layout_tests/fast/css/css3-nth-child.html +/sdcard/android/layout_tests/fast/css/css3-modsel-22.html +/sdcard/android/layout_tests/fast/css/css2-system-fonts.html +/sdcard/android/layout_tests/fast/css/css-imports.html +/sdcard/android/layout_tests/fast/css/continuationCrash.html +/sdcard/android/layout_tests/fast/css/contentImage.html +/sdcard/android/layout_tests/fast/css/contentDivWithChildren.html +/sdcard/android/layout_tests/fast/css/contentDiv.html +/sdcard/android/layout_tests/fast/css/content-dynamic.html +/sdcard/android/layout_tests/fast/css/color-quirk.html +/sdcard/android/layout_tests/fast/css/border-height.html +/sdcard/android/layout_tests/fast/css/begin-end-contain-selector-empty-value.html +/sdcard/android/layout_tests/fast/css/background-shorthand-invalid-url.html +/sdcard/android/layout_tests/fast/css/background-image-with-baseurl.html +/sdcard/android/layout_tests/fast/css/attribute-selector-empty-value.html +/sdcard/android/layout_tests/fast/css/apple-prefix.html +/sdcard/android/layout_tests/fast/css/affected-by-hover-after-style-change.html +/sdcard/android/layout_tests/fast/css/acid2.html +/sdcard/android/layout_tests/fast/css/acid2-pixel.html +/sdcard/android/layout_tests/fast/css/absolute-poition-in-rtl-parent.html +/sdcard/android/layout_tests/fast/css/008.html +/sdcard/android/layout_tests/fast/css/007.html +/sdcard/android/layout_tests/fast/css/006.html +/sdcard/android/layout_tests/fast/css/005.html +/sdcard/android/layout_tests/fast/css/004.html +/sdcard/android/layout_tests/fast/css/003.html +/sdcard/android/layout_tests/fast/css/002.html +/sdcard/android/layout_tests/fast/css/001.html +/sdcard/android/layout_tests/fast/compact/003.html +/sdcard/android/layout_tests/fast/compact/002.html +/sdcard/android/layout_tests/fast/compact/001.html +/sdcard/android/layout_tests/fast/clip/outline-overflowClip.html +/sdcard/android/layout_tests/fast/clip/nestedTransparencyClip.html +/sdcard/android/layout_tests/fast/clip/017.html +/sdcard/android/layout_tests/fast/clip/016.html +/sdcard/android/layout_tests/fast/clip/015.html +/sdcard/android/layout_tests/fast/clip/014.html +/sdcard/android/layout_tests/fast/clip/013.html +/sdcard/android/layout_tests/fast/clip/012.html +/sdcard/android/layout_tests/fast/clip/011.html +/sdcard/android/layout_tests/fast/clip/010.html +/sdcard/android/layout_tests/fast/clip/009.html +/sdcard/android/layout_tests/fast/clip/008.html +/sdcard/android/layout_tests/fast/clip/007.html +/sdcard/android/layout_tests/fast/clip/006.html +/sdcard/android/layout_tests/fast/clip/005.html +/sdcard/android/layout_tests/fast/clip/004.html +/sdcard/android/layout_tests/fast/clip/003.html +/sdcard/android/layout_tests/fast/clip/002.html +/sdcard/android/layout_tests/fast/clip/001.html +/sdcard/android/layout_tests/fast/canvas/zero-size-fill-rect.html +/sdcard/android/layout_tests/fast/canvas/shadow-offset-7.html +/sdcard/android/layout_tests/fast/canvas/shadow-offset-6.html +/sdcard/android/layout_tests/fast/canvas/shadow-offset-5.html +/sdcard/android/layout_tests/fast/canvas/shadow-offset-4.html +/sdcard/android/layout_tests/fast/canvas/shadow-offset-3.html +/sdcard/android/layout_tests/fast/canvas/shadow-offset-2.html +/sdcard/android/layout_tests/fast/canvas/shadow-offset-1.html +/sdcard/android/layout_tests/fast/canvas/quadraticCurveTo.xml +/sdcard/android/layout_tests/fast/canvas/patternfill-repeat.html +/sdcard/android/layout_tests/fast/canvas/image-object-in-canvas.html +/sdcard/android/layout_tests/fast/canvas/gradient-empty-path.html +/sdcard/android/layout_tests/fast/canvas/fillrect_gradient.html +/sdcard/android/layout_tests/fast/canvas/fillrect-gradient-zero-stops.html +/sdcard/android/layout_tests/fast/canvas/fill-stroke-clip-reset-path.html +/sdcard/android/layout_tests/fast/canvas/drawImage.html +/sdcard/android/layout_tests/fast/canvas/canvasDrawingIntoSelf.html +/sdcard/android/layout_tests/fast/canvas/canvas-transforms-during-path.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-skewed.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-non-invertible.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-nan.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-multiply.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-infinity.html +/sdcard/android/layout_tests/fast/canvas/canvas-transform-identity.html +/sdcard/android/layout_tests/fast/canvas/canvas-text-baseline.html +/sdcard/android/layout_tests/fast/canvas/canvas-text-alignment.html +/sdcard/android/layout_tests/fast/canvas/canvas-size-change-after-layout.html +/sdcard/android/layout_tests/fast/canvas/canvas-resize-reset.html +/sdcard/android/layout_tests/fast/canvas/canvas-bg.html +/sdcard/android/layout_tests/fast/canvas/canvas-before-css.html +/sdcard/android/layout_tests/fast/box-sizing/panels-two.html +/sdcard/android/layout_tests/fast/box-sizing/panels-one.html +/sdcard/android/layout_tests/fast/box-sizing/box-sizing.html +/sdcard/android/layout_tests/fast/box-shadow/border-radius-big.html +/sdcard/android/layout_tests/fast/box-shadow/basic-shadows.html +/sdcard/android/layout_tests/fast/borders/svg-as-border-image.html +/sdcard/android/layout_tests/fast/borders/svg-as-border-image-2.html +/sdcard/android/layout_tests/fast/borders/outline-offset-min-assert.html +/sdcard/android/layout_tests/fast/borders/inline-mask-overlay-image.html +/sdcard/android/layout_tests/fast/borders/fieldsetBorderRadius.html +/sdcard/android/layout_tests/fast/borders/borderRadiusSolid04.html +/sdcard/android/layout_tests/fast/borders/borderRadiusSolid03.html +/sdcard/android/layout_tests/fast/borders/borderRadiusSolid02.html +/sdcard/android/layout_tests/fast/borders/borderRadiusSolid01.html +/sdcard/android/layout_tests/fast/borders/borderRadiusRidge01.html +/sdcard/android/layout_tests/fast/borders/borderRadiusOutset01.html +/sdcard/android/layout_tests/fast/borders/borderRadiusInvalidColor.html +/sdcard/android/layout_tests/fast/borders/borderRadiusInset01.html +/sdcard/android/layout_tests/fast/borders/borderRadiusGroove02.html +/sdcard/android/layout_tests/fast/borders/borderRadiusGroove01.html +/sdcard/android/layout_tests/fast/borders/borderRadiusDouble03.html +/sdcard/android/layout_tests/fast/borders/borderRadiusDouble02.html +/sdcard/android/layout_tests/fast/borders/borderRadiusDouble01.html +/sdcard/android/layout_tests/fast/borders/borderRadiusDotted03.html +/sdcard/android/layout_tests/fast/borders/borderRadiusDotted02.html +/sdcard/android/layout_tests/fast/borders/borderRadiusDotted01.html +/sdcard/android/layout_tests/fast/borders/borderRadiusDashed03.html +/sdcard/android/layout_tests/fast/borders/borderRadiusDashed02.html +/sdcard/android/layout_tests/fast/borders/borderRadiusDashed01.html +/sdcard/android/layout_tests/fast/borders/borderRadiusArcs01.html +/sdcard/android/layout_tests/fast/borders/borderRadiusAllStylesAllCorners.html +/sdcard/android/layout_tests/fast/borders/border-radius-huge-assert.html +/sdcard/android/layout_tests/fast/borders/border-image-scale-transform.html +/sdcard/android/layout_tests/fast/borders/border-image-rotate-transform.html +/sdcard/android/layout_tests/fast/borders/border-image-omit-right-slice.html +/sdcard/android/layout_tests/fast/borders/border-image-01.html +/sdcard/android/layout_tests/fast/borders/border-fit.html +/sdcard/android/layout_tests/fast/borders/border-color-inherit.html +/sdcard/android/layout_tests/fast/borders/block-mask-overlay-image.html +/sdcard/android/layout_tests/fast/body-propagation/overflow/007.html +/sdcard/android/layout_tests/fast/body-propagation/overflow/006.html +/sdcard/android/layout_tests/fast/body-propagation/overflow/005.html +/sdcard/android/layout_tests/fast/body-propagation/overflow/004.html +/sdcard/android/layout_tests/fast/body-propagation/overflow/003.html +/sdcard/android/layout_tests/fast/body-propagation/overflow/002.html +/sdcard/android/layout_tests/fast/body-propagation/overflow/001.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/010.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/009.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/008.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/007.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/006.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/005.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/004.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/003.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/002.html +/sdcard/android/layout_tests/fast/body-propagation/background-image/001.html +/sdcard/android/layout_tests/fast/body-propagation/background-color/008.html +/sdcard/android/layout_tests/fast/body-propagation/background-color/007.html +/sdcard/android/layout_tests/fast/body-propagation/background-color/006.html +/sdcard/android/layout_tests/fast/body-propagation/background-color/005.html +/sdcard/android/layout_tests/fast/body-propagation/background-color/004.html +/sdcard/android/layout_tests/fast/body-propagation/background-color/003.html +/sdcard/android/layout_tests/fast/body-propagation/background-color/002.html +/sdcard/android/layout_tests/fast/body-propagation/background-color/001.html +/sdcard/android/layout_tests/fast/block/positioning/auto/007.html +/sdcard/android/layout_tests/fast/block/positioning/auto/006.html +/sdcard/android/layout_tests/fast/block/positioning/auto/005.html +/sdcard/android/layout_tests/fast/block/positioning/auto/004.html +/sdcard/android/layout_tests/fast/block/positioning/auto/003.html +/sdcard/android/layout_tests/fast/block/positioning/auto/002.html +/sdcard/android/layout_tests/fast/block/positioning/auto/001.html +/sdcard/android/layout_tests/fast/block/positioning/window-height-change.html +/sdcard/android/layout_tests/fast/block/positioning/replaced-inside-fixed-top-bottom.html +/sdcard/android/layout_tests/fast/block/positioning/relayout-on-position-change.html +/sdcard/android/layout_tests/fast/block/positioning/relative-overflow-replaced.html +/sdcard/android/layout_tests/fast/block/positioning/relative-overflow-replaced-float.html +/sdcard/android/layout_tests/fast/block/positioning/relative-overflow-block.html +/sdcard/android/layout_tests/fast/block/positioning/relative-overconstrained.html +/sdcard/android/layout_tests/fast/block/positioning/pref-width-change.html +/sdcard/android/layout_tests/fast/block/positioning/padding-percent.html +/sdcard/android/layout_tests/fast/block/positioning/offsetLeft-offsetTop-borders.html +/sdcard/android/layout_tests/fast/block/positioning/negative-right-pos.html +/sdcard/android/layout_tests/fast/block/positioning/move-with-auto-width.html +/sdcard/android/layout_tests/fast/block/positioning/leftmargin-topmargin.html +/sdcard/android/layout_tests/fast/block/positioning/inline-block-relposition.html +/sdcard/android/layout_tests/fast/block/positioning/height-change.html +/sdcard/android/layout_tests/fast/block/positioning/complex-percentage-height.html +/sdcard/android/layout_tests/fast/block/positioning/child-of-absolute-with-auto-height.html +/sdcard/android/layout_tests/fast/block/positioning/auto-height-with-top-and-bottom.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-with-html-border-strict.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-with-html-border-quirks.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-positioned-overconstrained.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-position-direction-strict.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-position-direction-quirk.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-length-of-neg-666666.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-in-inline-short-rtl.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-in-inline-short-ltr.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-in-inline-rtl.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-in-inline-rtl-3.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-in-inline-rtl-2.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-in-inline-ltr.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-in-inline-ltr-3.html +/sdcard/android/layout_tests/fast/block/positioning/absolute-in-inline-ltr-2.html +/sdcard/android/layout_tests/fast/block/positioning/abs-inside-inline-rel.html +/sdcard/android/layout_tests/fast/block/positioning/062.html +/sdcard/android/layout_tests/fast/block/positioning/061.html +/sdcard/android/layout_tests/fast/block/positioning/060.html +/sdcard/android/layout_tests/fast/block/positioning/059.html +/sdcard/android/layout_tests/fast/block/positioning/058.html +/sdcard/android/layout_tests/fast/block/positioning/057.html +/sdcard/android/layout_tests/fast/block/positioning/056.html +/sdcard/android/layout_tests/fast/block/positioning/055.html +/sdcard/android/layout_tests/fast/block/positioning/054.html +/sdcard/android/layout_tests/fast/block/positioning/053.html +/sdcard/android/layout_tests/fast/block/positioning/052.html +/sdcard/android/layout_tests/fast/block/positioning/051.html +/sdcard/android/layout_tests/fast/block/positioning/050.html +/sdcard/android/layout_tests/fast/block/positioning/049.html +/sdcard/android/layout_tests/fast/block/positioning/048.html +/sdcard/android/layout_tests/fast/block/positioning/047.html +/sdcard/android/layout_tests/fast/block/positioning/046.html +/sdcard/android/layout_tests/fast/block/positioning/045.html +/sdcard/android/layout_tests/fast/block/positioning/044.html +/sdcard/android/layout_tests/fast/block/positioning/043.html +/sdcard/android/layout_tests/fast/block/positioning/042.html +/sdcard/android/layout_tests/fast/block/positioning/041.html +/sdcard/android/layout_tests/fast/block/positioning/040.html +/sdcard/android/layout_tests/fast/block/positioning/039.html +/sdcard/android/layout_tests/fast/block/positioning/038.html +/sdcard/android/layout_tests/fast/block/positioning/037.html +/sdcard/android/layout_tests/fast/block/positioning/036.html +/sdcard/android/layout_tests/fast/block/positioning/035.html +/sdcard/android/layout_tests/fast/block/positioning/034.html +/sdcard/android/layout_tests/fast/block/positioning/033.html +/sdcard/android/layout_tests/fast/block/positioning/032.html +/sdcard/android/layout_tests/fast/block/positioning/031.html +/sdcard/android/layout_tests/fast/block/positioning/030.html +/sdcard/android/layout_tests/fast/block/positioning/029.html +/sdcard/android/layout_tests/fast/block/positioning/028.html +/sdcard/android/layout_tests/fast/block/positioning/027.html +/sdcard/android/layout_tests/fast/block/positioning/026.html +/sdcard/android/layout_tests/fast/block/positioning/025.html +/sdcard/android/layout_tests/fast/block/positioning/024.html +/sdcard/android/layout_tests/fast/block/positioning/023.html +/sdcard/android/layout_tests/fast/block/positioning/022.html +/sdcard/android/layout_tests/fast/block/positioning/021.html +/sdcard/android/layout_tests/fast/block/positioning/020.html +/sdcard/android/layout_tests/fast/block/positioning/019.html +/sdcard/android/layout_tests/fast/block/positioning/018.html +/sdcard/android/layout_tests/fast/block/positioning/017.html +/sdcard/android/layout_tests/fast/block/positioning/016.html +/sdcard/android/layout_tests/fast/block/positioning/015.html +/sdcard/android/layout_tests/fast/block/positioning/014.html +/sdcard/android/layout_tests/fast/block/positioning/013.html +/sdcard/android/layout_tests/fast/block/positioning/012.html +/sdcard/android/layout_tests/fast/block/positioning/011.html +/sdcard/android/layout_tests/fast/block/positioning/010.html +/sdcard/android/layout_tests/fast/block/positioning/009.html +/sdcard/android/layout_tests/fast/block/positioning/008.html +/sdcard/android/layout_tests/fast/block/positioning/007.html +/sdcard/android/layout_tests/fast/block/positioning/006.html +/sdcard/android/layout_tests/fast/block/positioning/005.html +/sdcard/android/layout_tests/fast/block/positioning/004.html +/sdcard/android/layout_tests/fast/block/positioning/003.html +/sdcard/android/layout_tests/fast/block/positioning/002.html +/sdcard/android/layout_tests/fast/block/positioning/001.html +/sdcard/android/layout_tests/fast/block/margin-collapse/negative-margins.html +/sdcard/android/layout_tests/fast/block/margin-collapse/empty-clear-blocks.html +/sdcard/android/layout_tests/fast/block/margin-collapse/104.html +/sdcard/android/layout_tests/fast/block/margin-collapse/103.html +/sdcard/android/layout_tests/fast/block/margin-collapse/102.html +/sdcard/android/layout_tests/fast/block/margin-collapse/101.html +/sdcard/android/layout_tests/fast/block/margin-collapse/100.html +/sdcard/android/layout_tests/fast/block/margin-collapse/063.html +/sdcard/android/layout_tests/fast/block/margin-collapse/062.html +/sdcard/android/layout_tests/fast/block/margin-collapse/059.html +/sdcard/android/layout_tests/fast/block/margin-collapse/058.html +/sdcard/android/layout_tests/fast/block/margin-collapse/057.html +/sdcard/android/layout_tests/fast/block/margin-collapse/056.html +/sdcard/android/layout_tests/fast/block/margin-collapse/055.html +/sdcard/android/layout_tests/fast/block/margin-collapse/045.html +/sdcard/android/layout_tests/fast/block/margin-collapse/044.html +/sdcard/android/layout_tests/fast/block/margin-collapse/043.html +/sdcard/android/layout_tests/fast/block/margin-collapse/042.html +/sdcard/android/layout_tests/fast/block/margin-collapse/041.html +/sdcard/android/layout_tests/fast/block/margin-collapse/040.html +/sdcard/android/layout_tests/fast/block/margin-collapse/039.html +/sdcard/android/layout_tests/fast/block/margin-collapse/038.html +/sdcard/android/layout_tests/fast/block/margin-collapse/037.html +/sdcard/android/layout_tests/fast/block/margin-collapse/035.html +/sdcard/android/layout_tests/fast/block/margin-collapse/034.html +/sdcard/android/layout_tests/fast/block/margin-collapse/033.html +/sdcard/android/layout_tests/fast/block/margin-collapse/032.html +/sdcard/android/layout_tests/fast/block/margin-collapse/031.html +/sdcard/android/layout_tests/fast/block/margin-collapse/030.html +/sdcard/android/layout_tests/fast/block/margin-collapse/029.html +/sdcard/android/layout_tests/fast/block/margin-collapse/028.html +/sdcard/android/layout_tests/fast/block/margin-collapse/027.html +/sdcard/android/layout_tests/fast/block/margin-collapse/026.html +/sdcard/android/layout_tests/fast/block/margin-collapse/025.html +/sdcard/android/layout_tests/fast/block/margin-collapse/022.html +/sdcard/android/layout_tests/fast/block/margin-collapse/021.html +/sdcard/android/layout_tests/fast/block/margin-collapse/020.html +/sdcard/android/layout_tests/fast/block/margin-collapse/019.html +/sdcard/android/layout_tests/fast/block/margin-collapse/018.html +/sdcard/android/layout_tests/fast/block/margin-collapse/017.html +/sdcard/android/layout_tests/fast/block/margin-collapse/016.html +/sdcard/android/layout_tests/fast/block/margin-collapse/015.html +/sdcard/android/layout_tests/fast/block/margin-collapse/012.html +/sdcard/android/layout_tests/fast/block/margin-collapse/011.html +/sdcard/android/layout_tests/fast/block/margin-collapse/010.html +/sdcard/android/layout_tests/fast/block/margin-collapse/006.html +/sdcard/android/layout_tests/fast/block/margin-collapse/005.html +/sdcard/android/layout_tests/fast/block/margin-collapse/004.html +/sdcard/android/layout_tests/fast/block/margin-collapse/003.html +/sdcard/android/layout_tests/fast/block/margin-collapse/002.html +/sdcard/android/layout_tests/fast/block/margin-collapse/001.html +/sdcard/android/layout_tests/fast/block/float/width-update-after-clear.html +/sdcard/android/layout_tests/fast/block/float/vertical-move-relayout.html +/sdcard/android/layout_tests/fast/block/float/tableshifting.html +/sdcard/android/layout_tests/fast/block/float/table-relayout.html +/sdcard/android/layout_tests/fast/block/float/shrink-to-fit-width.html +/sdcard/android/layout_tests/fast/block/float/relative-painted-twice.html +/sdcard/android/layout_tests/fast/block/float/overhanging-after-height-decrease.html +/sdcard/android/layout_tests/fast/block/float/overhanging-after-height-decrease-offsets.html +/sdcard/android/layout_tests/fast/block/float/nowrap-clear-min-width.html +/sdcard/android/layout_tests/fast/block/float/nopaint-after-layer-destruction2.html +/sdcard/android/layout_tests/fast/block/float/nopaint-after-layer-destruction.html +/sdcard/android/layout_tests/fast/block/float/nestedAnonymousBlocks2.html +/sdcard/android/layout_tests/fast/block/float/nestedAnonymousBlocks.html +/sdcard/android/layout_tests/fast/block/float/negative-margin-clear.html +/sdcard/android/layout_tests/fast/block/float/narrow-after-wide.html +/sdcard/android/layout_tests/fast/block/float/multiple-float-positioning.html +/sdcard/android/layout_tests/fast/block/float/marquee-shrink-to-avoid-floats.html +/sdcard/android/layout_tests/fast/block/float/intruding-painted-twice.html +/sdcard/android/layout_tests/fast/block/float/independent-align-positioning.html +/sdcard/android/layout_tests/fast/block/float/float-on-zero-height-line.html +/sdcard/android/layout_tests/fast/block/float/float-in-float-painting.html +/sdcard/android/layout_tests/fast/block/float/float-in-float-hit-testing.html +/sdcard/android/layout_tests/fast/block/float/float-avoidance.html +/sdcard/android/layout_tests/fast/block/float/editable-text-overlapping-float.html +/sdcard/android/layout_tests/fast/block/float/dynamic-unfloat-pref-width.html +/sdcard/android/layout_tests/fast/block/float/clamped-right-float.html +/sdcard/android/layout_tests/fast/block/float/br-with-clear.html +/sdcard/android/layout_tests/fast/block/float/br-with-clear-2.html +/sdcard/android/layout_tests/fast/block/float/4145535Crash.html +/sdcard/android/layout_tests/fast/block/float/035.html +/sdcard/android/layout_tests/fast/block/float/034.html +/sdcard/android/layout_tests/fast/block/float/033.html +/sdcard/android/layout_tests/fast/block/float/032.html +/sdcard/android/layout_tests/fast/block/float/031.html +/sdcard/android/layout_tests/fast/block/float/030.html +/sdcard/android/layout_tests/fast/block/float/029.html +/sdcard/android/layout_tests/fast/block/float/028.html +/sdcard/android/layout_tests/fast/block/float/027.html +/sdcard/android/layout_tests/fast/block/float/026.html +/sdcard/android/layout_tests/fast/block/float/025.html +/sdcard/android/layout_tests/fast/block/float/024.html +/sdcard/android/layout_tests/fast/block/float/023.html +/sdcard/android/layout_tests/fast/block/float/022.html +/sdcard/android/layout_tests/fast/block/float/021.html +/sdcard/android/layout_tests/fast/block/float/020.html +/sdcard/android/layout_tests/fast/block/float/019.html +/sdcard/android/layout_tests/fast/block/float/018.html +/sdcard/android/layout_tests/fast/block/float/017.html +/sdcard/android/layout_tests/fast/block/float/016.html +/sdcard/android/layout_tests/fast/block/float/015.html +/sdcard/android/layout_tests/fast/block/float/014.html +/sdcard/android/layout_tests/fast/block/float/013.html +/sdcard/android/layout_tests/fast/block/float/012.html +/sdcard/android/layout_tests/fast/block/float/011.html +/sdcard/android/layout_tests/fast/block/float/010.html +/sdcard/android/layout_tests/fast/block/float/009.html +/sdcard/android/layout_tests/fast/block/float/008.html +/sdcard/android/layout_tests/fast/block/float/007.html +/sdcard/android/layout_tests/fast/block/float/006.html +/sdcard/android/layout_tests/fast/block/float/005.html +/sdcard/android/layout_tests/fast/block/float/004.html +/sdcard/android/layout_tests/fast/block/float/003.html +/sdcard/android/layout_tests/fast/block/float/002.html +/sdcard/android/layout_tests/fast/block/float/001.html +/sdcard/android/layout_tests/fast/block/basic/white-space-pre-wraps.html +/sdcard/android/layout_tests/fast/block/basic/text-indent-rtl.html +/sdcard/android/layout_tests/fast/block/basic/quirk-percent-height-grandchild.html +/sdcard/android/layout_tests/fast/block/basic/quirk-height.html +/sdcard/android/layout_tests/fast/block/basic/minheight.html +/sdcard/android/layout_tests/fast/block/basic/min-pref-width-nowrap-floats.html +/sdcard/android/layout_tests/fast/block/basic/fieldset-stretch-to-legend.html +/sdcard/android/layout_tests/fast/block/basic/adding-near-anonymous-block.html +/sdcard/android/layout_tests/fast/block/basic/021.html +/sdcard/android/layout_tests/fast/block/basic/020.html +/sdcard/android/layout_tests/fast/block/basic/019.html +/sdcard/android/layout_tests/fast/block/basic/018.html +/sdcard/android/layout_tests/fast/block/basic/016.html +/sdcard/android/layout_tests/fast/block/basic/015.html +/sdcard/android/layout_tests/fast/block/basic/014.html +/sdcard/android/layout_tests/fast/block/basic/013.html +/sdcard/android/layout_tests/fast/block/basic/012.html +/sdcard/android/layout_tests/fast/block/basic/011.html +/sdcard/android/layout_tests/fast/block/basic/010.html +/sdcard/android/layout_tests/fast/block/basic/009.html +/sdcard/android/layout_tests/fast/block/basic/008.html +/sdcard/android/layout_tests/fast/block/basic/007.html +/sdcard/android/layout_tests/fast/block/basic/006.html +/sdcard/android/layout_tests/fast/block/basic/005.html +/sdcard/android/layout_tests/fast/block/basic/004.html +/sdcard/android/layout_tests/fast/block/basic/003.html +/sdcard/android/layout_tests/fast/block/basic/002.html +/sdcard/android/layout_tests/fast/block/basic/001.html +/sdcard/android/layout_tests/fast/backgrounds/size/zero.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize19.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize18.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize17.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize16.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize15.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize14.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize13.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize12.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize11.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize10.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize09.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize08.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize07.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize06.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize05.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize04.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize03.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize02.html +/sdcard/android/layout_tests/fast/backgrounds/size/backgroundSize01.html +/sdcard/android/layout_tests/fast/backgrounds/repeat/noRepeatCorrectClip.html +/sdcard/android/layout_tests/fast/backgrounds/repeat/negative-offset-repeat.html +/sdcard/android/layout_tests/fast/backgrounds/repeat/negative-offset-repeat-transformed.html +/sdcard/android/layout_tests/fast/backgrounds/repeat/mask-negative-offset-repeat.html +/sdcard/android/layout_tests/fast/backgrounds/svg-as-mask.html +/sdcard/android/layout_tests/fast/backgrounds/svg-as-background-6.html +/sdcard/android/layout_tests/fast/backgrounds/svg-as-background-5.html +/sdcard/android/layout_tests/fast/backgrounds/svg-as-background-4.html +/sdcard/android/layout_tests/fast/backgrounds/svg-as-background-3.html +/sdcard/android/layout_tests/fast/backgrounds/svg-as-background-2.html +/sdcard/android/layout_tests/fast/backgrounds/svg-as-background-1.html +/sdcard/android/layout_tests/fast/backgrounds/solid-color-context-restore.html +/sdcard/android/layout_tests/fast/backgrounds/mask-composite.html +/sdcard/android/layout_tests/fast/backgrounds/bgCompositeCopy.html +/sdcard/android/layout_tests/fast/backgrounds/background-position-rounding.html +/sdcard/android/layout_tests/fast/backgrounds/background-position-1.html +/sdcard/android/layout_tests/fast/backgrounds/background-origin-root-element.html +/sdcard/android/layout_tests/fast/backgrounds/background-inherit-color-bug.html +/sdcard/android/layout_tests/fast/backgrounds/001.html diff --git a/tests/DumpRenderTree/results/layout_tests_passed.txt b/tests/DumpRenderTree/results/layout_tests_passed.txt new file mode 100644 index 0000000000000..03e920e767660 --- /dev/null +++ b/tests/DumpRenderTree/results/layout_tests_passed.txt @@ -0,0 +1,990 @@ +/sdcard/android/layout_tests/fast/transforms/container-transform-crash.html +/sdcard/android/layout_tests/fast/tokenizer/write-unclosed-script.html +/sdcard/android/layout_tests/fast/tokenizer/write-partial-entity.html +/sdcard/android/layout_tests/fast/tokenizer/write-inline-script-open.html +/sdcard/android/layout_tests/fast/tokenizer/write-external-script-open.html +/sdcard/android/layout_tests/fast/tokenizer/nested-multiple-scripts.html +/sdcard/android/layout_tests/fast/tokenizer/nested-cached-scripts.html +/sdcard/android/layout_tests/fast/tokenizer/lessthan-terminates-tags-and-attrs.html +/sdcard/android/layout_tests/fast/tokenizer/image-empty-crash.html +/sdcard/android/layout_tests/fast/tokenizer/ignore-tags-in-iframe.html +/sdcard/android/layout_tests/fast/tokenizer/external-script-document-open.html +/sdcard/android/layout_tests/fast/tokenizer/doctype-search-reset.html +/sdcard/android/layout_tests/fast/tokenizer/badscript.html +/sdcard/android/layout_tests/fast/tokenizer/ampersand-in-special-tag.html +/sdcard/android/layout_tests/fast/tokenizer/004.html +/sdcard/android/layout_tests/fast/text/line-breaks-after-ideographic-comma-or-full-stop.html +/sdcard/android/layout_tests/fast/text/large-text-composed-char-dos.html +/sdcard/android/layout_tests/fast/text/find-case-folding.html +/sdcard/android/layout_tests/fast/table/td-display-nowrap.html +/sdcard/android/layout_tests/fast/table/section-in-table-before-misnested-text-crash-css.html +/sdcard/android/layout_tests/fast/table/rowindex-comment-nodes.html +/sdcard/android/layout_tests/fast/table/row-in-tbody-before-misnested-text-crash-css.html +/sdcard/android/layout_tests/fast/table/large-rowspan-crash.html +/sdcard/android/layout_tests/fast/table/incomplete-table-in-fragment-hang.html +/sdcard/android/layout_tests/fast/table/incomplete-table-in-fragment-2.html +/sdcard/android/layout_tests/fast/table/form-in-tbody-before-misnested-text-crash-css.html +/sdcard/android/layout_tests/fast/table/form-in-table-before-misnested-text-crash-css.html +/sdcard/android/layout_tests/fast/table/form-in-row-before-misnested-text-crash-css.html +/sdcard/android/layout_tests/fast/table/empty-auto-column-zero-divide.html +/sdcard/android/layout_tests/fast/table/destroy-cell-with-selection-crash.html +/sdcard/android/layout_tests/fast/table/colgroup-relative.html +/sdcard/android/layout_tests/fast/table/cell-in-row-before-misnested-text-crash-css.html +/sdcard/android/layout_tests/fast/table/border-changes.html +/sdcard/android/layout_tests/fast/replaced/object-param-no-name.html +/sdcard/android/layout_tests/fast/regex/test4.html +/sdcard/android/layout_tests/fast/regex/test1.html +/sdcard/android/layout_tests/fast/regex/slow.html +/sdcard/android/layout_tests/fast/regex/early-acid3-86.html +/sdcard/android/layout_tests/fast/reflections/teardown-crash.html +/sdcard/android/layout_tests/fast/reflections/reflection-computed-style.html +/sdcard/android/layout_tests/fast/parser/test-unicode-characters-in-attribute-name.html +/sdcard/android/layout_tests/fast/parser/tag-with-exclamation-point.html +/sdcard/android/layout_tests/fast/parser/strict-img-in-map.html +/sdcard/android/layout_tests/fast/parser/script-after-frameset-assert.html +/sdcard/android/layout_tests/fast/parser/rewrite-map.html +/sdcard/android/layout_tests/fast/parser/rewrite-form.html +/sdcard/android/layout_tests/fast/parser/residual-style-close-across-removed-block.html +/sdcard/android/layout_tests/fast/parser/residual-style-close-across-n-blocks.html +/sdcard/android/layout_tests/fast/parser/remove-parser-current-node.html +/sdcard/android/layout_tests/fast/parser/remove-node-stack.html +/sdcard/android/layout_tests/fast/parser/remove-current-node-parent.html +/sdcard/android/layout_tests/fast/parser/pre-first-line-break.html +/sdcard/android/layout_tests/fast/parser/parse-wbr.html +/sdcard/android/layout_tests/fast/parser/p-in-scope.html +/sdcard/android/layout_tests/fast/parser/p-in-scope-strict.html +/sdcard/android/layout_tests/fast/parser/open-comment-in-script-tricky.html +/sdcard/android/layout_tests/fast/parser/number-sign-in-map-name.html +/sdcard/android/layout_tests/fast/parser/nsup-entity.html +/sdcard/android/layout_tests/fast/parser/input-textarea-inside-select-element.html +/sdcard/android/layout_tests/fast/parser/html-whitespace.html +/sdcard/android/layout_tests/fast/parser/hex-entities-length.html +/sdcard/android/layout_tests/fast/parser/head-comment.html +/sdcard/android/layout_tests/fast/parser/entity-surrogate-pairs.html +/sdcard/android/layout_tests/fast/parser/entity-end-xmp-tag.html +/sdcard/android/layout_tests/fast/parser/entity-end-title-tag.html +/sdcard/android/layout_tests/fast/parser/entity-end-textarea-tag.html +/sdcard/android/layout_tests/fast/parser/entity-end-style-tag.html +/sdcard/android/layout_tests/fast/parser/entity-end-iframe-tag.html +/sdcard/android/layout_tests/fast/parser/entity-comment-in-title.html +/sdcard/android/layout_tests/fast/parser/entity-comment-in-script-tricky.html +/sdcard/android/layout_tests/fast/parser/entities-in-html.html +/sdcard/android/layout_tests/fast/parser/duplicate-html-body-element-IDs.html +/sdcard/android/layout_tests/fast/parser/comment-in-title.html +/sdcard/android/layout_tests/fast/parser/comment-in-script-tricky.html +/sdcard/android/layout_tests/fast/parser/assertion-empty-attribute.html +/sdcard/android/layout_tests/fast/parser/area-in-div.html +/sdcard/android/layout_tests/fast/overflow/onscroll-layer-self-destruct.html +/sdcard/android/layout_tests/fast/overflow/generated-content-crash.html +/sdcard/android/layout_tests/fast/multicol/gap-non-negative.html +/sdcard/android/layout_tests/fast/multicol/content-height-zero-crash.html +/sdcard/android/layout_tests/fast/loader/xmlhttprequest-bad-mimetype.html +/sdcard/android/layout_tests/fast/loader/window-clearing.html +/sdcard/android/layout_tests/fast/loader/user-style-sheet-resource-load-callbacks.html +/sdcard/android/layout_tests/fast/loader/url-strip-cr-lf-tab.html +/sdcard/android/layout_tests/fast/loader/url-parse-1.html +/sdcard/android/layout_tests/fast/loader/url-data-replace-backslash.html +/sdcard/android/layout_tests/fast/loader/unloadable-script.html +/sdcard/android/layout_tests/fast/loader/simultaneous-reloads-assert.html +/sdcard/android/layout_tests/fast/loader/redirect-with-open-subframe.html +/sdcard/android/layout_tests/fast/loader/redirect-with-open-subframe-2.html +/sdcard/android/layout_tests/fast/loader/meta-refresh-vs-open.html +/sdcard/android/layout_tests/fast/loader/local-css-allowed-in-strict-mode.html +/sdcard/android/layout_tests/fast/loader/loadInProgress.html +/sdcard/android/layout_tests/fast/loader/link-no-URL.html +/sdcard/android/layout_tests/fast/loader/javascript-url-encoding.html +/sdcard/android/layout_tests/fast/loader/invalid-charset-on-script-crashes-loader.html +/sdcard/android/layout_tests/fast/loader/inherit-charset-to-empty-frame.html +/sdcard/android/layout_tests/fast/loader/iframe-recursive-synchronous-load.html +/sdcard/android/layout_tests/fast/loader/font-face-empty.html +/sdcard/android/layout_tests/fast/loader/file-URL-with-port-number.html +/sdcard/android/layout_tests/fast/loader/external-script-URL-location.html +/sdcard/android/layout_tests/fast/loader/empty-ref-versus-no-ref.html +/sdcard/android/layout_tests/fast/loader/early-load-cancel.html +/sdcard/android/layout_tests/fast/loader/data-url-encoding-html.html +/sdcard/android/layout_tests/fast/loader/charset-parse.html +/sdcard/android/layout_tests/fast/leaks/002.html +/sdcard/android/layout_tests/fast/leaks/001.html +/sdcard/android/layout_tests/fast/layers/resize-layer-deletion-crash.html +/sdcard/android/layout_tests/fast/layers/removed-by-scroll-handler.html +/sdcard/android/layout_tests/fast/layers/generated-layer-scrollbar-crash.html +/sdcard/android/layout_tests/fast/js/pic/rehash-poisons-structure.html +/sdcard/android/layout_tests/fast/js/pic/get-set-proxy-object.html +/sdcard/android/layout_tests/fast/js/pic/get-empty-string.html +/sdcard/android/layout_tests/fast/js/pic/dictionary-prototype.html +/sdcard/android/layout_tests/fast/js/pic/cached-single-entry-transition.html +/sdcard/android/layout_tests/fast/js/pic/cached-prototype-setter.html +/sdcard/android/layout_tests/fast/js/pic/cached-getter-setter.html +/sdcard/android/layout_tests/fast/js/pic/cached-getter-dictionary-and-proto.html +/sdcard/android/layout_tests/fast/js/pic/cached-deleted-properties.html +/sdcard/android/layout_tests/fast/js/window-location-href-file-urls.html +/sdcard/android/layout_tests/fast/js/while-expression-value.html +/sdcard/android/layout_tests/fast/js/vardecl-preserve-vardecl.html +/sdcard/android/layout_tests/fast/js/vardecl-preserve-parameters.html +/sdcard/android/layout_tests/fast/js/vardecl-preserve-arguments.html +/sdcard/android/layout_tests/fast/js/vardecl-blocks-init.html +/sdcard/android/layout_tests/fast/js/var-shadows-arg-crash.html +/sdcard/android/layout_tests/fast/js/var-declarations.html +/sdcard/android/layout_tests/fast/js/var-declarations-shadowing.html +/sdcard/android/layout_tests/fast/js/unmatching-argument-count.html +/sdcard/android/layout_tests/fast/js/unexpected-constant-crash.html +/sdcard/android/layout_tests/fast/js/typeof-syntax.html +/sdcard/android/layout_tests/fast/js/typeof-codegen-crash.html +/sdcard/android/layout_tests/fast/js/toString-try-else.html +/sdcard/android/layout_tests/fast/js/toString-prefix-postfix-preserve-parens.html +/sdcard/android/layout_tests/fast/js/toString-overrides.html +/sdcard/android/layout_tests/fast/js/toString-number-dot-expr.html +/sdcard/android/layout_tests/fast/js/toString-for-var-decl.html +/sdcard/android/layout_tests/fast/js/toString-exception.html +/sdcard/android/layout_tests/fast/js/tostring-exception-in-property-access.html +/sdcard/android/layout_tests/fast/js/toString-elision-trailing-comma.html +/sdcard/android/layout_tests/fast/js/toString-dontEnum.html +/sdcard/android/layout_tests/fast/js/throw-from-array-sort.html +/sdcard/android/layout_tests/fast/js/this-non-object-proto.html +/sdcard/android/layout_tests/fast/js/switch-behaviour.html +/sdcard/android/layout_tests/fast/js/string_replace.html +/sdcard/android/layout_tests/fast/js/string-substr.html +/sdcard/android/layout_tests/fast/js/string-split-ignore-case.html +/sdcard/android/layout_tests/fast/js/string-sort.html +/sdcard/android/layout_tests/fast/js/string-slice-abnormal-values.html +/sdcard/android/layout_tests/fast/js/string-replace-exception-crash.html +/sdcard/android/layout_tests/fast/js/string-replace-3.html +/sdcard/android/layout_tests/fast/js/string-replace-2.html +/sdcard/android/layout_tests/fast/js/string-property-iteration.html +/sdcard/android/layout_tests/fast/js/string-index-overflow.html +/sdcard/android/layout_tests/fast/js/string-from-char-code.html +/sdcard/android/layout_tests/fast/js/string-capitalization.html +/sdcard/android/layout_tests/fast/js/static-scope-object.html +/sdcard/android/layout_tests/fast/js/statement-list-register-crash.html +/sdcard/android/layout_tests/fast/js/stack-unwinding.html +/sdcard/android/layout_tests/fast/js/sparse-array.html +/sdcard/android/layout_tests/fast/js/sort-stability.html +/sdcard/android/layout_tests/fast/js/sort-randomly.html +/sdcard/android/layout_tests/fast/js/sort-large-array.html +/sdcard/android/layout_tests/fast/js/slash-lineterminator-parse.html +/sdcard/android/layout_tests/fast/js/select-options-remove.html +/sdcard/android/layout_tests/fast/js/select-options-remove-gc.html +/sdcard/android/layout_tests/fast/js/select-options-add.html +/sdcard/android/layout_tests/fast/js/resize-array-assign.html +/sdcard/android/layout_tests/fast/js/reserved-words.html +/sdcard/android/layout_tests/fast/js/removing-Cf-characters.html +/sdcard/android/layout_tests/fast/js/rehash-assign.html +/sdcard/android/layout_tests/fast/js/regexp-unicode-overflow.html +/sdcard/android/layout_tests/fast/js/regexp-unicode-handling.html +/sdcard/android/layout_tests/fast/js/regexp-stack-overflow.html +/sdcard/android/layout_tests/fast/js/regexp-ranges-and-escaped-hyphens.html +/sdcard/android/layout_tests/fast/js/regexp-range-out-of-order.html +/sdcard/android/layout_tests/fast/js/regexp-overflow.html +/sdcard/android/layout_tests/fast/js/regexp-non-character.html +/sdcard/android/layout_tests/fast/js/regexp-non-capturing-groups.html +/sdcard/android/layout_tests/fast/js/regexp-non-bmp.html +/sdcard/android/layout_tests/fast/js/regexp-no-extensions.html +/sdcard/android/layout_tests/fast/js/regexp-negative-special-characters.html +/sdcard/android/layout_tests/fast/js/regexp-many-brackets.html +/sdcard/android/layout_tests/fast/js/regexp-lastindex.html +/sdcard/android/layout_tests/fast/js/regexp-find-first-asserted.html +/sdcard/android/layout_tests/fast/js/regexp-extended-characters-more.html +/sdcard/android/layout_tests/fast/js/regexp-extended-characters-match.html +/sdcard/android/layout_tests/fast/js/regexp-extended-characters-crash.html +/sdcard/android/layout_tests/fast/js/regexp-divequal.html +/sdcard/android/layout_tests/fast/js/regexp-compile.html +/sdcard/android/layout_tests/fast/js/regexp-compile-crash.html +/sdcard/android/layout_tests/fast/js/regexp-char-insensitive.html +/sdcard/android/layout_tests/fast/js/regexp-caching.html +/sdcard/android/layout_tests/fast/js/reentrant-call-unwind.html +/sdcard/android/layout_tests/fast/js/read-modify-eval.html +/sdcard/android/layout_tests/fast/js/propertyIsEnumerable.html +/sdcard/android/layout_tests/fast/js/property-getters-and-setters.html +/sdcard/android/layout_tests/fast/js/primitive-method-this.html +/sdcard/android/layout_tests/fast/js/pretty-print.html +/sdcard/android/layout_tests/fast/js/prefix-syntax.html +/sdcard/android/layout_tests/fast/js/postfix-syntax.html +/sdcard/android/layout_tests/fast/js/parse-backslash-before-newline.html +/sdcard/android/layout_tests/fast/js/order-of-operations.html +/sdcard/android/layout_tests/fast/js/object-prototype-toLocaleString.html +/sdcard/android/layout_tests/fast/js/object-prototype-constructor.html +/sdcard/android/layout_tests/fast/js/object-extra-comma.html +/sdcard/android/layout_tests/fast/js/numeric-conversion.html +/sdcard/android/layout_tests/fast/js/number-toString.html +/sdcard/android/layout_tests/fast/js/number-toprecision.html +/sdcard/android/layout_tests/fast/js/number-tofixed.html +/sdcard/android/layout_tests/fast/js/number-toExponential.html +/sdcard/android/layout_tests/fast/js/null-char-in-string.html +/sdcard/android/layout_tests/fast/js/non-object-proto.html +/sdcard/android/layout_tests/fast/js/nested-function-scope.html +/sdcard/android/layout_tests/fast/js/navigator-plugins-crash.html +/sdcard/android/layout_tests/fast/js/named-function-expression.html +/sdcard/android/layout_tests/fast/js/modify-non-references.html +/sdcard/android/layout_tests/fast/js/mod-crash.html +/sdcard/android/layout_tests/fast/js/missing-title-end-tag-js.html +/sdcard/android/layout_tests/fast/js/math.html +/sdcard/android/layout_tests/fast/js/logical-or-jless.html +/sdcard/android/layout_tests/fast/js/lexical-lookup-in-function-constructor.html +/sdcard/android/layout_tests/fast/js/lastModified.html +/sdcard/android/layout_tests/fast/js/isPrototypeOf.html +/sdcard/android/layout_tests/fast/js/invalid-syntax-for-function.html +/sdcard/android/layout_tests/fast/js/integer-extremes.html +/sdcard/android/layout_tests/fast/js/implicit-global-to-global-reentry.html +/sdcard/android/layout_tests/fast/js/implicit-call-with-global-reentry.html +/sdcard/android/layout_tests/fast/js/has-own-property.html +/sdcard/android/layout_tests/fast/js/gmail-re-re.html +/sdcard/android/layout_tests/fast/js/global-var-limit.html +/sdcard/android/layout_tests/fast/js/getter-setter-gc.html +/sdcard/android/layout_tests/fast/js/function-toString-parentheses.html +/sdcard/android/layout_tests/fast/js/function-toString-object-literals.html +/sdcard/android/layout_tests/fast/js/function-redefinition.html +/sdcard/android/layout_tests/fast/js/function-prototype.html +/sdcard/android/layout_tests/fast/js/function-names.html +/sdcard/android/layout_tests/fast/js/function-name.html +/sdcard/android/layout_tests/fast/js/function-dot-arguments.html +/sdcard/android/layout_tests/fast/js/function-dot-arguments-and-caller.html +/sdcard/android/layout_tests/fast/js/function-decompilation-operators.html +/sdcard/android/layout_tests/fast/js/function-declarations.html +/sdcard/android/layout_tests/fast/js/function-declarations-in-switch-statement.html +/sdcard/android/layout_tests/fast/js/function-call-register-allocation.html +/sdcard/android/layout_tests/fast/js/function-argument-evaluation-before-exception.html +/sdcard/android/layout_tests/fast/js/function-apply.html +/sdcard/android/layout_tests/fast/js/for-in-var-scope.html +/sdcard/android/layout_tests/fast/js/for-in-to-text.html +/sdcard/android/layout_tests/fast/js/for-in-exeception.html +/sdcard/android/layout_tests/fast/js/for-in-avoid-duplicates.html +/sdcard/android/layout_tests/fast/js/finally-codegen-failure.html +/sdcard/android/layout_tests/fast/js/exec-state-marking.html +/sdcard/android/layout_tests/fast/js/exception-try-finally-scope-error.html +/sdcard/android/layout_tests/fast/js/exception-thrown-from-new.html +/sdcard/android/layout_tests/fast/js/exception-thrown-from-function-with-lazy-activation.html +/sdcard/android/layout_tests/fast/js/exception-thrown-from-eval-inside-closure.html +/sdcard/android/layout_tests/fast/js/exception-thrown-from-equal.html +/sdcard/android/layout_tests/fast/js/exception-linenums.html +/sdcard/android/layout_tests/fast/js/exception-linenums-in-html-2.html +/sdcard/android/layout_tests/fast/js/exception-linenums-in-html-1.html +/sdcard/android/layout_tests/fast/js/exception-expression-offset.html +/sdcard/android/layout_tests/fast/js/eval-var-decl.html +/sdcard/android/layout_tests/fast/js/eval-overriding.html +/sdcard/android/layout_tests/fast/js/eval-keyword-vs-function.html +/sdcard/android/layout_tests/fast/js/eval-cross-window.html +/sdcard/android/layout_tests/fast/js/eval-cache-crash.html +/sdcard/android/layout_tests/fast/js/equality.html +/sdcard/android/layout_tests/fast/js/encode-URI-test.html +/sdcard/android/layout_tests/fast/js/duplicate-param-crash.html +/sdcard/android/layout_tests/fast/js/dot-node-base-exception.html +/sdcard/android/layout_tests/fast/js/do-while-without-semicolon.html +/sdcard/android/layout_tests/fast/js/do-while-semicolon.html +/sdcard/android/layout_tests/fast/js/do-while-expression-value.html +/sdcard/android/layout_tests/fast/js/direct-entry-to-function-code.html +/sdcard/android/layout_tests/fast/js/delete-then-put.html +/sdcard/android/layout_tests/fast/js/delete-syntax.html +/sdcard/android/layout_tests/fast/js/delete-multiple-global-blocks.html +/sdcard/android/layout_tests/fast/js/delete-getters-setters.html +/sdcard/android/layout_tests/fast/js/delete-function-parameter.html +/sdcard/android/layout_tests/fast/js/deep-recursion-test.html +/sdcard/android/layout_tests/fast/js/declaration-in-block.html +/sdcard/android/layout_tests/fast/js/debugger.html +/sdcard/android/layout_tests/fast/js/date-set-to-nan.html +/sdcard/android/layout_tests/fast/js/date-proto-generic-invocation.html +/sdcard/android/layout_tests/fast/js/date-preserve-milliseconds.html +/sdcard/android/layout_tests/fast/js/date-parse-test.html +/sdcard/android/layout_tests/fast/js/date-parse-comments-test.html +/sdcard/android/layout_tests/fast/js/date-negative-setmonth.html +/sdcard/android/layout_tests/fast/js/date-DST-time-cusps.html +/sdcard/android/layout_tests/fast/js/date-DST-pre-1970.html +/sdcard/android/layout_tests/fast/js/date-constructor.html +/sdcard/android/layout_tests/fast/js/date-big-setmonth.html +/sdcard/android/layout_tests/fast/js/date-big-setdate.html +/sdcard/android/layout_tests/fast/js/date-big-constructor.html +/sdcard/android/layout_tests/fast/js/cyclic-ref-toString.html +/sdcard/android/layout_tests/fast/js/cyclic-prototypes.html +/sdcard/android/layout_tests/fast/js/cyclic-proto.html +/sdcard/android/layout_tests/fast/js/convert-nan-to-bool.html +/sdcard/android/layout_tests/fast/js/continue-break-multiple-labels.html +/sdcard/android/layout_tests/fast/js/constructor.html +/sdcard/android/layout_tests/fast/js/constructor-attributes.html +/sdcard/android/layout_tests/fast/js/construct-global-object.html +/sdcard/android/layout_tests/fast/js/constant-folding.html +/sdcard/android/layout_tests/fast/js/constant-count.html +/sdcard/android/layout_tests/fast/js/const.html +/sdcard/android/layout_tests/fast/js/const-without-initializer.html +/sdcard/android/layout_tests/fast/js/comparefn-sort-stability.html +/sdcard/android/layout_tests/fast/js/codegen-temporaries.html +/sdcard/android/layout_tests/fast/js/codegen-temporaries-multiple-global-blocks.html +/sdcard/android/layout_tests/fast/js/codegen-peephole-locals.html +/sdcard/android/layout_tests/fast/js/codegen-loops-logical-nodes.html +/sdcard/android/layout_tests/fast/js/code-serialize-paren.html +/sdcard/android/layout_tests/fast/js/closure-inside-extra-arg-call.html +/sdcard/android/layout_tests/fast/js/char-at.html +/sdcard/android/layout_tests/fast/js/caller-property.html +/sdcard/android/layout_tests/fast/js/bitwise-and-on-undefined.html +/sdcard/android/layout_tests/fast/js/avl-crash.html +/sdcard/android/layout_tests/fast/js/assign.html +/sdcard/android/layout_tests/fast/js/ascii-regexp-subject.html +/sdcard/android/layout_tests/fast/js/array-tostring-ignore-separator.html +/sdcard/android/layout_tests/fast/js/array-tostring-and-join.html +/sdcard/android/layout_tests/fast/js/array-splice.html +/sdcard/android/layout_tests/fast/js/array-sort-reentrance.html +/sdcard/android/layout_tests/fast/js/array-some.html +/sdcard/android/layout_tests/fast/js/array-reset-large-index.html +/sdcard/android/layout_tests/fast/js/array-map.html +/sdcard/android/layout_tests/fast/js/array-lastIndexOf.html +/sdcard/android/layout_tests/fast/js/array-join-bug-11524.html +/sdcard/android/layout_tests/fast/js/array-iterate-backwards.html +/sdcard/android/layout_tests/fast/js/array-indexof.html +/sdcard/android/layout_tests/fast/js/array-indexing.html +/sdcard/android/layout_tests/fast/js/array-index-immediate-types.html +/sdcard/android/layout_tests/fast/js/array-holes.html +/sdcard/android/layout_tests/fast/js/array-functions-non-arrays.html +/sdcard/android/layout_tests/fast/js/array-foreach.html +/sdcard/android/layout_tests/fast/js/array-float-delete.html +/sdcard/android/layout_tests/fast/js/array-filter.html +/sdcard/android/layout_tests/fast/js/array-every.html +/sdcard/android/layout_tests/fast/js/arguments.html +/sdcard/android/layout_tests/fast/js/arguments-bad-index.html +/sdcard/android/layout_tests/fast/js/activation-proto.html +/sdcard/android/layout_tests/fast/js/activation-object-function-lifetime.html +/sdcard/android/layout_tests/fast/invalid/test-case-tr-th-td-should-not-close-dl-list.html +/sdcard/android/layout_tests/fast/invalid/nestedh3s-rapidweaver.html +/sdcard/android/layout_tests/fast/inspector/cssURLQuotes.html +/sdcard/android/layout_tests/fast/innerHTML/javascript-url.html +/sdcard/android/layout_tests/fast/innerHTML/innerHTML-custom-tag.html +/sdcard/android/layout_tests/fast/innerHTML/innerHTML-case.html +/sdcard/android/layout_tests/fast/innerHTML/additional-inline-style.html +/sdcard/android/layout_tests/fast/innerHTML/005.html +/sdcard/android/layout_tests/fast/inline/clean-after-removing-temp-boxes.html +/sdcard/android/layout_tests/fast/images/text-content-crash.html +/sdcard/android/layout_tests/fast/images/text-content-crash-2.html +/sdcard/android/layout_tests/fast/images/load-img-with-empty-src.html +/sdcard/android/layout_tests/fast/images/border.html +/sdcard/android/layout_tests/fast/images/animated-background-image-crash.html +/sdcard/android/layout_tests/fast/html/xhtml-serialize.html +/sdcard/android/layout_tests/fast/html/script-allowed-types-languages.html +/sdcard/android/layout_tests/fast/html/empty-fragment-id-goto-top.html +/sdcard/android/layout_tests/fast/html/body-offset-properties.html +/sdcard/android/layout_tests/fast/frames/viewsource-plain-text-tags.html +/sdcard/android/layout_tests/fast/frames/set-unloaded-frame-location.html +/sdcard/android/layout_tests/fast/frames/repaint-display-none-crash.html +/sdcard/android/layout_tests/fast/frames/remove-frame-with-scrollbars-crash.html +/sdcard/android/layout_tests/fast/frames/onload-remove-iframe-crash.html +/sdcard/android/layout_tests/fast/frames/negative-remaining-length-crash.html +/sdcard/android/layout_tests/fast/frames/location-put-after-removal.html +/sdcard/android/layout_tests/fast/frames/location-change.html +/sdcard/android/layout_tests/fast/frames/iframe-target.html +/sdcard/android/layout_tests/fast/frames/iframe-set-same-src.html +/sdcard/android/layout_tests/fast/frames/iframe-set-same-location.html +/sdcard/android/layout_tests/fast/frames/iframe-set-inner-html.html +/sdcard/android/layout_tests/fast/frames/iframe-remove-after-id-change.html +/sdcard/android/layout_tests/fast/frames/iframe-name-and-id.html +/sdcard/android/layout_tests/fast/frames/iframe-js-url-clientWidth.html +/sdcard/android/layout_tests/fast/frames/iframe-double-attach.html +/sdcard/android/layout_tests/fast/frames/iframe-display-none.html +/sdcard/android/layout_tests/fast/frames/hover-timer-crash.html +/sdcard/android/layout_tests/fast/frames/frame-set-same-src.html +/sdcard/android/layout_tests/fast/frames/frame-set-same-location.html +/sdcard/android/layout_tests/fast/frames/frame-name-reset.html +/sdcard/android/layout_tests/fast/frames/frame-display-none-focus.html +/sdcard/android/layout_tests/fast/frames/empty-frame-document.html +/sdcard/android/layout_tests/fast/frames/cross-site-this.html +/sdcard/android/layout_tests/fast/frames/crash-removed-iframe.html +/sdcard/android/layout_tests/fast/forms/willvalidate-009.html +/sdcard/android/layout_tests/fast/forms/willvalidate-008.html +/sdcard/android/layout_tests/fast/forms/willvalidate-007.html +/sdcard/android/layout_tests/fast/forms/willvalidate-006.html +/sdcard/android/layout_tests/fast/forms/willvalidate-005.html +/sdcard/android/layout_tests/fast/forms/willvalidate-004.html +/sdcard/android/layout_tests/fast/forms/willvalidate-003.html +/sdcard/android/layout_tests/fast/forms/willvalidate-002.html +/sdcard/android/layout_tests/fast/forms/willvalidate-001.html +/sdcard/android/layout_tests/fast/forms/willvalidate-000.html +/sdcard/android/layout_tests/fast/forms/textfield-focus-out.html +/sdcard/android/layout_tests/fast/forms/textarea-trailing-newline.html +/sdcard/android/layout_tests/fast/forms/textarea-setvalue-without-renderer.html +/sdcard/android/layout_tests/fast/forms/textarea-setvalue-submit.html +/sdcard/android/layout_tests/fast/forms/textarea-scrollbar-height.html +/sdcard/android/layout_tests/fast/forms/textarea-linewrap-dynamic.html +/sdcard/android/layout_tests/fast/forms/textarea-hard-linewrap-empty.html +/sdcard/android/layout_tests/fast/forms/textarea-crlf.html +/sdcard/android/layout_tests/fast/forms/text-set-value-crash.html +/sdcard/android/layout_tests/fast/forms/tabs-with-modifiers.html +/sdcard/android/layout_tests/fast/forms/tab-in-input.html +/sdcard/android/layout_tests/fast/forms/submit-with-base.html +/sdcard/android/layout_tests/fast/forms/submit-to-url-fragment.html +/sdcard/android/layout_tests/fast/forms/submit-nil-value-field-assert.html +/sdcard/android/layout_tests/fast/forms/slow-click.html +/sdcard/android/layout_tests/fast/forms/selected-index-assert.html +/sdcard/android/layout_tests/fast/forms/select-width-font-change.html +/sdcard/android/layout_tests/fast/forms/select-type-ahead-list-box-no-selection.html +/sdcard/android/layout_tests/fast/forms/select-set-inner.html +/sdcard/android/layout_tests/fast/forms/select-reset.html +/sdcard/android/layout_tests/fast/forms/select-replace-option.html +/sdcard/android/layout_tests/fast/forms/select-remove-option.html +/sdcard/android/layout_tests/fast/forms/select-out-of-bounds-index.html +/sdcard/android/layout_tests/fast/forms/select-namedItem.html +/sdcard/android/layout_tests/fast/forms/select-list-box-mouse-focus.html +/sdcard/android/layout_tests/fast/forms/select-index-setter.html +/sdcard/android/layout_tests/fast/forms/saved-state-adoptNode-crash.html +/sdcard/android/layout_tests/fast/forms/remove-radio-button-assert.html +/sdcard/android/layout_tests/fast/forms/range-reset.html +/sdcard/android/layout_tests/fast/forms/range-default-value.html +/sdcard/android/layout_tests/fast/forms/radio_checked_name.html +/sdcard/android/layout_tests/fast/forms/radio-no-theme-padding.html +/sdcard/android/layout_tests/fast/forms/radio-check-click-and-drag.html +/sdcard/android/layout_tests/fast/forms/radio-button-no-change-event.html +/sdcard/android/layout_tests/fast/forms/paste-multiline-text-input.html +/sdcard/android/layout_tests/fast/forms/paste-into-textarea.html +/sdcard/android/layout_tests/fast/forms/option-in-optgroup-removal.html +/sdcard/android/layout_tests/fast/forms/option-constructor-selected.html +/sdcard/android/layout_tests/fast/forms/option-change-single-selected.html +/sdcard/android/layout_tests/fast/forms/old-names.html +/sdcard/android/layout_tests/fast/forms/missing-action.html +/sdcard/android/layout_tests/fast/forms/menulist-selection-reset.html +/sdcard/android/layout_tests/fast/forms/menulist-no-renderer-onmousedown.html +/sdcard/android/layout_tests/fast/forms/listbox-typeahead-empty.html +/sdcard/android/layout_tests/fast/forms/listbox-scroll-after-options-removed.html +/sdcard/android/layout_tests/fast/forms/input-zero-height-focus.html +/sdcard/android/layout_tests/fast/forms/input-type-change-in-onfocus-mouse.html +/sdcard/android/layout_tests/fast/forms/input-type-change-in-onfocus-keyboard.html +/sdcard/android/layout_tests/fast/forms/input-setvalue-selection.html +/sdcard/android/layout_tests/fast/forms/input-selection-hidden.html +/sdcard/android/layout_tests/fast/forms/input-named-action-overrides-action-attribute.html +/sdcard/android/layout_tests/fast/forms/input-changing-value.html +/sdcard/android/layout_tests/fast/forms/input-appearance-maxlength.html +/sdcard/android/layout_tests/fast/forms/input-appearance-elementFromPoint.html +/sdcard/android/layout_tests/fast/forms/HTMLOptionElement_selected.html +/sdcard/android/layout_tests/fast/forms/hidden-input-not-enabled.html +/sdcard/android/layout_tests/fast/forms/form-post-urlencoded.html +/sdcard/android/layout_tests/fast/forms/form-get-multipart3.html +/sdcard/android/layout_tests/fast/forms/form-get-multipart2.html +/sdcard/android/layout_tests/fast/forms/form-get-multipart.html +/sdcard/android/layout_tests/fast/forms/form-data-encoding.html +/sdcard/android/layout_tests/fast/forms/form-data-encoding-normalization-overrun.html +/sdcard/android/layout_tests/fast/forms/form-data-encoding-2.html +/sdcard/android/layout_tests/fast/forms/focus-style-pending.html +/sdcard/android/layout_tests/fast/forms/empty-get.html +/sdcard/android/layout_tests/fast/forms/element-order.html +/sdcard/android/layout_tests/fast/forms/element-by-name.html +/sdcard/android/layout_tests/fast/forms/double-focus.html +/sdcard/android/layout_tests/fast/forms/domstring-replace-crash.html +/sdcard/android/layout_tests/fast/forms/document-write.html +/sdcard/android/layout_tests/fast/forms/display-none-in-onchange-keyboard.html +/sdcard/android/layout_tests/fast/forms/cursor-position.html +/sdcard/android/layout_tests/fast/forms/button-in-forms-collection.html +/sdcard/android/layout_tests/fast/forms/button-click-DOM.html +/sdcard/android/layout_tests/fast/forms/autofocus-opera-008.html +/sdcard/android/layout_tests/fast/forms/autofocus-opera-007.html +/sdcard/android/layout_tests/fast/forms/autofocus-opera-006.html +/sdcard/android/layout_tests/fast/forms/autofocus-opera-005.html +/sdcard/android/layout_tests/fast/forms/autofocus-opera-004.html +/sdcard/android/layout_tests/fast/forms/autofocus-opera-002.html +/sdcard/android/layout_tests/fast/forms/autofocus-opera-001.html +/sdcard/android/layout_tests/fast/forms/autofocus-attribute.html +/sdcard/android/layout_tests/fast/forms/add-remove-form-elements-stress-test.html +/sdcard/android/layout_tests/fast/forms/activate-and-disabled-elements.html +/sdcard/android/layout_tests/fast/forms/8250.html +/sdcard/android/layout_tests/fast/forms/4628409.html +/sdcard/android/layout_tests/fast/forms/11423.html +/sdcard/android/layout_tests/fast/flexbox/inline-children-crash.html +/sdcard/android/layout_tests/fast/events/window-load-capture.html +/sdcard/android/layout_tests/fast/events/submit-reset-nested-bubble.html +/sdcard/android/layout_tests/fast/events/stopPropagation-submit.html +/sdcard/android/layout_tests/fast/events/stopPropagation-checkbox.html +/sdcard/android/layout_tests/fast/events/space-scroll-event.html +/sdcard/android/layout_tests/fast/events/simulated-key-state.html +/sdcard/android/layout_tests/fast/events/shadow-boundary-crossing.html +/sdcard/android/layout_tests/fast/events/selectstart-during-autoscroll.html +/sdcard/android/layout_tests/fast/events/resize-subframe.html +/sdcard/android/layout_tests/fast/events/remove-event-listener.html +/sdcard/android/layout_tests/fast/events/programmatic-check-no-change-event.html +/sdcard/android/layout_tests/fast/events/overflow-events.html +/sdcard/android/layout_tests/fast/events/onunload-body-property.html +/sdcard/android/layout_tests/fast/events/onsubmit-bubbling.html +/sdcard/android/layout_tests/fast/events/onload-after-document-close-with-subresource.html +/sdcard/android/layout_tests/fast/events/onload-after-document-close-no-subresource.html +/sdcard/android/layout_tests/fast/events/onerror-bubbling.html +/sdcard/android/layout_tests/fast/events/no-window-load.html +/sdcard/android/layout_tests/fast/events/no-blur-on-page-leave.html +/sdcard/android/layout_tests/fast/events/no-blur-on-enter-button.html +/sdcard/android/layout_tests/fast/events/nested-window-event.html +/sdcard/android/layout_tests/fast/events/nested-event-remove-node-crash.html +/sdcard/android/layout_tests/fast/events/mouseup-outside-button.html +/sdcard/android/layout_tests/fast/events/mousedown_in_scrollbar.html +/sdcard/android/layout_tests/fast/events/message-port.html +/sdcard/android/layout_tests/fast/events/message-port-inactive-document.html +/sdcard/android/layout_tests/fast/events/message-port-deleted-frame.html +/sdcard/android/layout_tests/fast/events/message-port-deleted-document.html +/sdcard/android/layout_tests/fast/events/message-port-constructor-for-deleted-document.html +/sdcard/android/layout_tests/fast/events/message-channel-listener-circular-ownership.html +/sdcard/android/layout_tests/fast/events/message-channel-gc.html +/sdcard/android/layout_tests/fast/events/message-channel-gc-3.html +/sdcard/android/layout_tests/fast/events/message-channel-gc-2.html +/sdcard/android/layout_tests/fast/events/keypress-removed-node.html +/sdcard/android/layout_tests/fast/events/keydown-remove-frame.html +/sdcard/android/layout_tests/fast/events/init-event-null-view.html +/sdcard/android/layout_tests/fast/events/init-event-after-dispatch.html +/sdcard/android/layout_tests/fast/events/event-targets.html +/sdcard/android/layout_tests/fast/events/event-listener-html-non-html-confusion.html +/sdcard/android/layout_tests/fast/events/event-instanceof.html +/sdcard/android/layout_tests/fast/events/event-creation.html +/sdcard/android/layout_tests/fast/events/div-focus.html +/sdcard/android/layout_tests/fast/events/dispatch-to-handle-event.html +/sdcard/android/layout_tests/fast/events/delayed-style-mutation-event-crash.html +/sdcard/android/layout_tests/fast/events/caller-access-from-event-listener.html +/sdcard/android/layout_tests/fast/events/anchor-empty-focus.html +/sdcard/android/layout_tests/fast/encoding/hebrew/logical.html +/sdcard/android/layout_tests/fast/encoding/hebrew/iso-ir-138.html +/sdcard/android/layout_tests/fast/encoding/hebrew/hebrew.html +/sdcard/android/layout_tests/fast/encoding/hebrew/csISO88598I.html +/sdcard/android/layout_tests/fast/encoding/hebrew/8859-8.html +/sdcard/android/layout_tests/fast/encoding/hebrew/8859-8-i.html +/sdcard/android/layout_tests/fast/encoding/hebrew/8859-8-e.html +/sdcard/android/layout_tests/fast/encoding/gbk/x-gbk.html +/sdcard/android/layout_tests/fast/encoding/gbk/x-euc-cn.html +/sdcard/android/layout_tests/fast/encoding/gbk/iso-ir-58.html +/sdcard/android/layout_tests/fast/encoding/gbk/gb_2312-80.html +/sdcard/android/layout_tests/fast/encoding/gbk/gbk.html +/sdcard/android/layout_tests/fast/encoding/gbk/gb2312.html +/sdcard/android/layout_tests/fast/encoding/gbk/EUC-CN.html +/sdcard/android/layout_tests/fast/encoding/gbk/csgb231280.html +/sdcard/android/layout_tests/fast/encoding/gbk/csgb2312.html +/sdcard/android/layout_tests/fast/encoding/gbk/cn-gb.html +/sdcard/android/layout_tests/fast/encoding/gbk/close-gbk-converter.html +/sdcard/android/layout_tests/fast/encoding/gbk/chinese.html +/sdcard/android/layout_tests/fast/encoding/yahoo-mail.html +/sdcard/android/layout_tests/fast/encoding/xml-charset-utf16.html +/sdcard/android/layout_tests/fast/encoding/utf-32-little-endian-nobom.xml +/sdcard/android/layout_tests/fast/encoding/utf-32-little-endian-bom.html +/sdcard/android/layout_tests/fast/encoding/utf-32-big-endian-nobom.xml +/sdcard/android/layout_tests/fast/encoding/utf-32-big-endian-bom.html +/sdcard/android/layout_tests/fast/encoding/tag-in-title.html +/sdcard/android/layout_tests/fast/encoding/script-in-head.html +/sdcard/android/layout_tests/fast/encoding/pseudo-xml.html +/sdcard/android/layout_tests/fast/encoding/pseudo-xml-4.html +/sdcard/android/layout_tests/fast/encoding/pseudo-xml-3.html +/sdcard/android/layout_tests/fast/encoding/pseudo-xml-2.html +/sdcard/android/layout_tests/fast/encoding/pseudo-tags-in-attributes.html +/sdcard/android/layout_tests/fast/encoding/preload-encoding.html +/sdcard/android/layout_tests/fast/encoding/noscript-in-head.html +/sdcard/android/layout_tests/fast/encoding/no-charset-on-dynamic-script-load.html +/sdcard/android/layout_tests/fast/encoding/namespace-tolerance.html +/sdcard/android/layout_tests/fast/encoding/mispositioned-meta.html +/sdcard/android/layout_tests/fast/encoding/misplaced-xml-declaration.html +/sdcard/android/layout_tests/fast/encoding/meta-charset.html +/sdcard/android/layout_tests/fast/encoding/latin1-winlatin.html +/sdcard/android/layout_tests/fast/encoding/high-bit-latin1.html +/sdcard/android/layout_tests/fast/encoding/hanarei-blog32-fc2-com.html +/sdcard/android/layout_tests/fast/encoding/floraexpress-ru.html +/sdcard/android/layout_tests/fast/encoding/decoder-allow-null-chars.html +/sdcard/android/layout_tests/fast/encoding/css-link-charset.html +/sdcard/android/layout_tests/fast/encoding/css-charset.html +/sdcard/android/layout_tests/fast/encoding/css-charset-evil.html +/sdcard/android/layout_tests/fast/encoding/css-charset-dom.html +/sdcard/android/layout_tests/fast/encoding/charset-xuser-defined.html +/sdcard/android/layout_tests/fast/encoding/charset-utf16.html +/sdcard/android/layout_tests/fast/encoding/charset-unicode.html +/sdcard/android/layout_tests/fast/encoding/charset-invalid.html +/sdcard/android/layout_tests/fast/encoding/charset-cp1251.html +/sdcard/android/layout_tests/fast/encoding/bom-in-content.html +/sdcard/android/layout_tests/fast/encoding/bom-in-content-utf16.html +/sdcard/android/layout_tests/fast/encoding/bandai-co-jp-releases.html +/sdcard/android/layout_tests/fast/encoding/ahram-org-eg.html +/sdcard/android/layout_tests/fast/dynamic/subtree-common-root.html +/sdcard/android/layout_tests/fast/dynamic/style-access-late-stylesheet-load.html +/sdcard/android/layout_tests/fast/dynamic/recursive-layout.html +/sdcard/android/layout_tests/fast/dynamic/outerHTML-no-element.html +/sdcard/android/layout_tests/fast/dynamic/insertAdjacentText.html +/sdcard/android/layout_tests/fast/dynamic/insertAdjacentHTML.html +/sdcard/android/layout_tests/fast/dynamic/insertAdjacentHTML-allowed-parents.html +/sdcard/android/layout_tests/fast/dynamic/inline-to-block-crash.html +/sdcard/android/layout_tests/fast/dynamic/hovered-detach.html +/sdcard/android/layout_tests/fast/dynamic/float-remove-above-line.html +/sdcard/android/layout_tests/fast/dynamic/checkbox-selection-crash.html +/sdcard/android/layout_tests/fast/dynamic/ancestor-to-absolute.html +/sdcard/android/layout_tests/fast/dynamic/5872671.html +/sdcard/android/layout_tests/fast/dom/Window/window-special-properties.html +/sdcard/android/layout_tests/fast/dom/Window/window-resize-and-move-sub-frame.html +/sdcard/android/layout_tests/fast/dom/Window/window-remove-event-listener.html +/sdcard/android/layout_tests/fast/dom/Window/window-property-shadowing.html +/sdcard/android/layout_tests/fast/dom/Window/window-property-shadowing-name.html +/sdcard/android/layout_tests/fast/dom/Window/window-open-top.html +/sdcard/android/layout_tests/fast/dom/Window/window-open-self.html +/sdcard/android/layout_tests/fast/dom/Window/window-open-self-from-other-frame.html +/sdcard/android/layout_tests/fast/dom/Window/window-open-parent.html +/sdcard/android/layout_tests/fast/dom/Window/window-open-parent-no-parent.html +/sdcard/android/layout_tests/fast/dom/Window/window-object-cross-frame-calls.html +/sdcard/android/layout_tests/fast/dom/Window/window-location-replace-functions.html +/sdcard/android/layout_tests/fast/dom/Window/window-function-frame-getter-precedence.html +/sdcard/android/layout_tests/fast/dom/Window/window-custom-prototype-crash.html +/sdcard/android/layout_tests/fast/dom/Window/window-closed-crash.html +/sdcard/android/layout_tests/fast/dom/Window/window-appendages-cleared.html +/sdcard/android/layout_tests/fast/dom/Window/setTimeout-no-arguments.html +/sdcard/android/layout_tests/fast/dom/Window/global-opener-function.html +/sdcard/android/layout_tests/fast/dom/Window/getMatchedCSSRules-null-crash.html +/sdcard/android/layout_tests/fast/dom/Window/element-constructors-on-window.html +/sdcard/android/layout_tests/fast/dom/Window/closure-access-after-navigation-iframe.html +/sdcard/android/layout_tests/fast/dom/Window/attr-constructor.html +/sdcard/android/layout_tests/fast/dom/Window/atob-btoa.html +/sdcard/android/layout_tests/fast/dom/Window/alert-undefined.html +/sdcard/android/layout_tests/fast/dom/TreeWalker/TreeWalker-currentNode.html +/sdcard/android/layout_tests/fast/dom/Text/replaceWholeText.html +/sdcard/android/layout_tests/fast/dom/StyleSheet/ownerNode-lifetime.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/viewless-document.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/not-supported-namespace-in-selector.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/id-fastpath.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/id-fastpath-strict.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/id-fastpath-almost-strict.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/elementRoot.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/dumpNodeList.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/dumpNodeList-almost-strict.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/detached-element.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/caseTag.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/caseID.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/caseID-strict.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/caseID-almost-strict.html +/sdcard/android/layout_tests/fast/dom/SelectorAPI/bug-17313.html +/sdcard/android/layout_tests/fast/dom/Range/range-processing-instructions.html +/sdcard/android/layout_tests/fast/dom/Range/range-modifycontents.html +/sdcard/android/layout_tests/fast/dom/Range/range-isPointInRange.html +/sdcard/android/layout_tests/fast/dom/Range/range-intersectsNode.html +/sdcard/android/layout_tests/fast/dom/Range/range-insertNode-splittext.html +/sdcard/android/layout_tests/fast/dom/Range/range-insertNode-separate-endContainer.html +/sdcard/android/layout_tests/fast/dom/Range/range-exceptions.html +/sdcard/android/layout_tests/fast/dom/Range/range-comparePoint.html +/sdcard/android/layout_tests/fast/dom/Range/range-compareNode.html +/sdcard/android/layout_tests/fast/dom/Range/range-clone-empty.html +/sdcard/android/layout_tests/fast/dom/Range/mutation.html +/sdcard/android/layout_tests/fast/dom/Range/compareBoundaryPoints-2.html +/sdcard/android/layout_tests/fast/dom/Range/compareBoundaryPoints-1.html +/sdcard/android/layout_tests/fast/dom/Range/acid3-surround-contents.html +/sdcard/android/layout_tests/fast/dom/Range/13000.html +/sdcard/android/layout_tests/fast/dom/NodeList/item-by-id-with-no-document.html +/sdcard/android/layout_tests/fast/dom/NodeList/invalidate-node-lists-when-parsing.html +/sdcard/android/layout_tests/fast/dom/NodeList/childNodes-reset-cache.html +/sdcard/android/layout_tests/fast/dom/NodeList/5725058-crash-scenario-3.html +/sdcard/android/layout_tests/fast/dom/NodeList/5725058-crash-scenario-2.html +/sdcard/android/layout_tests/fast/dom/NodeList/5725058-crash-scenario-1.html +/sdcard/android/layout_tests/fast/dom/Node/initial-values.html +/sdcard/android/layout_tests/fast/dom/Node/DOMNodeRemovedEvent.html +/sdcard/android/layout_tests/fast/dom/HTMLTableSectionElement/rows.html +/sdcard/android/layout_tests/fast/dom/HTMLTableRowElement/insertCell.html +/sdcard/android/layout_tests/fast/dom/HTMLTableRowElement/cells.html +/sdcard/android/layout_tests/fast/dom/HTMLTableElement/tBodies.html +/sdcard/android/layout_tests/fast/dom/HTMLTableElement/rows.html +/sdcard/android/layout_tests/fast/dom/HTMLTableElement/insert-row.html +/sdcard/android/layout_tests/fast/dom/HTMLTableElement/early-acid3-66-excerpt.html +/sdcard/android/layout_tests/fast/dom/HTMLTableElement/early-acid3-65-excerpt.html +/sdcard/android/layout_tests/fast/dom/HTMLTableElement/cellpadding-attribute.html +/sdcard/android/layout_tests/fast/dom/HTMLSelectElement/options-collection-set-string-length.html +/sdcard/android/layout_tests/fast/dom/HTMLSelectElement/options-collection-detached.html +/sdcard/android/layout_tests/fast/dom/HTMLScriptElement/script-set-src.html +/sdcard/android/layout_tests/fast/dom/HTMLScriptElement/script-reexecution.html +/sdcard/android/layout_tests/fast/dom/HTMLScriptElement/script-load-events.html +/sdcard/android/layout_tests/fast/dom/HTMLScriptElement/script-decoding-error-after-setting-src.html +/sdcard/android/layout_tests/fast/dom/HTMLOptionElement/set-option-index-text.html +/sdcard/android/layout_tests/fast/dom/HTMLOptionElement/option-text.html +/sdcard/android/layout_tests/fast/dom/HTMLOptionElement/option-prototype.html +/sdcard/android/layout_tests/fast/dom/HTMLObjectElement/form/test1.html +/sdcard/android/layout_tests/fast/dom/HTMLMetaElement/meta-attributes.html +/sdcard/android/layout_tests/fast/dom/HTMLLabelElement/form/test1.html +/sdcard/android/layout_tests/fast/dom/HTMLInputElement/size-attribute.html +/sdcard/android/layout_tests/fast/dom/HTMLInputElement/size-as-number.html +/sdcard/android/layout_tests/fast/dom/HTMLInputElement/input-text-reset.html +/sdcard/android/layout_tests/fast/dom/HTMLInputElement/input-hidden-value.html +/sdcard/android/layout_tests/fast/dom/HTMLInputElement/input-checked-reset.html +/sdcard/android/layout_tests/fast/dom/HTMLInputElement/checked-pseudo-selector.html +/sdcard/android/layout_tests/fast/dom/HTMLImageElement/image-without-renderer-width.html +/sdcard/android/layout_tests/fast/dom/HTMLImageElement/image-src-absolute-url.html +/sdcard/android/layout_tests/fast/dom/HTMLImageElement/image-natural-width-height.html +/sdcard/android/layout_tests/fast/dom/HTMLImageElement/image-lowsrc-getset.html +/sdcard/android/layout_tests/fast/dom/HTMLImageElement/image-longdesc-absolute-url.html +/sdcard/android/layout_tests/fast/dom/HTMLImageElement/image-loading-gc.html +/sdcard/android/layout_tests/fast/dom/HTMLImageElement/constructor-mutation-event-dispatch.html +/sdcard/android/layout_tests/fast/dom/HTMLHtmlElement/set-version.html +/sdcard/android/layout_tests/fast/dom/HTMLHtmlElement/duplicate-html-element-crash.html +/sdcard/android/layout_tests/fast/dom/HTMLHeadElement/head-check.html +/sdcard/android/layout_tests/fast/dom/HTMLFormElement/elements-not-in-document.html +/sdcard/android/layout_tests/fast/dom/HTMLFontElement/size-attribute.html +/sdcard/android/layout_tests/fast/dom/HTMLElement/set-inner-outer-optimization.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/writeln-multiple-calls.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/writeln-call.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/write-multiple-calls.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/write-call.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/url-getset.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/title-set.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/title-get.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/object-by-name-unknown-child-element.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/object-by-name-or-id.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/document-special-properties.html +/sdcard/android/layout_tests/fast/dom/HTMLDocument/document-plugins.html +/sdcard/android/layout_tests/fast/dom/HTMLDivElement/align/getset.html +/sdcard/android/layout_tests/fast/dom/HTMLButtonElement/value/getset.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/array/004.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/array/003.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/array/002.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/array/001.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/dumpNodeList.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/015.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/014.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/013.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/012.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/009.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/008.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/007.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/006.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/005.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/004.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/003.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/002.html +/sdcard/android/layout_tests/fast/dom/getElementsByClassName/001.html +/sdcard/android/layout_tests/fast/dom/EntityReference/readonly-exceptions.html +/sdcard/android/layout_tests/fast/dom/Element/setAttribute-with-colon.html +/sdcard/android/layout_tests/fast/dom/Element/setAttribute-case-insensitivity.html +/sdcard/android/layout_tests/fast/dom/Element/onclick-case.html +/sdcard/android/layout_tests/fast/dom/Element/offsetTop-table-cell.html +/sdcard/android/layout_tests/fast/dom/Element/getAttribute-check-case-sensitivity.html +/sdcard/android/layout_tests/fast/dom/Element/element-traversal.html +/sdcard/android/layout_tests/fast/dom/Element/dimension-properties-unrendered.html +/sdcard/android/layout_tests/fast/dom/Element/contains-method.html +/sdcard/android/layout_tests/fast/dom/Element/attribute-uppercase.html +/sdcard/android/layout_tests/fast/dom/Element/attr-param-typechecking.html +/sdcard/android/layout_tests/fast/dom/DOMImplementation/createDocumentType-err.html +/sdcard/android/layout_tests/fast/dom/DOMException/RangeException.html +/sdcard/android/layout_tests/fast/dom/DOMException/prototype-object.html +/sdcard/android/layout_tests/fast/dom/DOMException/EventException.html +/sdcard/android/layout_tests/fast/dom/Document/title-property-set-multiple-times.html +/sdcard/android/layout_tests/fast/dom/Document/title-property-creates-title-element.html +/sdcard/android/layout_tests/fast/dom/Document/replaceChild-null-oldChild.html +/sdcard/android/layout_tests/fast/dom/Document/replace-child.html +/sdcard/android/layout_tests/fast/dom/Document/open-with-pending-load.html +/sdcard/android/layout_tests/fast/dom/Document/document-reopen.html +/sdcard/android/layout_tests/fast/dom/Document/document-charset.html +/sdcard/android/layout_tests/fast/dom/Document/doc-open-while-parsing.html +/sdcard/android/layout_tests/fast/dom/Document/createElementNS-namespace-err.html +/sdcard/android/layout_tests/fast/dom/Document/createAttributeNS-namespace-err.html +/sdcard/android/layout_tests/fast/dom/CSSStyleDeclaration/transition-property-names.html +/sdcard/android/layout_tests/fast/dom/CSSStyleDeclaration/empty-string-property.html +/sdcard/android/layout_tests/fast/dom/CSSStyleDeclaration/css-properties-case-sensitive.html +/sdcard/android/layout_tests/fast/dom/XMLSerializer.html +/sdcard/android/layout_tests/fast/dom/XMLSerializer-doctype2.html +/sdcard/android/layout_tests/fast/dom/XMLSerializer-doctype.html +/sdcard/android/layout_tests/fast/dom/xmlhttprequest-invalid-values.html +/sdcard/android/layout_tests/fast/dom/xmlhttprequest-html-response-encoding.html +/sdcard/android/layout_tests/fast/dom/wrapper-identity.html +/sdcard/android/layout_tests/fast/dom/undetectable-style-filter.html +/sdcard/android/layout_tests/fast/dom/undetectable-document-all.html +/sdcard/android/layout_tests/fast/dom/title-text-property.html +/sdcard/android/layout_tests/fast/dom/title-text-property-2.html +/sdcard/android/layout_tests/fast/dom/timer-clear-interval-in-handler.html +/sdcard/android/layout_tests/fast/dom/space-to-text.html +/sdcard/android/layout_tests/fast/dom/setAttributeNS.html +/sdcard/android/layout_tests/fast/dom/setAttribute-using-initial-input-value.html +/sdcard/android/layout_tests/fast/dom/set-inner-text-newlines.html +/sdcard/android/layout_tests/fast/dom/serialize-cdata.html +/sdcard/android/layout_tests/fast/dom/select-selectedIndex-multiple.html +/sdcard/android/layout_tests/fast/dom/select-selectedIndex-bug-12942.html +/sdcard/android/layout_tests/fast/dom/script-element-without-frame-crash.html +/sdcard/android/layout_tests/fast/dom/script-element-remove-self.html +/sdcard/android/layout_tests/fast/dom/script-element-gc.html +/sdcard/android/layout_tests/fast/dom/script-add.html +/sdcard/android/layout_tests/fast/dom/resource-locations-in-created-html-document.html +/sdcard/android/layout_tests/fast/dom/replace-first-child.html +/sdcard/android/layout_tests/fast/dom/replace-child-siblings.html +/sdcard/android/layout_tests/fast/dom/remove-style-element.html +/sdcard/android/layout_tests/fast/dom/remove-named-attribute-crash.html +/sdcard/android/layout_tests/fast/dom/Range-insertNode-crash.html +/sdcard/android/layout_tests/fast/dom/prototypes.html +/sdcard/android/layout_tests/fast/dom/prototype-chain.html +/sdcard/android/layout_tests/fast/dom/plugin-attributes-enumeration.html +/sdcard/android/layout_tests/fast/dom/outerText-no-element.html +/sdcard/android/layout_tests/fast/dom/option-properties.html +/sdcard/android/layout_tests/fast/dom/objc-big-method-name.html +/sdcard/android/layout_tests/fast/dom/null-document-xmlhttprequest-open.html +/sdcard/android/layout_tests/fast/dom/location-assign.html +/sdcard/android/layout_tests/fast/dom/javascript-backslash.html +/sdcard/android/layout_tests/fast/dom/innerHTML-nbsp.html +/sdcard/android/layout_tests/fast/dom/innerHTML-escaping-attribute.html +/sdcard/android/layout_tests/fast/dom/inner-width-height.html +/sdcard/android/layout_tests/fast/dom/inner-text-with-no-renderer.html +/sdcard/android/layout_tests/fast/dom/inner-text-rtl.html +/sdcard/android/layout_tests/fast/dom/importNode-prefix.html +/sdcard/android/layout_tests/fast/dom/importNode-null.html +/sdcard/android/layout_tests/fast/dom/import-document-fragment.html +/sdcard/android/layout_tests/fast/dom/import-attribute-node.html +/sdcard/android/layout_tests/fast/dom/implementation-createHTMLDocument.html +/sdcard/android/layout_tests/fast/dom/ImageDocument-image-deletion.html +/sdcard/android/layout_tests/fast/dom/image-object.html +/sdcard/android/layout_tests/fast/dom/iframe-document.html +/sdcard/android/layout_tests/fast/dom/iframe-contentWindow-crash.html +/sdcard/android/layout_tests/fast/dom/html-attribute-types.html +/sdcard/android/layout_tests/fast/dom/getter-on-window-object2.html +/sdcard/android/layout_tests/fast/dom/getter-on-window-object.html +/sdcard/android/layout_tests/fast/dom/getelementbyname-invalidation.html +/sdcard/android/layout_tests/fast/dom/generic-form-element-assert.html +/sdcard/android/layout_tests/fast/dom/gc-7.html +/sdcard/android/layout_tests/fast/dom/gc-6.html +/sdcard/android/layout_tests/fast/dom/gc-5.html +/sdcard/android/layout_tests/fast/dom/gc-4.html +/sdcard/android/layout_tests/fast/dom/gc-3.html +/sdcard/android/layout_tests/fast/dom/gc-2.html +/sdcard/android/layout_tests/fast/dom/gc-11.html +/sdcard/android/layout_tests/fast/dom/gc-1.html +/sdcard/android/layout_tests/fast/dom/frame-contentWindow-crash.html +/sdcard/android/layout_tests/fast/dom/features.html +/sdcard/android/layout_tests/fast/dom/exception-no-frame-timeout-crash.html +/sdcard/android/layout_tests/fast/dom/exception-no-frame-inline-script-crash.html +/sdcard/android/layout_tests/fast/dom/everything-to-string.html +/sdcard/android/layout_tests/fast/dom/element-attribute-js-null.html +/sdcard/android/layout_tests/fast/dom/early-frame-url.html +/sdcard/android/layout_tests/fast/dom/duplicate-ids.html +/sdcard/android/layout_tests/fast/dom/duplicate-ids-document-order.html +/sdcard/android/layout_tests/fast/dom/DOMParser-assign-variable.html +/sdcard/android/layout_tests/fast/dom/domListEnumeration.html +/sdcard/android/layout_tests/fast/dom/dom-instanceof.html +/sdcard/android/layout_tests/fast/dom/documenturi-loses-to-base-tag.html +/sdcard/android/layout_tests/fast/dom/documenturi-assigned-junk-implies-relative-urls-do-not-resolve.html +/sdcard/android/layout_tests/fast/dom/documenturi-assigned-junk-implies-baseuri-null.html +/sdcard/android/layout_tests/fast/dom/documenturi-affects-relative-paths.html +/sdcard/android/layout_tests/fast/dom/documentElement-null.html +/sdcard/android/layout_tests/fast/dom/document-scripts.html +/sdcard/android/layout_tests/fast/dom/document-dir-property.html +/sdcard/android/layout_tests/fast/dom/document-attribute-js-null.html +/sdcard/android/layout_tests/fast/dom/document-all-select.html +/sdcard/android/layout_tests/fast/dom/document-all-input.html +/sdcard/android/layout_tests/fast/dom/dir-no-body.html +/sdcard/android/layout_tests/fast/dom/destroy-selected-radio-button-crash.html +/sdcard/android/layout_tests/fast/dom/defaultView.html +/sdcard/android/layout_tests/fast/dom/css-shortHands.html +/sdcard/android/layout_tests/fast/dom/css-set-property-exception.html +/sdcard/android/layout_tests/fast/dom/css-selectorText.html +/sdcard/android/layout_tests/fast/dom/css-RGBValue.html +/sdcard/android/layout_tests/fast/dom/css-mediarule-functions.html +/sdcard/android/layout_tests/fast/dom/css-element-attribute-js-null.html +/sdcard/android/layout_tests/fast/dom/css-dom-read.html +/sdcard/android/layout_tests/fast/dom/css-dom-read-2.html +/sdcard/android/layout_tests/fast/dom/createElementNS.html +/sdcard/android/layout_tests/fast/dom/createElement.html +/sdcard/android/layout_tests/fast/dom/createElement-with-column.xml +/sdcard/android/layout_tests/fast/dom/createElement-with-column.html +/sdcard/android/layout_tests/fast/dom/createDocumentType2.html +/sdcard/android/layout_tests/fast/dom/createDocument.html +/sdcard/android/layout_tests/fast/dom/createDocument-empty.html +/sdcard/android/layout_tests/fast/dom/createAttribute-exception.html +/sdcard/android/layout_tests/fast/dom/constructors-overriding.html +/sdcard/android/layout_tests/fast/dom/constants.html +/sdcard/android/layout_tests/fast/dom/computed-style-set-property.html +/sdcard/android/layout_tests/fast/dom/compatMode-Strict.html +/sdcard/android/layout_tests/fast/dom/compatMode-Compat.html +/sdcard/android/layout_tests/fast/dom/compatMode-AlmostStrict.html +/sdcard/android/layout_tests/fast/dom/comment-dom-node.html +/sdcard/android/layout_tests/fast/dom/comment-document-fragment.html +/sdcard/android/layout_tests/fast/dom/collection-null-like-arguments.html +/sdcard/android/layout_tests/fast/dom/collection-namedItem-via-item.html +/sdcard/android/layout_tests/fast/dom/clone-node-style.html +/sdcard/android/layout_tests/fast/dom/clone-node-form-elements.html +/sdcard/android/layout_tests/fast/dom/clone-node-form-elements-with-attr.html +/sdcard/android/layout_tests/fast/dom/class-all-whitespace.html +/sdcard/android/layout_tests/fast/dom/capturing-event-listeners.html +/sdcard/android/layout_tests/fast/dom/canvasContext2d-element-attribute-js-null.html +/sdcard/android/layout_tests/fast/dom/background-shorthand-csstext.html +/sdcard/android/layout_tests/fast/dom/attribute-namespaces-get-set.html +/sdcard/android/layout_tests/fast/dom/attribute-empty-value-no-children.html +/sdcard/android/layout_tests/fast/dom/attribute-downcast-right.html +/sdcard/android/layout_tests/fast/dom/attribute-case-sensitivity.html +/sdcard/android/layout_tests/fast/dom/array-special-accessors-should-ignore-items.html +/sdcard/android/layout_tests/fast/dom/anchor-toString.html +/sdcard/android/layout_tests/fast/dom/anchor-backslash.html +/sdcard/android/layout_tests/fast/doctypes/html-doctype.html +/sdcard/android/layout_tests/fast/doctypes/doctype-parsing.html +/sdcard/android/layout_tests/fast/doctypes/doctype-in-element.html +/sdcard/android/layout_tests/fast/doctypes/doctype-at-end.html +/sdcard/android/layout_tests/fast/doctypes/doctype-after-comment.html +/sdcard/android/layout_tests/fast/doctypes/005-case-preserving.html +/sdcard/android/layout_tests/fast/css-generated-content/reset-content-to-initial.html +/sdcard/android/layout_tests/fast/css-generated-content/empty-content-with-float-crash.html +/sdcard/android/layout_tests/fast/css/variables/invalid-identifier.html +/sdcard/android/layout_tests/fast/css/counters/counter-number-input.html +/sdcard/android/layout_tests/fast/css/counters/counter-function-input.html +/sdcard/android/layout_tests/fast/css/counters/counter-function-input-2.html +/sdcard/android/layout_tests/fast/css/word-break-user-modify-allowed-values.html +/sdcard/android/layout_tests/fast/css/transition_shorthand_parsing.html +/sdcard/android/layout_tests/fast/css/transition-timing-function.html +/sdcard/android/layout_tests/fast/css/transform-function-lowercase-assert.html +/sdcard/android/layout_tests/fast/css/stale-style-selector-crash-2.html +/sdcard/android/layout_tests/fast/css/stale-style-selector-crash-1.html +/sdcard/android/layout_tests/fast/css/sheet-title.html +/sdcard/android/layout_tests/fast/css/remove-shorthand.html +/sdcard/android/layout_tests/fast/css/readonly-pseudoclass-opera-005.html +/sdcard/android/layout_tests/fast/css/readonly-pseudoclass-opera-004.html +/sdcard/android/layout_tests/fast/css/readonly-pseudoclass-opera-003.html +/sdcard/android/layout_tests/fast/css/readonly-pseudoclass-opera-002.html +/sdcard/android/layout_tests/fast/css/readonly-pseudoclass-opera-001.html +/sdcard/android/layout_tests/fast/css/pseudostyle-anonymous-text.html +/sdcard/android/layout_tests/fast/css/parse-timing-function-crash.html +/sdcard/android/layout_tests/fast/css/padding-no-renderer.html +/sdcard/android/layout_tests/fast/css/overflow-property.html +/sdcard/android/layout_tests/fast/css/outline-hidden-illegal-value.html +/sdcard/android/layout_tests/fast/css/orphaned_units_crash.html +/sdcard/android/layout_tests/fast/css/nested-rule-parent-sheet.html +/sdcard/android/layout_tests/fast/css/min-device-aspect-ratio.html +/sdcard/android/layout_tests/fast/css/media-rule-dyn.html +/sdcard/android/layout_tests/fast/css/max-height-and-max-width.html +/sdcard/android/layout_tests/fast/css/max-device-aspect-ratio.html +/sdcard/android/layout_tests/fast/css/legacy-opacity-styles.html +/sdcard/android/layout_tests/fast/css/invalid-rule-value.html +/sdcard/android/layout_tests/fast/css/invalid-cursor-property-crash.html +/sdcard/android/layout_tests/fast/css/insertRule-media.html +/sdcard/android/layout_tests/fast/css/insertRule-font-face.html +/sdcard/android/layout_tests/fast/css/import-style-update.html +/sdcard/android/layout_tests/fast/css/hexColor-isDigit-assert.html +/sdcard/android/layout_tests/fast/css/getPropertyValue-clip.html +/sdcard/android/layout_tests/fast/css/getPropertyValue-border.html +/sdcard/android/layout_tests/fast/css/getComputedStyle-zIndex-auto.html +/sdcard/android/layout_tests/fast/css/getComputedStyle-relayout.html +/sdcard/android/layout_tests/fast/css/getComputedStyle-borderRadius.html +/sdcard/android/layout_tests/fast/css/getComputedStyle-border-spacing.html +/sdcard/android/layout_tests/fast/css/getComputedStyle-border-image.html +/sdcard/android/layout_tests/fast/css/getComputedStyle-border-box.html +/sdcard/android/layout_tests/fast/css/getComputedStyle-background-size.html +/sdcard/android/layout_tests/fast/css/getComputedStyle-background-position.html +/sdcard/android/layout_tests/fast/css/font-property-priority.html +/sdcard/android/layout_tests/fast/css/font-family-initial.html +/sdcard/android/layout_tests/fast/css/font-face-multiple-families.html +/sdcard/android/layout_tests/fast/css/font-face-descriptor-multiple-values-parsing.html +/sdcard/android/layout_tests/fast/css/emptyStyleTag.html +/sdcard/android/layout_tests/fast/css/empty-script.html +/sdcard/android/layout_tests/fast/css/display-none-inline-style-change-crash.html +/sdcard/android/layout_tests/fast/css/device-aspect-ratio.html +/sdcard/android/layout_tests/fast/css/dashboard-regions-attr-crash.html +/sdcard/android/layout_tests/fast/css/CSSPrimitiveValue-exceptions.html +/sdcard/android/layout_tests/fast/css/css-selector-text.html +/sdcard/android/layout_tests/fast/css/css-properties-case-insensitive.html +/sdcard/android/layout_tests/fast/css/computed-style-negative-top.html +/sdcard/android/layout_tests/fast/css/computed-style-display-none.html +/sdcard/android/layout_tests/fast/css/child-selector-implicit-tbody.html +/sdcard/android/layout_tests/fast/css/case-transform.html +/sdcard/android/layout_tests/fast/css/border-image-crash.html +/sdcard/android/layout_tests/fast/css/background-position-serialize.html +/sdcard/android/layout_tests/fast/css/background-position-inherit.html +/sdcard/android/layout_tests/fast/css/background-currentcolor.html +/sdcard/android/layout_tests/fast/cookies/local-file-can-set-cookies.html +/sdcard/android/layout_tests/fast/canvas/unclosed-canvas-4.html +/sdcard/android/layout_tests/fast/canvas/unclosed-canvas-3.html +/sdcard/android/layout_tests/fast/canvas/unclosed-canvas-2.html +/sdcard/android/layout_tests/fast/canvas/unclosed-canvas-1.html +/sdcard/android/layout_tests/fast/canvas/toDataURL-noData.html +/sdcard/android/layout_tests/fast/canvas/script-inside-canvas-fallback.html +/sdcard/android/layout_tests/fast/canvas/radialGradient-infinite-values.html +/sdcard/android/layout_tests/fast/canvas/pointInPath.html +/sdcard/android/layout_tests/fast/canvas/linearGradient-infinite-values.html +/sdcard/android/layout_tests/fast/canvas/pattern-with-transform.html +/sdcard/android/layout_tests/fast/canvas/gradient-with-clip.html +/sdcard/android/layout_tests/fast/canvas/gradient-addColorStop-with-invalid-color.html +/sdcard/android/layout_tests/fast/canvas/drawImage-with-negative-source-destination.html +/sdcard/android/layout_tests/fast/canvas/drawImage-with-invalid-args.html +/sdcard/android/layout_tests/fast/canvas/create-pattern-does-not-crash.html +/sdcard/android/layout_tests/fast/canvas/canvas-with-incorrect-args.html +/sdcard/android/layout_tests/fast/canvas/canvas-putImageData.html +/sdcard/android/layout_tests/fast/canvas/canvas-pattern-behaviour.html +/sdcard/android/layout_tests/fast/canvas/canvas-path-with-inf-nan-dimensions.html +/sdcard/android/layout_tests/fast/canvas/canvas-invalid-strokestyle.html +/sdcard/android/layout_tests/fast/canvas/canvas-invalid-fillstyle.html +/sdcard/android/layout_tests/fast/canvas/canvas-ImageData-behaviour.html +/sdcard/android/layout_tests/fast/canvas/canvas-hides-fallback.html +/sdcard/android/layout_tests/fast/canvas/arc-crash.html +/sdcard/android/layout_tests/fast/canvas/access-zero-sized-canvas.html diff --git a/tests/DumpRenderTree/run_layout_tests.py b/tests/DumpRenderTree/run_layout_tests.py new file mode 100755 index 0000000000000..b4eb685068b6d --- /dev/null +++ b/tests/DumpRenderTree/run_layout_tests.py @@ -0,0 +1,176 @@ +#!/usr/bin/python + +"""Run layout tests using Android emulator and instrumentation. + + First, you need to get an SD card or sdcard image that has layout tests on it. + Layout tests are in following directory: + /sdcard/android/layout_tests + For example, /sdcard/android/layout_tests/fast + + Usage: + Run all tests under fast/ directory: + run_layout_tests.py, or + run_layout_tests.py fast + + Run all tests under a sub directory: + run_layout_tests.py fast/dom + + Run a single test: + run_layout_tests.py fast/dom/ + + After a merge, if there are changes of layout tests in SD card, you need to + use --refresh-test-list option *once* to re-generate test list on the card. + + Some other options are: + --time-out-ms (default is 8000 millis) for each test + --adb-options="-e" passes option string to adb + --results-directory=..., (default is ./layout-test-results) directory name under which results are stored. +""" + +import logging +import optparse +import os +import subprocess +import sys +import time + +def CountLineNumber(filename): + """Compute the number of lines in a given file. + + Args: + filename: a file name related to the current directory. + """ + + fp = open(os.path.abspath(filename), "r"); + lines = 0 + for line in fp.readlines(): + lines = lines + 1 + fp.close() + return lines + +def main(options, args): + """Run the tests. Will call sys.exit when complete. + + Args: + options: a dictionary of command line options + args: a list of sub directories or files to test + """ + + # Set up logging format. + log_level = logging.INFO + if options.verbose: + log_level = logging.DEBUG + logging.basicConfig(level=log_level, + format='%(message)s') + + # Include all tests if none are specified. + if not args: + path = 'fast'; + else: + path = ' '.join(args); + + adb_cmd = "adb "; + if options.adb_options: + adb_cmd += options.adb_options + + # Re-generate the test list if --refresh-test-list is on + if options.refresh_test_list: + logging.info("Generating test list."); + shell_cmd_str = adb_cmd + " shell am instrument -e class com.android.dumprendertree.LayoutTestsAutoTest#generateTestList -e path fast -w com.android.dumprendertree/.LayoutTestsAutoRunner" + adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] + + if adb_output.find('Process crashed') != -1: + logging.info("Aborting because cannot generate test list.\n" + adb_output) + sys.exit(1) + + + logging.info("Starting tests") + + # Count crashed tests. + crashed_tests = [] + + timeout_ms = '8000' + if options.time_out_ms: + timeout_ms = options.time_out_ms + + # Run test until it's done + + # Call LayoutTestsAutoTest::startLayoutTests. + shell_cmd_str = adb_cmd + " shell am instrument -e class com.android.dumprendertree.LayoutTestsAutoTest#startLayoutTests -e path \"" + path + "\" -e timeout " + timeout_ms + " -w com.android.dumprendertree/.LayoutTestsAutoRunner" + adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] + while adb_output.find('Process crashed') != -1: + # Get the running_test.txt + logging.error("DumpRenderTree crashed, output:\n" + adb_output) + + shell_cmd_str = adb_cmd + " shell cat /sdcard/running_test.txt" + crashed_test = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE).communicate()[0] + + logging.info(crashed_test + " CRASHED"); + crashed_tests.append(crashed_test); + + logging.info("Resuming layout test runner..."); + # Call LayoutTestsAutoTest::resumeLayoutTests + shell_cmd_str = adb_cmd + " shell am instrument -e class com.android.dumprendertree.LayoutTestsAutoTest#resumeLayoutTests -e path \"" + path + "\" -e timeout " + timeout_ms + " -w com.android.dumprendertree/.LayoutTestsAutoRunner" + + adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] + + if adb_output.find('INSTRUMENTATION_FAILED') != -1: + logging.error("Error happened : " + adb_output) + sys.exit(1) + + logging.info("Done"); + logging.debug(adb_output); + + # Pull results from /sdcard + results_dir = options.results_directory + if not os.path.exists(results_dir): + os.makedirs(results_dir) + if not os.path.isdir(results_dir): + logging.error("Cannot create results dir: " + results_dir); + sys.exit(1); + + result_files = ["/sdcard/layout_tests_passed.txt", + "/sdcard/layout_tests_failed.txt", + "/sdcard/layout_tests_nontext.txt"] + for file in result_files: + shell_cmd_str = adb_cmd + " pull " + file + " " + results_dir + adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] + logging.debug(adb_output) + + # Create the crash list. + fp = open(results_dir + "/layout_tests_crashed.txt", "w"); + fp.writelines(crashed_tests) + fp.close() + + # Count the number of tests in each category. + passed_tests = CountLineNumber(results_dir + "/layout_tests_passed.txt") + logging.info(str(passed_tests) + " passed") + failed_tests = CountLineNumber(results_dir + "/layout_tests_failed.txt") + logging.info(str(failed_tests) + " failed") + crashed_tests = CountLineNumber(results_dir + "/layout_tests_crashed.txt") + logging.info(str(crashed_tests) + " crashed") + nontext_tests = CountLineNumber(results_dir + "/layout_tests_nontext.txt") + logging.info(str(nontext_tests) + " no dumpAsText") + + logging.info("Results are stored under: " + results_dir) + + +if '__main__' == __name__: + option_parser = optparse.OptionParser() + option_parser.add_option("", "--time-out-ms", + default=None, + help="set the timeout for each test") + option_parser.add_option("", "--verbose", action="store_true", + default=False, + help="include debug-level logging") + option_parser.add_option("", "--refresh-test-list", action="store_true", + default=False, + help="re-generate test list, it may take some time.") + option_parser.add_option("", "--adb-options", + default=None, + help="pass options to adb, such as -d -e, etc"); + option_parser.add_option("", "--results-directory", + default="layout-test-results", + help="directory name under which results are stored.") + options, args = option_parser.parse_args(); + main(options, args) diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java index 91597d5227896..9be33db291850 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java @@ -24,13 +24,19 @@ public class FileFilter { public static boolean ignoreTest(String file) { // treat files like directories for the time being. - int size = ignoreTestList.length; - for (int i = 0; i < size; i ++) { - if (file.startsWith(ignoreTestList[i])) { + for (int i = 0; i < ignoreTestList.length; i ++) { + if (file.endsWith(ignoreTestList[i])) { Log.e("FileFilter", "File path in IgnoreTest: " + file); return true; } } + for (int i = 0; i < ignoreTestDirs.length; i++) { + if (file.endsWith(ignoreTestDirs[i])) { + Log.e("FileFilter", "File path in ignore list: " + file); + return true; + } + } + return false; } @@ -64,8 +70,8 @@ public class FileFilter { fillIgnoreResultSet(); fillBugTable(); } - - static final String [] ignoreTestList = { + + static final String[] ignoreTestDirs = { ".", // ignore hidden directories and files "resources", // ignore resource directories "AppleScript", // AppleScript not supported @@ -73,13 +79,16 @@ public class FileFilter { "xsl", //xsl requires libxml2 & libxslt, not sup. "kde", // don't run kde tests. ".svn", // don't run anything under .svn folder - "gradients", //known crash + "gradients", // known crash + "profiler" // profiler is not supported + }; + + static final String [] ignoreTestList = { "toString-stack-overflow.html", // Crashes #606688 "frame-limit.html", // generates too many GREFs "css-insert-import-rule.html", // Crashes, #717414 "input-text-enter.html", // Crashes. #735088 "text-shadow-extreme-value.html", // Crashes #571671 - "001.html", "reflection-masks.html", "frame-creation-removal.html", "large-expressions.html", @@ -203,6 +212,8 @@ public class FileFilter { ignoreResultList.add("fast/loader/local-iFrame-source-from-local.html"); // extra spacing because iFrames rendered next to each other on Apple ignoreResultList.add("fast/loader/opaque-base-url.html"); + // RegExp is too large, causing OOM + ignoreResultList.add("fast/js/regexp-charclass-crash.html"); ignoreResultList.add("fast/text/plain-text-line-breaks.html"); diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java index 8b33d16a7806b..d685f5d0476aa 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java @@ -117,9 +117,14 @@ public abstract class FileList extends ListActivity { Map map = (Map) l.getItemAtPosition(position); String path = (String)map.get("path"); - if (path.length() > 0) - processFile(path, true); + if ((new File(path)).isDirectory()) { + mPath = path; + mFocusFile = null; + updateList(); + } else { + processFile(path, false); + } } /* @@ -148,7 +153,7 @@ public abstract class FileList extends ListActivity protected void setupPath() { - mPath = "/sdcard"; + mPath = "/sdcard/android/layout_tests"; mBaseLength = mPath.length(); } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java index 9521f8038656f..c77d98ae55c90 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java @@ -16,14 +16,16 @@ package com.android.dumprendertree; +import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; +import java.io.FileReader; import java.io.IOException; +import java.util.List; import java.util.Vector; -import java.util.Stack; import android.app.Activity; import android.content.Intent; @@ -38,14 +40,12 @@ import android.webkit.WebSettings; import android.webkit.WebView; import android.widget.LinearLayout; import android.os.*; -import android.test.TestRecorder; -// SQLite3 in android has a bunch of bugs which -// is causing TestRecorder to not record the results -// properly. This class is a wrapper around it and records -// results in a file as well. -class TestRecorderV2 extends TestRecorder { - @Override +// TestRecorder creates two files, one for passing tests +// and another for failing tests and writes the paths to +// layout tests one line at a time. TestRecorder does not +// have ability to clear the results. +class TestRecorder { public void passed(String layout_file) { try { mBufferedOutputPassedStream.write(layout_file.getBytes()); @@ -54,72 +54,68 @@ class TestRecorderV2 extends TestRecorder { } catch(Exception e) { e.printStackTrace(); } - super.passed(layout_file); } - @Override public void failed(String layout_file, String reason) { try { mBufferedOutputFailedStream.write(layout_file.getBytes()); + mBufferedOutputFailedStream.write(" : ".getBytes()); + mBufferedOutputFailedStream.write(reason.getBytes()); mBufferedOutputFailedStream.write('\n'); mBufferedOutputFailedStream.flush(); } catch(Exception e) { e.printStackTrace(); } - super.failed(layout_file, reason); } - public TestRecorderV2() { - super(); + public void nontext(String layout_file) { try { - File resultsPassedFile = new File("/sdcard/layout_test_presults.txt"); - File resultsFailedFile = new File("/sdcard/layout_test_fresults.txt"); + mBufferedOutputNontextStream.write(layout_file.getBytes()); + mBufferedOutputNontextStream.write('\n'); + mBufferedOutputNontextStream.flush(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + public TestRecorder(boolean resume) { + try { + File resultsPassedFile = new File("/sdcard/layout_tests_passed.txt"); + File resultsFailedFile = new File("/sdcard/layout_tests_failed.txt"); + File resultsNontextFile = new File("/sdcard/layout_tests_nontext.txt"); mBufferedOutputPassedStream = - new BufferedOutputStream(new FileOutputStream(resultsPassedFile, true)); + new BufferedOutputStream(new FileOutputStream(resultsPassedFile, resume)); mBufferedOutputFailedStream = - new BufferedOutputStream(new FileOutputStream(resultsFailedFile, true)); - + new BufferedOutputStream(new FileOutputStream(resultsFailedFile, resume)); + mBufferedOutputNontextStream = + new BufferedOutputStream(new FileOutputStream(resultsNontextFile, resume)); } catch (Exception e) { e.printStackTrace(); } } - - protected void finalize() throws Throwable { - mBufferedOutputPassedStream.flush(); - mBufferedOutputFailedStream.flush(); - mBufferedOutputPassedStream.close(); - mBufferedOutputFailedStream.close(); - } - private static BufferedOutputStream mBufferedOutputPassedStream; - private static BufferedOutputStream mBufferedOutputFailedStream; + public void close() { + try { + mBufferedOutputPassedStream.close(); + mBufferedOutputFailedStream.close(); + mBufferedOutputNontextStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + private BufferedOutputStream mBufferedOutputPassedStream; + private BufferedOutputStream mBufferedOutputFailedStream; + private BufferedOutputStream mBufferedOutputNontextStream; } public class HTMLHostActivity extends Activity implements LayoutTestController { - - private TestRecorderV2 mResultRecorder = new TestRecorderV2(); - private HTMLHostCallbackInterface mCallback = null; - private CallbackProxy mCallbackProxy; - - public class FileEntry { - public FileEntry(String path, int index) { - mPath = path; mIndex=index; - } - String mPath; - int mIndex; - } public class AsyncHandler extends Handler { @Override public void handleMessage(Message msg) { - if (msg.what == MSG_DUMP) { - this.removeMessages(MSG_TIMEOUT); - mTimedOut = false; - requestWebKitData(); - return; - } else if (msg.what == MSG_TIMEOUT) { + if (msg.what == MSG_TIMEOUT) { mTimedOut = true; requestWebKitData(); return; @@ -127,21 +123,24 @@ public class HTMLHostActivity extends Activity HTMLHostActivity.this.dump(mTimedOut, (String)msg.obj); return; } - + super.handleMessage(msg); } - - void requestWebKitData() { - Message callback = obtainMessage(MSG_WEBKIT_DATA); - if (dumpAsText) { - mWebView.documentAsText(callback); - } else { - mWebView.externalRepresentation(callback); - } - } - } + public void requestWebKitData() { + Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA); + + if (mRequestedWebKitData) + throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl()); + + mRequestedWebKitData = true; + if (mDumpAsText) { + mWebView.documentAsText(callback); + } else { + mWebView.externalRepresentation(callback); + } + } // Activity methods public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -153,8 +152,9 @@ public class HTMLHostActivity extends Activity mWebView = new WebView(this); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.setWebChromeClient(mChromeClient); - eventSender = new WebViewEventSender(mWebView); - mCallbackProxy = new CallbackProxy(eventSender, this); + mEventSender = new WebViewEventSender(mWebView); + mCallbackProxy = new CallbackProxy(mEventSender, this); + mFinishedRunning = false; mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController"); mWebView.addJavascriptInterface(mCallbackProxy, "eventSender"); @@ -168,32 +168,99 @@ public class HTMLHostActivity extends Activity super.onRestoreInstanceState(savedInstanceState); } + private void getTestList() { + // Read test list. + try { + BufferedReader inReader = new BufferedReader(new FileReader(LAYOUT_TESTS_LIST_FILE)); + String line = inReader.readLine(); + while (line != null) { + if (line.startsWith(mTestPathPrefix)) + mTestList.add(line); + line = inReader.readLine(); + } + inReader.close(); + Log.v(LOGTAG, "Test list has " + mTestList.size() + " test(s)."); + } catch (Exception e) { + Log.e(LOGTAG, "Error while reading test list : " + e.getMessage()); + } + } + + private void resumeTestList() { + // read out the test name it stoped last time. + try { + BufferedReader inReader = new BufferedReader(new FileReader(TEST_STATUS_FILE)); + String line = inReader.readLine(); + for (int i = 0; i < mTestList.size(); i++) { + if (mTestList.elementAt(i).equals(line)) { + mTestList = new Vector(mTestList.subList(i+1, mTestList.size())); + break; + } + } + inReader.close(); + } catch (Exception e) { + Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE); + } + } + + private void clearTestStatus() { + // Delete TEST_STATUS_FILE + try { + File f = new File(TEST_STATUS_FILE); + if (f.delete()) + Log.v(LOGTAG, "Deleted " + TEST_STATUS_FILE); + else + Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE); + } catch (Exception e) { + Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE + " : " + e.getMessage()); + } + } + + private void updateTestStatus(String s) { + // Write TEST_STATUS_FILE + try { + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_STATUS_FILE)); + bos.write(s.getBytes()); + bos.close(); + } catch (Exception e) { + Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE); + } + } + protected void onResume() { super.onResume(); - if (mProcessStack == null || mProcessStack.isEmpty() ) { - mOutstandingLoads = 0; - dumpAsText = false; - pageComplete = false; - - mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); - - mFinishedStack = new Stack(); - + if (mTestList == null) + mTestList = new Vector(); + + if (mTestList.isEmpty()) { + // Read settings Intent intent = getIntent(); - if (intent.getData() != null) { - File f = new File(intent.getData().toString()); - - if (f.isDirectory()) { - mProcessStack = new Vector(); - mProcessStack.add(new FileEntry(intent.getData().toString(), 0)); - Log.v(LOGTAG, "Initial dir: "+intent.getData().toString()); - loadNextPage(); - } else { - mCurrentFile = intent.getData().toString(); - mWebView.loadUrl("file://"+intent.getData().toString()); - } + mTestPathPrefix = intent.getStringExtra(TEST_PATH_PREFIX); + mSingleTestMode = intent.getBooleanExtra(SINGLE_TEST_MODE, false); + boolean resume = intent.getBooleanExtra(RESUME_FROM_CRASH, false); + mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 8000); + + mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); + + if (mTestPathPrefix == null) + throw new AssertionError("mTestPathPrefix cannot be null"); + + Log.v(LOGTAG, "Run tests with prefix: " + mTestPathPrefix); + mResultRecorder = new TestRecorder(resume); + + if (!resume) + clearTestStatus(); + + if (!mSingleTestMode) { + getTestList(); + if (resume) + resumeTestList(); + } else { + mTestList.add(mTestPathPrefix); } + + if (!mTestList.isEmpty()) + runTestAtIndex(0); else mWebView.loadUrl("about:"); } @@ -206,9 +273,17 @@ public class HTMLHostActivity extends Activity protected void onDestroy() { super.onDestroy(); + mResultRecorder.close(); mWebView.destroy(); mWebView = null; } + + public void onLowMemory() { + super.onLowMemory(); + // Simulate a crash + Log.e(LOGTAG, "Low memory, killing self"); + System.exit(1); + } public boolean dispatchKeyEvent(KeyEvent event) { // Log key strokes as they don't seem to be matched @@ -216,82 +291,51 @@ public class HTMLHostActivity extends Activity return super.dispatchKeyEvent(event); } - // Functions - - protected void loadNextPage() { - dumpAsText = false; - pageComplete = false; - dumpTitleChanges = false; - eventSender.resetMouse(); - while (!mProcessStack.isEmpty()) { - FileEntry fe = (FileEntry)mProcessStack.remove(0); - if (fe.mIndex == 0) { - System.out.println(); - System.out.print(fe.mPath); - } - Log.v(LOGTAG, "Processing dir: "+fe.mPath+" size: "+mProcessStack.size()); - File f = new File(fe.mPath); - String [] files = f.list(); - for (int i = fe.mIndex; i < files.length; i++) { - if (FileFilter.ignoreTest(files[i])) { - continue; - } - File c = new File(f.getPath(), files[i]); - if (c.isDirectory()) { - Log.v(LOGTAG, "Adding dir: "+fe.mPath+"/"+files[i]); - mProcessStack.add(new FileEntry(fe.mPath+"/"+files[i], 0)); - } else if (files[i].toLowerCase().endsWith("ml")) { - mProcessStack.add(0, new FileEntry(fe.mPath, i+1)); - mCurrentFile = fe.mPath+"/"+files[i]; - Log.e(LOGTAG, "Processing: "+mCurrentFile); - mWebView.loadUrl("file://"+mCurrentFile); + // Run a test at specified index in the test list. + // Stops activity if run out of tests. + protected void runTestAtIndex(int testIndex) { + mTestIndex = testIndex; + + resetTestStatus(); - // Create a timeout timer - Message m = mHandler.obtainMessage(MSG_TIMEOUT); - // Some tests can take up to 5secs to run. - mHandler.sendMessageDelayed(m, 6000); - return; - } - } - Log.v(LOGTAG, "Finished dir: "+fe.mPath+" size: "+mProcessStack.size()); - } - // If we got to here, then we must have finished completely - finished(); - } - - public void scheduleDump() { - // Only schedule if we really are ready - if (waitToDump || mOutstandingLoads > 0 || mDumpRequested) { + if (testIndex == mTestList.size()) { + finished(); return; } - mDumpRequested = true; - mHandler.obtainMessage(MSG_DUMP).sendToTarget(); + String s = mTestList.elementAt(testIndex); + if (!mSingleTestMode) + updateTestStatus(s); + + Log.v(LOGTAG, " Running test: "+s); + mWebView.loadUrl("file://"+s); + + if (!mSingleTestMode) { + // Create a timeout timer + Message m = mHandler.obtainMessage(MSG_TIMEOUT); + mHandler.sendMessageDelayed(m, mTimeoutInMillis); + } } - + // Dump the page public void dump(boolean timeout, String webkitData) { - mDumpRequested = false; - System.out.print('.'); - - // remove the extension - String resultFile = mCurrentFile.substring(0, mCurrentFile.lastIndexOf('.')); - - // store the finished file on the stack so that we can do a diff at the end. - mFinishedStack.push(resultFile); + String currentTest = mTestList.elementAt(mTestIndex); + String resultFile = currentTest.substring(0, currentTest.lastIndexOf('.')); // dumpAsText version can be directly compared to expected results - if (dumpAsText) { + if (mDumpAsText) { resultFile += "-results.txt"; } else { resultFile += "-android-results.txt"; } + try { FileOutputStream os = new FileOutputStream(resultFile); if (timeout) { - Log.i("Layout test: Timeout", resultFile); - os.write("**Test timeout\n".getBytes()); + Log.w("Layout test: Timeout", resultFile); + os.write(TIMEOUT_STR.getBytes()); + os.write('\n'); } - if (dumpTitleChanges) + if (mDumpTitleChanges) os.write(mTitleChanges.toString().getBytes()); if (mDialogStrings != null) os.write(mDialogStrings.toString().getBytes()); @@ -305,18 +349,15 @@ public class HTMLHostActivity extends Activity ex.printStackTrace(); } - if (mProcessStack != null) - loadNextPage(); - else - finished(); + processResult(timeout, currentTest); + runTestAtIndex(mTestIndex + 1); } // Wrap up public void failedCase(String file, String reason) { - Log.i("Layout test:", file + " failed" + reason); + Log.w("Layout test: ", file + " failed " + reason); mResultRecorder.failed(file, reason); - - file = file + ".html"; + String bugNumber = FileFilter.isKnownBug(file); if (bugNumber != null) { System.out.println("FAIL known:"+bugNumber+ " "+file+reason); @@ -327,13 +368,11 @@ public class HTMLHostActivity extends Activity } System.out.println("FAIL: "+file+reason); } - + public void passedCase(String file) { - // Add the result to the sqlite database - Log.i("Layout test:", file + " passed"); + Log.v("Layout test:", file + " passed"); mResultRecorder.passed(file); - file = file + ".html"; String bugNumber = FileFilter.isKnownBug(file); if (bugNumber != null) { System.out.println("Bug Fixed: "+bugNumber+ " "+file); @@ -345,90 +384,105 @@ public class HTMLHostActivity extends Activity return; } } + + public void nontextCase(String file) { + Log.v("Layout test:", file + " nontext"); + mResultRecorder.nontext(file); + } public void setCallback(HTMLHostCallbackInterface callback) { mCallback = callback; } - - public void finished() { - int passed = 0; - while (!mFinishedStack.empty()) { - Log.v(LOGTAG, "Comparing dump and reference"); - String file = (String)mFinishedStack.pop(); - // Only check results that we can check, ie dumpAsText results - String dumpFile = file + "-results.txt"; - File f = new File(dumpFile); - if (f.exists()) { - try { - FileInputStream fr = new FileInputStream(file+"-results.txt"); - FileInputStream fe = new FileInputStream(file+"-expected.txt"); - - mResultRecorder.started(file); - - // If the length is different then they are different - int diff = fe.available() - fr.available(); - if (diff > 1 || diff < 0) { - failedCase(file, " different length"); - fr.close(); - fe.close(); - - mResultRecorder.finished(file); - continue; - } - byte[] br = new byte[fr.available()]; - byte[] be = new byte[fe.available()]; - fr.read(br); - fe.read(be); - boolean fail = false; - for (int i = 0; i < br.length; i++) { - if (br[i] != be[i]) { - failedCase(file, " @offset: "+i); - fail = true; - break; - } - } - if (br.length != be.length && be[be.length-1] == '\n') { - Log.d(LOGTAG, "Extra new line being ignore:" + file); - } + public void processResult(boolean timeout, String test_path) { + Log.v(LOGTAG, " Processing result: " + test_path); + // remove the extension + String short_file = test_path.substring(0, test_path.lastIndexOf('.')); + if (timeout) { + failedCase(test_path, "TIMEDOUT"); + return; + } + // Only check results that we can check, ie dumpAsText results + String dumpFile = short_file + "-results.txt"; + File f = new File(dumpFile); + if (f.exists()) { + try { + FileInputStream fr = new FileInputStream(short_file+"-results.txt"); + FileInputStream fe = new FileInputStream(short_file+"-expected.txt"); + + // If the length is different then they are different + int diff = fe.available() - fr.available(); + if (diff > 1 || diff < 0) { + failedCase(test_path, " different length"); fr.close(); fe.close(); - if (!fail) { - passed++; - passedCase(file); - } - } catch (FileNotFoundException ex) { - // TODO do something here - } catch (IOException ex) { - // Failed on available() or read() + return; } - mResultRecorder.finished(file); + byte[] br = new byte[fr.available()]; + byte[] be = new byte[fe.available()]; + fr.read(br); + fe.read(be); + boolean fail = false; + for (int i = 0; i < br.length; i++) { + if (br[i] != be[i]) { + failedCase(test_path, " @offset: "+i); + fr.close(); + fe.close(); + return; + } + } + if (br.length != be.length && be[be.length-1] == '\n') { + Log.d(LOGTAG, "Extra new line being ignore:" + test_path); + } + fr.close(); + fe.close(); + passedCase(test_path); + } catch (FileNotFoundException ex) { + // TODO do something here + } catch (IOException ex) { + // Failed on available() or read() } - } + + return; + } - if (mCallback != null) { + File nontext_result = new File(short_file + "-android-results.txt"); + if (nontext_result.exists()) + mResultRecorder.nontext(test_path); + } + + public void finished() { + if (mCallback != null) { mCallback.waitForFinish(); - } - + } + + mFinishedRunning = true; finish(); } // LayoutTestController Functions public void dumpAsText() { - dumpAsText = true; - String url = mWebView.getUrl(); - Log.v(LOGTAG, "dumpAsText called:"+url); - if (url.length() > 60) - url = url.substring(60); + mDumpAsText = true; + if (mWebView != null) { + String url = mWebView.getUrl(); + Log.v(LOGTAG, "dumpAsText called: "+url); + } } public void waitUntilDone() { - waitToDump = true; + mWaitUntilDone = true; + String url = mWebView.getUrl(); + Log.v(LOGTAG, "waitUntilDone called: " + url); } public void notifyDone() { - waitToDump = false; - mChromeClient.onProgressChanged(mWebView, 100); + String url = mWebView.getUrl(); + Log.v(LOGTAG, "notifyDone called: " + url); + if (mWaitUntilDone) { + mWaitUntilDone = false; + mChromeClient.onProgressChanged(mWebView, 100); + } } + public void display() { mWebView.invalidate(); } @@ -461,18 +515,17 @@ public class HTMLHostActivity extends Activity } public void dumpTitleChanges() { - if (!dumpTitleChanges) { + if (!mDumpTitleChanges) { mTitleChanges = new StringBuffer(); } - dumpTitleChanges = true; + mDumpTitleChanges = true; } public void keepWebHistory() { - if (!keepWebHistory) { + if (!mKeepWebHistory) { mWebHistory = new Vector(); } - keepWebHistory = true; - + mKeepWebHistory = true; } public void queueBackNavigation(int howfar) { @@ -491,7 +544,7 @@ public class HTMLHostActivity extends Activity } public void queueReload() { - mWebView.reload(); + mWebView.reload(); } public void queueScript(String scriptToRunInCurrentContext) { @@ -520,33 +573,36 @@ public class HTMLHostActivity extends Activity } public void testRepaint() { - mWebView.invalidate(); + mWebView.invalidate(); } // Instrumentation calls this to find // if the activity has finished running the layout tests + // TODO(fqian): need to sync on mFinisheRunning public boolean hasFinishedRunning() { - if( mProcessStack == null || mFinishedStack == null) - return false; - - if (mProcessStack.isEmpty() && mFinishedStack.empty()) { - return true; - } - - return false; + return mFinishedRunning; } - + private final WebChromeClient mChromeClient = new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { if (newProgress == 100) { - pageComplete = true; - String url = mWebView.getUrl(); - if (url != null) { + if (!mSingleTestMode && !mTimedOut && !mWaitUntilDone && !mRequestedWebKitData) { + String url = mWebView.getUrl(); Log.v(LOGTAG, "Finished: "+ url); - if (url.length() > 60) - url = url.substring(60); - scheduleDump(); + mHandler.removeMessages(MSG_TIMEOUT); + requestWebKitData(); + } else { + String url = mWebView.getUrl(); + if (mSingleTestMode) { + Log.v(LOGTAG, "Single test mode: " + url); + } else if (mTimedOut) { + Log.v(LOGTAG, "Timed out before finishing: " + url); + } else if (mWaitUntilDone) { + Log.v(LOGTAG, "Waiting for notifyDone: " + url); + } else if (mRequestedWebKitData) { + Log.v(LOGTAG, "Requested webkit data ready: " + url); + } } } } @@ -556,7 +612,7 @@ public class HTMLHostActivity extends Activity if (title.length() > 30) title = "..."+title.substring(title.length()-30); setTitle(title); - if (dumpTitleChanges) { + if (mDumpTitleChanges) { mTitleChanges.append("TITLE CHANGED: "); mTitleChanges.append(title); mTitleChanges.append("\n"); @@ -572,36 +628,90 @@ public class HTMLHostActivity extends Activity mDialogStrings.append("ALERT: "); mDialogStrings.append(message); mDialogStrings.append('\n'); - return false; + result.confirm(); + return true; + } + + @Override + public boolean onJsConfirm(WebView view, String url, String message, + JsResult result) { + if (mDialogStrings == null) { + mDialogStrings = new StringBuffer(); + } + mDialogStrings.append("CONFIRM: "); + mDialogStrings.append(message); + mDialogStrings.append('\n'); + result.confirm(); + return true; + } + + @Override + public boolean onJsPrompt(WebView view, String url, String message, + String defaultValue, JsPromptResult result) { + if (mDialogStrings == null) { + mDialogStrings = new StringBuffer(); + } + mDialogStrings.append("PROMPT: "); + mDialogStrings.append(message); + mDialogStrings.append(", default text: "); + mDialogStrings.append(defaultValue); + mDialogStrings.append('\n'); + result.confirm(); + return true; } }; - + + private void resetTestStatus() { + mWaitUntilDone = false; + mDumpAsText = false; + mTimedOut = false; + mDumpTitleChanges = false; + mRequestedWebKitData = false; + mEventSender.resetMouse(); + } + + private TestRecorder mResultRecorder; + private HTMLHostCallbackInterface mCallback = null; + private CallbackProxy mCallbackProxy; + private WebView mWebView; - private WebViewEventSender eventSender; - private Vector mProcessStack; - private Stack mFinishedStack; - static final String LOGTAG="DumpRenderTree"; - private String mCurrentFile; - private int mOutstandingLoads; + private WebViewEventSender mEventSender; + + private Vector mTestList; + private int mTestIndex; + + private int mTimeoutInMillis; + private String mTestPathPrefix; + private boolean mSingleTestMode; + private AsyncHandler mHandler; - private boolean mDumpRequested; - - private boolean dumpAsText; - private boolean waitToDump; - private boolean pageComplete; - - private boolean dumpTitleChanges; - private StringBuffer mTitleChanges; - - private StringBuffer mDialogStrings; - - private boolean keepWebHistory; - private Vector mWebHistory; + private boolean mFinishedRunning; private boolean mTimedOut; + private boolean mRequestedWebKitData; + private boolean mDumpAsText; + private boolean mWaitUntilDone; + private boolean mDumpTitleChanges; + + private StringBuffer mTitleChanges; + private StringBuffer mDialogStrings; - static final int MSG_DUMP = 0; - static final int MSG_TIMEOUT = 1; - static final int MSG_WEBKIT_DATA = 2; + private boolean mKeepWebHistory; + private Vector mWebHistory; + static final String TIMEOUT_STR = "**Test timeout"; + + static final int MSG_TIMEOUT = 0; + static final int MSG_WEBKIT_DATA = 1; + + static final String LOGTAG="DumpRenderTree"; + + static final String LAYOUT_TESTS_ROOT = "/sdcard/android/layout_tests/"; + static final String LAYOUT_TESTS_LIST_FILE = "/sdcard/layout_tests_list.txt"; + static final String TEST_STATUS_FILE = "/sdcard/running_test.txt"; + + static final String RESUME_FROM_CRASH = "ResumeFromCrash"; + static final String TEST_PATH_PREFIX = "TestPathPrefix"; + static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis"; + static final String SINGLE_TEST_MODE = "SingleTestMode"; } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java index e0e535e356b1d..1f37405c8e67c 100755 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java @@ -21,6 +21,9 @@ import com.android.dumprendertree.LayoutTestsAutoTest; import android.test.InstrumentationTestRunner; import android.test.InstrumentationTestSuite; +import android.util.Log; +import android.content.Intent; +import android.os.Bundle; /** @@ -44,5 +47,22 @@ public class LayoutTestsAutoRunner extends InstrumentationTestRunner { public ClassLoader getLoader() { return LayoutTestsAutoRunner.class.getClassLoader(); } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + String path = (String) icicle.get("path"); + LayoutTestsAutoTest.setLayoutTestDir(path); + String timeout_str = (String) icicle.get("timeout"); + int timeout = 0; // default value + if (timeout_str != null) { + try { + timeout = Integer.parseInt(timeout_str); + } catch (Exception e) { + e.printStackTrace(); + } + } + LayoutTestsAutoTest.setTimeoutInMillis(timeout); + } } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java index f46b263bfc7ce..3e65f03588a3b 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java @@ -16,142 +16,157 @@ package com.android.dumprendertree; +import android.app.Activity; import android.app.Instrumentation; import android.app.Instrumentation.ActivityMonitor; import android.content.ContentResolver; import android.content.ContentValues; +import android.content.Intent; + import android.util.Log; import android.view.KeyEvent; +import android.os.Bundle; +import android.os.Message; import android.test.ActivityInstrumentationTestCase; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import com.android.dumprendertree.HTMLHostActivity; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase { private final static String LOGTAG = "LayoutTests"; - private final static String LAYOUT_TESTS_ROOT = "/sdcard/android/layout_tests/"; - + private final static int DEFAULT_TIMEOUT_IN_MILLIS = 6000; + private static String layoutTestDir = null; + private static int mTimeoutInMillis = 0; + public LayoutTestsAutoTest() { super("com.android.dumprendertree", Menu.class); } + // This function writes the result of the layout test to + // Am status so that it can be picked up from a script. + public void passOrFailCallback(String file, boolean result) { + Instrumentation inst = getInstrumentation(); + Bundle bundle = new Bundle(); + bundle.putBoolean(file, result); + inst.sendStatus(0, bundle); + } + + public static void setTimeoutInMillis(int millis) { + mTimeoutInMillis = (millis > 0) ? millis : DEFAULT_TIMEOUT_IN_MILLIS; + } + + public static void setLayoutTestDir(String name) { + if (name == null) + throw new AssertionError("Layout test directory cannot be null."); + layoutTestDir = HTMLHostActivity.LAYOUT_TESTS_ROOT + name; + Log.v("LayoutTestsAutoTest", " Only running the layout tests : " + layoutTestDir); + } + // Invokes running of layout tests // and waits till it has finished running. - public void executeLayoutTests(String folder) { + public void executeLayoutTests(boolean resume) { Instrumentation inst = getInstrumentation(); - getActivity().processFile(folder, true); - + + { + Activity activity = getActivity(); + Intent intent = new Intent(); + intent.setClass(activity, HTMLHostActivity.class); + intent.putExtra(HTMLHostActivity.RESUME_FROM_CRASH, resume); + intent.putExtra(HTMLHostActivity.SINGLE_TEST_MODE, false); + intent.putExtra(HTMLHostActivity.TEST_PATH_PREFIX, layoutTestDir); + intent.putExtra(HTMLHostActivity.TIMEOUT_IN_MILLIS, mTimeoutInMillis); + activity.startActivity(intent); + } + ActivityMonitor htmlHostActivityMonitor = inst.addMonitor("com.android.dumprendertree.HTMLHostActivity", null, false); + HTMLHostActivity activity = - (HTMLHostActivity) htmlHostActivityMonitor.waitForActivityWithTimeout(6000); - + (HTMLHostActivity) htmlHostActivityMonitor.waitForActivity(); + while (!activity.hasFinishedRunning()) { // Poll every 5 seconds to determine if the layout // tests have finished running try {Thread.sleep(5000); } catch(Exception e){} } - + // Wait few more seconds so that results are // flushed to the /sdcard try {Thread.sleep(5000); } catch(Exception e){} - return ; + // Clean up the HTMLHostActivity activity + activity.finish(); + } + + public void generateTestList() { + try { + File tests_list = new File(HTMLHostActivity.LAYOUT_TESTS_LIST_FILE); + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false)); + findTestsRecursively(bos, layoutTestDir); + bos.flush(); + bos.close(); + } catch (Exception e) { + Log.e(LOGTAG, "Error when creating test list: " + e.getMessage()); + } + } + + private void findTestsRecursively(BufferedOutputStream bos, String dir) throws IOException { + Log.v(LOGTAG, "Searching tests under " + dir); + + File d = new File(dir); + if (!d.isDirectory()) { + throw new AssertionError("A directory expected, but got " + dir); + } + + String[] files = d.list(); + for (int i = 0; i < files.length; i++) { + String s = dir + "/" + files[i]; + if (FileFilter.ignoreTest(s)) { + Log.v(LOGTAG, " Ignoring: " + s); + continue; + } + if (s.toLowerCase().endsWith(".html") + || s.toLowerCase().endsWith(".xml")) { + bos.write(s.getBytes()); + bos.write('\n'); + continue; + } + + File f = new File(s); + if (f.isDirectory()) { + findTestsRecursively(bos, s); + continue; + } + + Log.v(LOGTAG, "Skipping " + s); + } } // Running all the layout tests at once sometimes // causes the dumprendertree to run out of memory. // So, additional tests are added to run the tests // in chunks. - @LargeTest - public void testAllLayoutTests() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast"); + public void startLayoutTests() { + try { + File tests_list = new File(HTMLHostActivity.LAYOUT_TESTS_LIST_FILE); + if (!tests_list.exists()) + generateTestList(); + } catch (Exception e) { + e.printStackTrace(); + } + + executeLayoutTests(false); } - @LargeTest - public void testLayoutSubset1() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/backgrounds"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/borders"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/box-shadow"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/box-sizing"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/canvas"); - } - - @LargeTest - public void testLayoutSubset2() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/clip"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/compact"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/cookies"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/css"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/css-generated-content"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/doctypes"); - } - - @LargeTest - public void testLayoutSubset3() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/dom"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/dynamic"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/encoding"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/events"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/flexbox"); - } - - @LargeTest - public void testLayoutSubset4() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/forms"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/frames"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/gradients"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/history"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/html"); - } - - @LargeTest - public void testLayoutSubset5() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/images"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/inline"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/inline-block"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/innerHTML"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/invalid"); - } - - @LargeTest - public void testLayoutSubset6() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/js"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/layers"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/leaks"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/lists"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/loader"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/media"); - } - - @LargeTest - public void testLayoutSubset7() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/multicol"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/overflow"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/parser"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/profiler"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/reflections"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/regex"); - } - - @LargeTest - public void testLayoutSubset8() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/repaint"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/replaced"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/runin"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/selectors"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/table"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/text"); - } - - @LargeTest - public void testLayoutSubset9() { - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/tokenizer"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/transforms"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/xpath"); - executeLayoutTests(LAYOUT_TESTS_ROOT + "fast/xsl"); + public void resumeLayoutTests() { + executeLayoutTests(true); } } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java index 2def8f353e3e9..de0da61d5542c 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java @@ -43,9 +43,12 @@ public class Menu extends FileList { void processFile(String filename, boolean selection) { - Intent result = new Intent(null, Uri.parse(filename)); + Intent result = new Intent(); result.setClass(this, HTMLHostActivity.class); - result.putExtra("ReturnWhenFinished", selection); + result.putExtra(HTMLHostActivity.RESUME_FROM_CRASH, false); + result.putExtra(HTMLHostActivity.SINGLE_TEST_MODE, true); + result.putExtra(HTMLHostActivity.TEST_PATH_PREFIX, filename); + result.putExtra(HTMLHostActivity.TIMEOUT_IN_MILLIS, 8000); startActivity(result); } diff --git a/tests/FrameworkTest/AndroidManifest.xml b/tests/FrameworkTest/AndroidManifest.xml index 8106a50e2eca5..c0824c06bf5f0 100644 --- a/tests/FrameworkTest/AndroidManifest.xml +++ b/tests/FrameworkTest/AndroidManifest.xml @@ -845,6 +845,13 @@ + + + + + + + diff --git a/tests/FrameworkTest/src/com/android/frameworktest/drawable/MutateDrawable.java b/tests/FrameworkTest/src/com/android/frameworktest/drawable/MutateDrawable.java new file mode 100644 index 0000000000000..2fcaea3f8db7d --- /dev/null +++ b/tests/FrameworkTest/src/com/android/frameworktest/drawable/MutateDrawable.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.frameworktest.drawable; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.LinearLayout; +import android.widget.Button; +import com.android.frameworktest.R; + +public class MutateDrawable extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + + Button ok = new Button(this); + ok.setId(R.id.a); + ok.setBackgroundDrawable(getResources().getDrawable( + R.drawable.sym_now_playing_skip_forward_1)); + + Button cancel = new Button(this); + cancel.setId(R.id.b); + cancel.setBackgroundDrawable(getResources().getDrawable( + R.drawable.sym_now_playing_skip_forward_1)); + + layout.addView(ok); + layout.addView(cancel); + + ok.getBackground().mutate().setAlpha(127); + + setContentView(layout); + } +} diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/drawable/MutateDrawableTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/drawable/MutateDrawableTest.java new file mode 100644 index 0000000000000..53085ca9b681b --- /dev/null +++ b/tests/FrameworkTest/tests/src/com/android/frameworktest/drawable/MutateDrawableTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.frameworktest.drawable; + +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.MediumTest; +import android.view.View; + +public class MutateDrawableTest extends ActivityInstrumentationTestCase2 { + private View mFirstButton; + private View mSecondButton; + + public MutateDrawableTest() { + super("com.android.frameworktest", MutateDrawable.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mFirstButton = getActivity().findViewById(com.android.frameworktest.R.id.a); + mSecondButton = getActivity().findViewById(com.android.frameworktest.R.id.b); + } + + @MediumTest + public void testSetUpConditions() throws Exception { + assertNotNull(mFirstButton); + assertNotNull(mSecondButton); + assertNotSame(mFirstButton.getBackground(), mSecondButton.getBackground()); + } + + @MediumTest + public void testDrawableCanMutate() throws Exception { + assertNotSame(mFirstButton.getBackground().getConstantState(), + mSecondButton.getBackground().getConstantState()); + } +} diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/listview/ListScrollListenerTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/listview/ListScrollListenerTest.java index 485199edc7791..44958d90c1eba 100644 --- a/tests/FrameworkTest/tests/src/com/android/frameworktest/listview/ListScrollListenerTest.java +++ b/tests/FrameworkTest/tests/src/com/android/frameworktest/listview/ListScrollListenerTest.java @@ -24,7 +24,6 @@ import android.view.KeyEvent; import android.widget.AbsListView; import android.widget.ListView; -import com.android.frameworktest.listview.ListScrollListener; import android.test.TouchUtils; public class ListScrollListenerTest extends ActivityInstrumentationTestCase implements @@ -88,8 +87,6 @@ public class ListScrollListenerTest extends ActivityInstrumentationTestCase mInstalled; - - public GadgetPickActivity() { - mGadgetManager = GadgetManager.getInstance(this); - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - Bundle extras = getIntent().getExtras(); - - List installed = mGadgetManager.getInstalledProviders(); - mInstalled = installed; - final int N = installed.size(); - String[] labels = new String[N]; - for (int i=0; i(this, android.R.layout.simple_list_item_1, labels)); - } - - @Override - public void onListItemClick(ListView l, View v, int position, long id) - { - int gadgetId = mGadgetManager.allocateGadgetId(getCallingPackage()); - mGadgetManager.bindGadgetId(gadgetId, mInstalled.get(position).provider); - - Intent result = new Intent(); - result.putExtra(GadgetManager.EXTRA_GADGET_ID, gadgetId); - - setResult(RESULT_OK, result); - finish(); - } -} - diff --git a/tests/ImfTest/AndroidManifest.xml b/tests/ImfTest/AndroidManifest.xml index 85d6b0ce862c4..627ee6dc3dcbb 100755 --- a/tests/ImfTest/AndroidManifest.xml +++ b/tests/ImfTest/AndroidManifest.xml @@ -19,6 +19,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml b/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml new file mode 100644 index 0000000000000..e8ffa1ce8fc86 --- /dev/null +++ b/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml @@ -0,0 +1,36 @@ + + + + + + + + + + diff --git a/tests/ImfTest/res/layout/full_screen_edit_text.xml b/tests/ImfTest/res/layout/full_screen_edit_text.xml new file mode 100755 index 0000000000000..f22aa2f724cb7 --- /dev/null +++ b/tests/ImfTest/res/layout/full_screen_edit_text.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/tests/ImfTest/res/layout/one_edit_text_activity.xml b/tests/ImfTest/res/layout/one_edit_text_activity.xml new file mode 100755 index 0000000000000..09925e1e76cbc --- /dev/null +++ b/tests/ImfTest/res/layout/one_edit_text_activity.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + diff --git a/tests/ImfTest/res/values/strings.xml b/tests/ImfTest/res/values/strings.xml index a56c36375f1c5..fc87480788d63 100755 --- a/tests/ImfTest/res/values/strings.xml +++ b/tests/ImfTest/res/values/strings.xml @@ -35,5 +35,16 @@ Datetime Date Time + Cap Chars + Cap Words + Multiline + Search (flag) + Cap Sentences + Auto Complete + Auto Correct + Test Dialog + open scrollable dialog + open nonscrollable dialog + diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollablePanScan.java new file mode 100644 index 0000000000000..15a29c8009288 --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollablePanScan.java @@ -0,0 +1,35 @@ +package com.android.imftest.samples; + +import com.android.imftest.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.LinearLayout; + +public class AppAdjustmentBigEditTextNonScrollablePanScan extends Activity { + + private LinearLayout mLayout; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + + mLayout = new LinearLayout(this); + mLayout.setOrientation(LinearLayout.VERTICAL); + mLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + EditText editText = (EditText) getLayoutInflater().inflate(R.layout.full_screen_edit_text, mLayout, false); + + mLayout.addView(editText); + + setContentView(mLayout); + } + +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollableResize.java new file mode 100644 index 0000000000000..07268235670fb --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextNonScrollableResize.java @@ -0,0 +1,35 @@ +package com.android.imftest.samples; + +import com.android.imftest.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.LinearLayout; + +public class AppAdjustmentBigEditTextNonScrollableResize extends Activity { + + private LinearLayout mLayout; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + mLayout = new LinearLayout(this); + mLayout.setOrientation(LinearLayout.VERTICAL); + mLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + EditText editText = (EditText) getLayoutInflater().inflate(R.layout.full_screen_edit_text, mLayout, false); + + mLayout.addView(editText); + + setContentView(mLayout); + } + +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollablePanScan.java new file mode 100644 index 0000000000000..50a980b325c6e --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollablePanScan.java @@ -0,0 +1,44 @@ +package com.android.imftest.samples; + +import com.android.imftest.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ScrollView; + +public class AppAdjustmentBigEditTextScrollablePanScan extends Activity { + + private ScrollView mScrollView; + private LinearLayout mLayout; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + + mScrollView = new ScrollView(this); + mScrollView.setFillViewport(true); + mScrollView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + mLayout = new LinearLayout(this); + mLayout.setOrientation(LinearLayout.VERTICAL); + mLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + EditText editText = (EditText) getLayoutInflater().inflate(R.layout.full_screen_edit_text, mScrollView, false); + + mLayout.addView(editText); + mScrollView.addView(mLayout); + + setContentView(mScrollView); + } + +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollableResize.java new file mode 100644 index 0000000000000..a256878c55737 --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentBigEditTextScrollableResize.java @@ -0,0 +1,44 @@ +package com.android.imftest.samples; + +import com.android.imftest.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ScrollView; + +public class AppAdjustmentBigEditTextScrollableResize extends Activity { + + private ScrollView mScrollView; + private LinearLayout mLayout; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + mScrollView = new ScrollView(this); + mScrollView.setFillViewport(true); + mScrollView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + mLayout = new LinearLayout(this); + mLayout.setOrientation(LinearLayout.VERTICAL); + mLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + EditText editText = (EditText) getLayoutInflater().inflate(R.layout.full_screen_edit_text, mScrollView, false); + + mLayout.addView(editText); + mScrollView.addView(mLayout); + + setContentView(mScrollView); + } + +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentEditTextDialog.java b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentEditTextDialog.java new file mode 100644 index 0000000000000..e82f1d50238c8 --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/AppAdjustmentEditTextDialog.java @@ -0,0 +1,96 @@ +package com.android.imftest.samples; + +import com.android.imftest.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ScrollView; + +public class AppAdjustmentEditTextDialog extends Activity { + + private static final int SCROLLABLE_DIALOG_ID = 0; + private static final int NONSCROLLABLE_DIALOG_ID = 1; + + private LinearLayout mLayout; + private ScrollView mScrollView; + private LayoutInflater mInflater; + private Button mButton1; + private Button mButton2; + + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mLayout = new LinearLayout(this); + mLayout.setOrientation(LinearLayout.VERTICAL); + mLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + mButton1 = new Button(this); + mButton1.setText(R.string.open_dialog_scrollable); + mButton1.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + showDialog(SCROLLABLE_DIALOG_ID); + } + }); + + mButton2 = new Button(this); + mButton2.setText(R.string.open_dialog_nonscrollable); + mButton2.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + showDialog(NONSCROLLABLE_DIALOG_ID); + } + }); + + mLayout.addView(mButton1); + mLayout.addView(mButton2); + + setContentView(mLayout); + } + + @Override + protected Dialog onCreateDialog(int id) { + switch (id) { + case SCROLLABLE_DIALOG_ID: + return createDialog(true); + case NONSCROLLABLE_DIALOG_ID: + return createDialog(false); + } + + return super.onCreateDialog(id); + } + + protected Dialog createDialog(boolean scrollable) { + View layout; + EditText editText; + + if (scrollable) { + layout = new ScrollView(AppAdjustmentEditTextDialog.this); + ((ScrollView) layout).setMinimumHeight(mLayout.getHeight()); + + ((ScrollView) layout).addView(( + LinearLayout) View.inflate(AppAdjustmentEditTextDialog.this, + R.layout.dialog_edit_text_no_scroll, null)); + } else { + layout = View.inflate(AppAdjustmentEditTextDialog.this, + R.layout.dialog_edit_text_no_scroll, null); + } + + Dialog d = new Dialog(AppAdjustmentEditTextDialog.this); + d.setTitle(getString(R.string.test_dialog)); + d.setCancelable(true); + d.setContentView(layout); + return d; + } + +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java new file mode 100644 index 0000000000000..9638d34ab567c --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java @@ -0,0 +1,85 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.app.ActivityManagerNative; +import android.os.Bundle; +import android.os.RemoteException; +import android.provider.MediaStore; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.AutoCompleteTextView; +import android.widget.ArrayAdapter; +import android.content.Intent; +import android.content.pm.ActivityInfo; + +import com.android.internal.R; + +/* + * Activity with AutoCompleteTextView forced to landscape mode + */ +public class AutoCompleteTextViewActivityLandscape extends Activity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + setContentView(R.layout.auto_complete_list); + + ArrayAdapter adapter = new ArrayAdapter(this, + android.R.layout.simple_dropdown_item_1line, COUNTRIES); + AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.edit); + textView.setAdapter(adapter); + } + + static final String[] COUNTRIES = new String[] { + "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", + "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", + "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", + "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", + "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", + "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", + "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi", + "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde", + "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", + "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", + "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic", + "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic", + "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", + "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland", + "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia", + "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar", + "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau", + "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary", + "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica", + "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos", + "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", + "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", + "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova", + "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", + "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", + "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas", + "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", + "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar", + "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena", + "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon", + "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal", + "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", + "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea", + "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden", + "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas", + "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", + "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda", + "Ukraine", "United Arab Emirates", "United Kingdom", + "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", + "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara", + "Yemen", "Yugoslavia", "Zambia", "Zimbabwe" + }; +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java new file mode 100644 index 0000000000000..58651e1bfb86e --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java @@ -0,0 +1,79 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.AutoCompleteTextView; +import android.widget.ArrayAdapter; + +import com.android.internal.R; + +/* + * Activity with AutoCompleteTextView (Candidate bar should not appear) + */ +public class AutoCompleteTextViewActivityPortrait extends Activity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + setContentView(R.layout.auto_complete_list); + + ArrayAdapter adapter = new ArrayAdapter(this, + android.R.layout.simple_dropdown_item_1line, COUNTRIES); + AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.edit); + textView.setAdapter(adapter); + } + + static final String[] COUNTRIES = new String[] { + "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", + "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", + "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", + "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", + "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", + "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", + "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi", + "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde", + "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", + "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", + "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic", + "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic", + "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", + "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland", + "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia", + "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar", + "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau", + "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary", + "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica", + "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos", + "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", + "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", + "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova", + "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", + "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", + "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas", + "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", + "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar", + "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena", + "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon", + "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal", + "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", + "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea", + "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden", + "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas", + "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", + "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda", + "Ukraine", "United Arab Emirates", "United Kingdom", + "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", + "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara", + "Yemen", "Yugoslavia", "Zambia", "Zimbabwe" + }; +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java new file mode 100644 index 0000000000000..d74b9dd9d9eb2 --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java @@ -0,0 +1,37 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.android.imftest.R; + +/* + * Activity with EditText at the bottom (Pan&Scan) + */ +public class BottomEditTextActivityPanScan extends Activity +{ + private LayoutInflater mInflater; + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + mInflater = getLayoutInflater(); + + View view = mInflater.inflate(R.layout.one_edit_text_activity, layout, false); + layout.addView(view); + + setContentView(layout); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + } +} \ No newline at end of file diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java new file mode 100644 index 0000000000000..82da29a3af38c --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java @@ -0,0 +1,37 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.android.imftest.R; + +/* + * Activity with EditText at the bottom (Resize) + */ +public class BottomEditTextActivityResize extends Activity +{ + private LayoutInflater mInflater; + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + mInflater = getLayoutInflater(); + + View view = mInflater.inflate(R.layout.one_edit_text_activity, layout, false); + layout.addView(view); + + setContentView(layout); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + } +} \ No newline at end of file diff --git a/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java b/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java new file mode 100644 index 0000000000000..e49301cd9821b --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java @@ -0,0 +1,102 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.EditText; +import android.widget.Button; +import android.view.LayoutInflater; +import android.app.Dialog; + +import com.android.internal.R; + +public class DialogActivity extends Activity { + + private static final int DIALOG_WITHOUT_EDITTEXT = 0; + private static final int DIALOG_WITH_EDITTEXT = 1; + + private LinearLayout mLayout; + private LayoutInflater mInflater; + private Button mButton1; + private Button mButton2; + private EditText mEditText; + + + @Override + protected void onCreate(Bundle icicle) + { + super.onCreate(icicle); + + mLayout = new LinearLayout(this); + mLayout.setOrientation(LinearLayout.VERTICAL); + mLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + + mButton1 = new Button(this); + mButton1.setText("Dialog WITHOUT EditText");//(R.string.open_dialog_scrollable); + mButton1.setOnClickListener(new View.OnClickListener() + { + public void onClick(View v) + { + showDialog(DIALOG_WITHOUT_EDITTEXT); + } + }); + + mButton2 = new Button(this); + mButton2.setText("Dialog WITH EditText");//(R.string.open_dialog_nonscrollable); + mButton2.setOnClickListener(new View.OnClickListener() + { + public void onClick(View v) + { + showDialog(DIALOG_WITH_EDITTEXT); + } + }); + + mEditText = new EditText(this); + mLayout.addView(mEditText); + mLayout.addView(mButton1); + mLayout.addView(mButton2); + + setContentView(mLayout); + } + + @Override + protected Dialog onCreateDialog(int id) + { + switch (id) + { + case DIALOG_WITHOUT_EDITTEXT: + return createDialog(false); + case DIALOG_WITH_EDITTEXT: + return createDialog(true); + } + + return super.onCreateDialog(id); + } + + protected Dialog createDialog(boolean bEditText) + { + LinearLayout layout; + layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + if(bEditText) + { + EditText editText; + editText = new EditText(this); + layout.addView(editText); + } + + Dialog d = new Dialog(this); + d.setTitle("The DIALOG!!!"); + d.setCancelable(true); + d.setContentView(layout); + return d; + } + + } diff --git a/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityNoScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityNoScrollPanScan.java new file mode 100644 index 0000000000000..b59602310856a --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityNoScrollPanScan.java @@ -0,0 +1,38 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.Button; +import android.widget.TextView; +import android.widget.ScrollView; + +import com.android.internal.R; + +public class EditTextActivityNoScrollPanScan extends Activity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + String string = new String(); + for (int i=0; i<9; i++) + { + final EditText editText = new EditText(this); + editText.setText(string.valueOf(i)); + layout.addView(editText); + } + setContentView(layout); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + } +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java b/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java index f7aaa7bbabda9..6c71e86f0dcbe 100755 --- a/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java +++ b/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java @@ -56,43 +56,67 @@ public class InputTypeActivity extends Activity { mParent = mLayout; /* Normal Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_NORMAL, + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL, R.string.normal_edit_text_label)); + /* Normal Edit Text w/Cap Chars Flag*/ + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS, + R.string.cap_chars_edit_text_label)); + + /* Normal Edit Text w/Cap Words Flag*/ + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS, + R.string.cap_words_edit_text_label)); + + /* Normal Edit Text w/Cap Multiline Flag */ + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE, + R.string.multiline_edit_text_label)); + + /* Normal Edit Text w/Cap Sentences Flag */ + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES, + R.string.cap_sentences_edit_text_label)); + + /* Normal Edit Text w/Auto-complete Flag */ + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE, + R.string.auto_complete_edit_text_label)); + + /* Normal Edit Text w/Auto-correct Flag */ + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT, + R.string.auto_correct_edit_text_label)); + + /* Normal Edit Text w/Search Flag*/ + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_SEARCH, + R.string.search_edit_text_label)); + /* Uri Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_URI, + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_URI, R.string.uri_edit_text_label)); /* Email Address Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, R.string.email_address_edit_text_label)); /* Email Subject Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT, + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT, R.string.email_subject_edit_text_label)); /* Email Content Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_EMAIL_CONTENT, + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE, R.string.email_content_edit_text_label)); /* Person Name Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME, + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME, R.string.person_name_edit_text_label)); /* Postal Address Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, R.string.postal_address_edit_text_label)); /* Password Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD, + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_PASSWORD, R.string.password_edit_text_label)); - /* Search String Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_SEARCH_STRING, - R.string.search_string_edit_text_label)); - /* Web Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT, + mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT, R.string.web_edit_text_label)); /* Signed Number Edit Text */ diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java new file mode 100644 index 0000000000000..4cb3af6ab81cd --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java @@ -0,0 +1,41 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.Button; +import android.widget.TextView; +import android.widget.ScrollView; + +import com.android.internal.R; + +/* + * Full screen of EditTexts (Non-Scrollable, Pan&Scan) + */ +public class ManyEditTextActivityNoScrollPanScan extends Activity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + String string = new String(); + for (int i=0; i<9; i++) + { + final EditText editText = new EditText(this); + editText.setText(string.valueOf(i)); + layout.addView(editText); + } + setContentView(layout); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + } +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java new file mode 100644 index 0000000000000..bd32828a1d08e --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java @@ -0,0 +1,45 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.Button; +import android.widget.TextView; +import android.widget.ScrollView; + +import com.android.internal.R; + +/* + * Full screen of EditTexts (Scrollable, Pan&Scan) + */ +public class ManyEditTextActivityScrollPanScan extends Activity +{ + private ScrollView mScrollView; + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + mScrollView = new ScrollView(this); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + String string = new String(); + for (int i=0; i<12; i++) + { + final EditText editText = new EditText(this); + editText.setText(string.valueOf(i)); + layout.addView(editText); + } + + mScrollView.addView(layout); + setContentView(mScrollView); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + } +} \ No newline at end of file diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java new file mode 100644 index 0000000000000..eaaa98bf05c73 --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java @@ -0,0 +1,37 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.EditText; +import android.widget.ScrollView; + +/* + * Full screen of EditTexts (Scrollable, Resize) + */ +public class ManyEditTextActivityScrollResize extends Activity +{ + private ScrollView mScrollView; + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + mScrollView = new ScrollView(this); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + String string = new String(); + for (int i=0; i<12; i++) + { + final EditText editText = new EditText(this); + editText.setText(string.valueOf(i)); + layout.addView(editText); + } + + mScrollView.addView(layout); + setContentView(mScrollView); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + } +} \ No newline at end of file diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java new file mode 100644 index 0000000000000..5fef8840cd293 --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java @@ -0,0 +1,40 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.Button; +import android.widget.TextView; +import android.widget.ScrollView; + +import com.android.internal.R; + +/* + * Activity with non-EditText view selected initially + */ +public class OneEditTextActivityNotSelected extends Activity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + final EditText editText = new EditText(this); + final TextView textView = new TextView(this); + textView.setText("The focus is here."); + layout.addView(editText); + layout.addView(textView); + + setContentView(layout); + textView.requestFocus(); + } +} diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java new file mode 100644 index 0000000000000..2fd19e890d181 --- /dev/null +++ b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java @@ -0,0 +1,37 @@ +package com.android.imftest.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.Button; +import android.widget.TextView; +import android.widget.ScrollView; + +import com.android.internal.R; + +/* + * Activity with EditText selected initially + */ +public class OneEditTextActivitySelected extends Activity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + final EditText editText = new EditText(this); + layout.addView(editText); + + setContentView(layout); + editText.requestFocus(); + } +} diff --git a/tests/SmokeTest/tests/AndroidManifest.xml b/tests/SmokeTest/tests/AndroidManifest.xml index 20b33ee063e77..517eb1ea9cb44 100644 --- a/tests/SmokeTest/tests/AndroidManifest.xml +++ b/tests/SmokeTest/tests/AndroidManifest.xml @@ -25,8 +25,6 @@ - - + + + + + + + + +